Index: clang-tidy/readability/CMakeLists.txt =================================================================== --- clang-tidy/readability/CMakeLists.txt +++ clang-tidy/readability/CMakeLists.txt @@ -8,6 +8,7 @@ IdentifierNamingCheck.cpp ImplicitBoolCastCheck.cpp InconsistentDeclarationParameterNameCheck.cpp + MisleadingIndentationCheck.cpp NamedParameterCheck.cpp NamespaceCommentCheck.cpp ReadabilityTidyModule.cpp Index: clang-tidy/readability/Mislead =================================================================== --- /dev/null +++ clang-tidy/readability/Mislead @@ -0,0 +1,77 @@ +//===--- MisleadingIndentationCheck.cpp - clang-tidy-----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MisleadingIndentationCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace readability { + +void MisleadingIndentationCheck::danglingElseCheck( + const MatchFinder::MatchResult &Result) { + auto *If = Result.Nodes.getNodeAs("if"); + if (!If->getElse()) + return; + + SourceLocation IfLoc = If->getIfLoc(); + SourceLocation ElseLoc = If->getElseLoc(); + + if (Result.SourceManager->getExpansionColumnNumber(IfLoc) != + Result.SourceManager->getExpansionColumnNumber(ElseLoc)) + diag(ElseLoc, "potentional dangling else"); +} + +void MisleadingIndentationCheck::missingBracesCheck( + const MatchFinder::MatchResult &Result) { + auto *CStmt = Result.Nodes.getNodeAs("stmt"); + for (unsigned int i = 0; i < CStmt->size() - 1; i++) { + Stmt *CurrentStmt = CStmt->body_begin()[i]; + + if (!isa(CurrentStmt)) + continue; + + IfStmt *CurrentIf = dyn_cast(CurrentStmt); + Stmt *InIf = + CurrentIf->getElse() ? CurrentIf->getElse() : CurrentIf->getThen(); + + if (isa(InIf)) + continue; + + SourceLocation StmtLoc = InIf->getLocStart(); + Stmt *NextStmt = CStmt->body_begin()[i + 1]; + SourceLocation NextLoc = NextStmt->getLocStart(); + + if (Result.SourceManager->getExpansionColumnNumber(StmtLoc) == + Result.SourceManager->getExpansionColumnNumber(NextLoc)) + diag(NextLoc, "Wrong Indentation - statement is indentated as a member " + "of if statement"); + } +} + +void MisleadingIndentationCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(ifStmt().bind("if"), this); + Finder->addMatcher(compoundStmt(has(ifStmt())).bind("stmt"), this); +} + +void MisleadingIndentationCheck::check(const MatchFinder::MatchResult &Result) { + + if (Result.Nodes.getNodeAs("if")) + danglingElseCheck(Result); + + if (Result.Nodes.getNodeAs("stmt")) + missingBracesCheck(Result); +} + +} // namespace readability +} // namespace tidy +} // namespace clang Index: clang-tidy/readability/Misleadin =================================================================== --- /dev/null +++ clang-tidy/readability/Misleadin @@ -0,0 +1,38 @@ +//===--- MisleadingIndentationCheck.h - clang-tidy---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MISLEADING_INDENTATION_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MISLEADING_INDENTATION_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace readability { + +/// Checks the code for dangling else, +/// and possible misleading indentations due to missing braces. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/readability-misleading-indentation.html +class MisleadingIndentationCheck : public ClangTidyCheck { +public: + MisleadingIndentationCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void danglingElseCheck(const ast_matchers::MatchFinder::MatchResult &Result); + void missingBracesCheck(const ast_matchers::MatchFinder::MatchResult &Result); +}; + +} // namespace readability +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MISLEADING_INDENTATION_H Index: clang-tidy/readability/ReadabilityT =================================================================== --- clang-tidy/readability/ReadabilityT +++ clang-tidy/readability/ReadabilityT @@ -17,6 +17,7 @@ #include "IdentifierNamingCheck.h" #include "ImplicitBoolCastCheck.h" #include "InconsistentDeclarationParameterNameCheck.h" +#include "MisleadingIndentationCheck.h" #include "NamedParameterCheck.h" #include "RedundantControlFlowCheck.h" #include "RedundantSmartptrGetCheck.h" @@ -45,6 +46,8 @@ "readability-implicit-bool-cast"); CheckFactories.registerCheck( "readability-inconsistent-declaration-parameter-name"); + CheckFactories.registerCheck( + "readability-misleading-indentation"); CheckFactories.registerCheck( "readability-named-parameter"); CheckFactories.registerCheck( Index: docs/clang-tidy/checks =================================================================== --- /dev/null +++ docs/clang-tidy/checks @@ -0,0 +1,30 @@ +.. title:: clang-tidy - readability-misleading-indentation + +readability-misleading-indentation +================================== + +Description + +Correct indentation helps to understand code. Mismatch of the syntactical structure and the indent + +The way to avoid dangling else is to always check that an else belongs to the if that begins in th + +You can omit braces when your inner part of e.g. an IF statement has only one statement in it. Alt + +Examples: + +.. code-block:: c++ + + // Dangling else: + + if (cond1) + if (cond2) + foo1(); + else + foo2(); // wrong indentation: else belongs to if(cond2) statement + + // Missing braces + + if (cond1) + foo1(); + foo2(); // not part of if(cond1) Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -93,6 +93,7 @@ readability-identifier-naming readability-implicit-bool-cast readability-inconsistent-declaration-parameter-name + readability-misleading-indentation readability-named-parameter readability-redundant-control-flow readability-redundant-smartptr-get Index: test/clang-tidy/readability-m =================================================================== --- /dev/null +++ test/clang-tidy/readability-m @@ -0,0 +1,62 @@ +// RUN: %check_clang_tidy %s readability-misleading-indentation %t + +void foo1() {} +void foo2() {} + +int main() +{ + bool cond1 = true; + bool cond2 = true; + + if (cond1) + if (cond2) + foo1(); + else + foo2(); // comment +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: wrong indentation, else belongs to if(cond2) statement +// CHECK-FIXES: {{^}} // comment + + if (cond1) { + if (cond2) + foo1(); + } + else + foo2(); // ok, else belongs to if(cond1) statement + + if (cond1) + if (cond2) + foo1(); + else + foo2(); // ok, indentation matches to syntactical structure + + if (cond1) + foo1(); + foo2(); // comment +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: not part of if(cond1) +// CHECK-FIXES: {{^}} // comment + + if (cond2) + foo1(); + foo2(); // comment +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: not part of if(cond2) +// CHECK-FIXES: {{^}} // comment + + if (cond1) + { + foo1(); + } + foo2(); // ok + + if (cond1) + foo1(); + foo2(); // ok + + if (cond1) + foo1(); + foo2(); // comment +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: not part of if(cond1) +// CHECK-FIXES: {{^}} // comment + foo2(); // no need for redundant warning + + return 0; +}