blob: d3980b7b4e2b77ca11a3b93bd2a304e54f99c9f7 [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
20void MisleadingIndentationCheck::danglingElseCheck(const SourceManager &SM,
21 const IfStmt *If) {
22 SourceLocation IfLoc = If->getIfLoc();
23 SourceLocation ElseLoc = If->getElseLoc();
24
25 if (IfLoc.isMacroID() || ElseLoc.isMacroID())
26 return;
27
28 if (SM.getExpansionLineNumber(If->getThen()->getLocEnd()) ==
29 SM.getExpansionLineNumber(ElseLoc))
30 return;
31
32 if (SM.getExpansionColumnNumber(IfLoc) !=
33 SM.getExpansionColumnNumber(ElseLoc))
34 diag(ElseLoc, "different indentation for 'if' and corresponding 'else'");
35}
36
37void MisleadingIndentationCheck::missingBracesCheck(const SourceManager &SM,
38 const CompoundStmt *CStmt) {
39 const static StringRef StmtNames[] = {"if", "for", "while"};
40 for (unsigned int i = 0; i < CStmt->size() - 1; i++) {
41 const Stmt *CurrentStmt = CStmt->body_begin()[i];
42 const Stmt *Inner = nullptr;
43 int StmtKind = 0;
44
45 if (const auto *CurrentIf = dyn_cast<IfStmt>(CurrentStmt)) {
46 StmtKind = 0;
47 Inner =
48 CurrentIf->getElse() ? CurrentIf->getElse() : CurrentIf->getThen();
49 } else if (const auto *CurrentFor = dyn_cast<ForStmt>(CurrentStmt)) {
50 StmtKind = 1;
51 Inner = CurrentFor->getBody();
52 } else if (const auto *CurrentWhile = dyn_cast<WhileStmt>(CurrentStmt)) {
53 StmtKind = 2;
54 Inner = CurrentWhile->getBody();
55 } else {
56 continue;
57 }
58
59 if (isa<CompoundStmt>(Inner))
60 continue;
61
62 SourceLocation InnerLoc = Inner->getLocStart();
63 SourceLocation OuterLoc = CurrentStmt->getLocStart();
64
65 if (SM.getExpansionLineNumber(InnerLoc) ==
66 SM.getExpansionLineNumber(OuterLoc))
67 continue;
68
69 const Stmt *NextStmt = CStmt->body_begin()[i + 1];
70 SourceLocation NextLoc = NextStmt->getLocStart();
71
72 if (InnerLoc.isMacroID() || OuterLoc.isMacroID() || NextLoc.isMacroID())
73 continue;
74
75 if (SM.getExpansionColumnNumber(InnerLoc) ==
76 SM.getExpansionColumnNumber(NextLoc)) {
77 diag(NextLoc, "misleading indentation: statement is indented too deeply");
78 diag(OuterLoc, "did you mean this line to be inside this '%0'",
79 DiagnosticIDs::Note)
80 << StmtNames[StmtKind];
81 }
82 }
83}
84
85void MisleadingIndentationCheck::registerMatchers(MatchFinder *Finder) {
86 Finder->addMatcher(ifStmt(hasElse(stmt())).bind("if"), this);
87 Finder->addMatcher(
88 compoundStmt(has(stmt(anyOf(ifStmt(), forStmt(), whileStmt()))))
89 .bind("compound"),
90 this);
91}
92
93void MisleadingIndentationCheck::check(const MatchFinder::MatchResult &Result) {
94 if (const auto *If = Result.Nodes.getNodeAs<IfStmt>("if"))
95 danglingElseCheck(*Result.SourceManager, If);
96
97 if (const auto *CStmt = Result.Nodes.getNodeAs<CompoundStmt>("compound"))
98 missingBracesCheck(*Result.SourceManager, CStmt);
99}
100
101} // namespace readability
102} // namespace tidy
103} // namespace clang