| //===--- 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 { |
| |
| static const IfStmt *getPrecedingIf(const SourceManager &SM, |
| ASTContext *Context, const IfStmt *If) { |
| auto parents = Context->getParents(*If); |
| if (parents.size() != 1) |
| return nullptr; |
| if (const auto *PrecedingIf = parents[0].get<IfStmt>()) { |
| SourceLocation PreviousElseLoc = PrecedingIf->getElseLoc(); |
| if (SM.getExpansionLineNumber(PreviousElseLoc) == |
| SM.getExpansionLineNumber(If->getIfLoc())) |
| return PrecedingIf; |
| } |
| return nullptr; |
| } |
| |
| void MisleadingIndentationCheck::danglingElseCheck(const SourceManager &SM, |
| ASTContext *Context, |
| const IfStmt *If) { |
| SourceLocation IfLoc = If->getIfLoc(); |
| SourceLocation ElseLoc = If->getElseLoc(); |
| |
| if (IfLoc.isMacroID() || ElseLoc.isMacroID()) |
| return; |
| |
| if (SM.getExpansionLineNumber(If->getThen()->getLocEnd()) == |
| SM.getExpansionLineNumber(ElseLoc)) |
| return; |
| |
| // Find location of first 'if' in a 'if else if' chain. |
| for (auto PrecedingIf = getPrecedingIf(SM, Context, If); PrecedingIf; |
| PrecedingIf = getPrecedingIf(SM, Context, PrecedingIf)) |
| IfLoc = PrecedingIf->getIfLoc(); |
| |
| if (SM.getExpansionColumnNumber(IfLoc) != |
| SM.getExpansionColumnNumber(ElseLoc)) |
| diag(ElseLoc, "different indentation for 'if' and corresponding 'else'"); |
| } |
| |
| void MisleadingIndentationCheck::missingBracesCheck(const SourceManager &SM, |
| const CompoundStmt *CStmt) { |
| const static StringRef StmtNames[] = {"if", "for", "while"}; |
| for (unsigned int i = 0; i < CStmt->size() - 1; i++) { |
| const Stmt *CurrentStmt = CStmt->body_begin()[i]; |
| const Stmt *Inner = nullptr; |
| int StmtKind = 0; |
| |
| if (const auto *CurrentIf = dyn_cast<IfStmt>(CurrentStmt)) { |
| StmtKind = 0; |
| Inner = |
| CurrentIf->getElse() ? CurrentIf->getElse() : CurrentIf->getThen(); |
| } else if (const auto *CurrentFor = dyn_cast<ForStmt>(CurrentStmt)) { |
| StmtKind = 1; |
| Inner = CurrentFor->getBody(); |
| } else if (const auto *CurrentWhile = dyn_cast<WhileStmt>(CurrentStmt)) { |
| StmtKind = 2; |
| Inner = CurrentWhile->getBody(); |
| } else { |
| continue; |
| } |
| |
| if (isa<CompoundStmt>(Inner)) |
| continue; |
| |
| SourceLocation InnerLoc = Inner->getLocStart(); |
| SourceLocation OuterLoc = CurrentStmt->getLocStart(); |
| |
| if (SM.getExpansionLineNumber(InnerLoc) == |
| SM.getExpansionLineNumber(OuterLoc)) |
| continue; |
| |
| const Stmt *NextStmt = CStmt->body_begin()[i + 1]; |
| SourceLocation NextLoc = NextStmt->getLocStart(); |
| |
| if (InnerLoc.isMacroID() || OuterLoc.isMacroID() || NextLoc.isMacroID()) |
| continue; |
| |
| if (SM.getExpansionColumnNumber(InnerLoc) == |
| SM.getExpansionColumnNumber(NextLoc)) { |
| diag(NextLoc, "misleading indentation: statement is indented too deeply"); |
| diag(OuterLoc, "did you mean this line to be inside this '%0'", |
| DiagnosticIDs::Note) |
| << StmtNames[StmtKind]; |
| } |
| } |
| } |
| |
| void MisleadingIndentationCheck::registerMatchers(MatchFinder *Finder) { |
| Finder->addMatcher(ifStmt(hasElse(stmt())).bind("if"), this); |
| Finder->addMatcher( |
| compoundStmt(has(stmt(anyOf(ifStmt(), forStmt(), whileStmt())))) |
| .bind("compound"), |
| this); |
| } |
| |
| void MisleadingIndentationCheck::check(const MatchFinder::MatchResult &Result) { |
| if (const auto *If = Result.Nodes.getNodeAs<IfStmt>("if")) |
| danglingElseCheck(*Result.SourceManager, Result.Context, If); |
| |
| if (const auto *CStmt = Result.Nodes.getNodeAs<CompoundStmt>("compound")) |
| missingBracesCheck(*Result.SourceManager, CStmt); |
| } |
| |
| } // namespace readability |
| } // namespace tidy |
| } // namespace clang |