blob: 8a6c8c47598ee7ffc06ffd7206c7e5fbac299fcf [file] [log] [blame]
Gabor Horvathe647bd52017-02-14 10:03:27 +00001//===--- MisleadingIndentationCheck.cpp - clang-tidy-----------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "MisleadingIndentationCheck.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13
14using namespace clang::ast_matchers;
15
16namespace clang {
17namespace tidy {
18namespace readability {
19
Alexander Kornienkobc8d1462017-03-17 09:58:30 +000020static const IfStmt *getPrecedingIf(const SourceManager &SM,
21 ASTContext *Context, const IfStmt *If) {
22 auto parents = Context->getParents(*If);
23 if (parents.size() != 1)
24 return nullptr;
25 if (const auto *PrecedingIf = parents[0].get<IfStmt>()) {
26 SourceLocation PreviousElseLoc = PrecedingIf->getElseLoc();
27 if (SM.getExpansionLineNumber(PreviousElseLoc) ==
28 SM.getExpansionLineNumber(If->getIfLoc()))
29 return PrecedingIf;
30 }
31 return nullptr;
32}
33
Gabor Horvathe647bd52017-02-14 10:03:27 +000034void MisleadingIndentationCheck::danglingElseCheck(const SourceManager &SM,
Alexander Kornienkobc8d1462017-03-17 09:58:30 +000035 ASTContext *Context,
Gabor Horvathe647bd52017-02-14 10:03:27 +000036 const IfStmt *If) {
37 SourceLocation IfLoc = If->getIfLoc();
38 SourceLocation ElseLoc = If->getElseLoc();
39
40 if (IfLoc.isMacroID() || ElseLoc.isMacroID())
41 return;
42
43 if (SM.getExpansionLineNumber(If->getThen()->getLocEnd()) ==
44 SM.getExpansionLineNumber(ElseLoc))
45 return;
46
Alexander Kornienkobc8d1462017-03-17 09:58:30 +000047 // Find location of first 'if' in a 'if else if' chain.
48 for (auto PrecedingIf = getPrecedingIf(SM, Context, If); PrecedingIf;
49 PrecedingIf = getPrecedingIf(SM, Context, PrecedingIf))
50 IfLoc = PrecedingIf->getIfLoc();
51
Gabor Horvathe647bd52017-02-14 10:03:27 +000052 if (SM.getExpansionColumnNumber(IfLoc) !=
53 SM.getExpansionColumnNumber(ElseLoc))
54 diag(ElseLoc, "different indentation for 'if' and corresponding 'else'");
55}
56
57void MisleadingIndentationCheck::missingBracesCheck(const SourceManager &SM,
58 const CompoundStmt *CStmt) {
59 const static StringRef StmtNames[] = {"if", "for", "while"};
60 for (unsigned int i = 0; i < CStmt->size() - 1; i++) {
61 const Stmt *CurrentStmt = CStmt->body_begin()[i];
62 const Stmt *Inner = nullptr;
63 int StmtKind = 0;
64
65 if (const auto *CurrentIf = dyn_cast<IfStmt>(CurrentStmt)) {
66 StmtKind = 0;
67 Inner =
68 CurrentIf->getElse() ? CurrentIf->getElse() : CurrentIf->getThen();
69 } else if (const auto *CurrentFor = dyn_cast<ForStmt>(CurrentStmt)) {
70 StmtKind = 1;
71 Inner = CurrentFor->getBody();
72 } else if (const auto *CurrentWhile = dyn_cast<WhileStmt>(CurrentStmt)) {
73 StmtKind = 2;
74 Inner = CurrentWhile->getBody();
75 } else {
76 continue;
77 }
78
79 if (isa<CompoundStmt>(Inner))
80 continue;
81
82 SourceLocation InnerLoc = Inner->getLocStart();
83 SourceLocation OuterLoc = CurrentStmt->getLocStart();
84
85 if (SM.getExpansionLineNumber(InnerLoc) ==
86 SM.getExpansionLineNumber(OuterLoc))
87 continue;
88
89 const Stmt *NextStmt = CStmt->body_begin()[i + 1];
90 SourceLocation NextLoc = NextStmt->getLocStart();
91
92 if (InnerLoc.isMacroID() || OuterLoc.isMacroID() || NextLoc.isMacroID())
93 continue;
94
95 if (SM.getExpansionColumnNumber(InnerLoc) ==
96 SM.getExpansionColumnNumber(NextLoc)) {
97 diag(NextLoc, "misleading indentation: statement is indented too deeply");
98 diag(OuterLoc, "did you mean this line to be inside this '%0'",
99 DiagnosticIDs::Note)
100 << StmtNames[StmtKind];
101 }
102 }
103}
104
105void MisleadingIndentationCheck::registerMatchers(MatchFinder *Finder) {
106 Finder->addMatcher(ifStmt(hasElse(stmt())).bind("if"), this);
107 Finder->addMatcher(
108 compoundStmt(has(stmt(anyOf(ifStmt(), forStmt(), whileStmt()))))
109 .bind("compound"),
110 this);
111}
112
113void MisleadingIndentationCheck::check(const MatchFinder::MatchResult &Result) {
114 if (const auto *If = Result.Nodes.getNodeAs<IfStmt>("if"))
Alexander Kornienkobc8d1462017-03-17 09:58:30 +0000115 danglingElseCheck(*Result.SourceManager, Result.Context, If);
Gabor Horvathe647bd52017-02-14 10:03:27 +0000116
117 if (const auto *CStmt = Result.Nodes.getNodeAs<CompoundStmt>("compound"))
118 missingBracesCheck(*Result.SourceManager, CStmt);
119}
120
121} // namespace readability
122} // namespace tidy
123} // namespace clang