clang-tools  14.0.0git
MisleadingIndentationCheck.cpp
Go to the documentation of this file.
1 //===--- MisleadingIndentationCheck.cpp - clang-tidy-----------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 
13 using namespace clang::ast_matchers;
14 
15 namespace clang {
16 namespace tidy {
17 namespace readability {
18 
19 static const IfStmt *getPrecedingIf(const SourceManager &SM,
20  ASTContext *Context, const IfStmt *If) {
21  auto Parents = Context->getParents(*If);
22  if (Parents.size() != 1)
23  return nullptr;
24  if (const auto *PrecedingIf = Parents[0].get<IfStmt>()) {
25  SourceLocation PreviousElseLoc = PrecedingIf->getElseLoc();
26  if (SM.getExpansionLineNumber(PreviousElseLoc) ==
27  SM.getExpansionLineNumber(If->getIfLoc()))
28  return PrecedingIf;
29  }
30  return nullptr;
31 }
32 
33 void MisleadingIndentationCheck::danglingElseCheck(const SourceManager &SM,
34  ASTContext *Context,
35  const IfStmt *If) {
36  SourceLocation IfLoc = If->getIfLoc();
37  SourceLocation ElseLoc = If->getElseLoc();
38 
39  if (IfLoc.isMacroID() || ElseLoc.isMacroID())
40  return;
41 
42  if (SM.getExpansionLineNumber(If->getThen()->getEndLoc()) ==
43  SM.getExpansionLineNumber(ElseLoc))
44  return;
45 
46  // Find location of first 'if' in a 'if else if' chain.
47  for (const auto *PrecedingIf = getPrecedingIf(SM, Context, If); PrecedingIf;
48  PrecedingIf = getPrecedingIf(SM, Context, PrecedingIf))
49  IfLoc = PrecedingIf->getIfLoc();
50 
51  if (SM.getExpansionColumnNumber(IfLoc) !=
52  SM.getExpansionColumnNumber(ElseLoc))
53  diag(ElseLoc, "different indentation for 'if' and corresponding 'else'");
54 }
55 
56 void MisleadingIndentationCheck::missingBracesCheck(const SourceManager &SM,
57  const CompoundStmt *CStmt) {
58  const static StringRef StmtNames[] = {"if", "for", "while"};
59  for (unsigned int I = 0; I < CStmt->size() - 1; I++) {
60  const Stmt *CurrentStmt = CStmt->body_begin()[I];
61  const Stmt *Inner = nullptr;
62  int StmtKind = 0;
63 
64  if (const auto *CurrentIf = dyn_cast<IfStmt>(CurrentStmt)) {
65  StmtKind = 0;
66  Inner =
67  CurrentIf->getElse() ? CurrentIf->getElse() : CurrentIf->getThen();
68  } else if (const auto *CurrentFor = dyn_cast<ForStmt>(CurrentStmt)) {
69  StmtKind = 1;
70  Inner = CurrentFor->getBody();
71  } else if (const auto *CurrentWhile = dyn_cast<WhileStmt>(CurrentStmt)) {
72  StmtKind = 2;
73  Inner = CurrentWhile->getBody();
74  } else {
75  continue;
76  }
77 
78  if (isa<CompoundStmt>(Inner))
79  continue;
80 
81  SourceLocation InnerLoc = Inner->getBeginLoc();
82  SourceLocation OuterLoc = CurrentStmt->getBeginLoc();
83 
84  if (InnerLoc.isInvalid() || InnerLoc.isMacroID() || OuterLoc.isInvalid() ||
85  OuterLoc.isMacroID())
86  continue;
87 
88  if (SM.getExpansionLineNumber(InnerLoc) ==
89  SM.getExpansionLineNumber(OuterLoc))
90  continue;
91 
92  const Stmt *NextStmt = CStmt->body_begin()[I + 1];
93  SourceLocation NextLoc = NextStmt->getBeginLoc();
94 
95  if (NextLoc.isInvalid() || NextLoc.isMacroID())
96  continue;
97 
98  if (SM.getExpansionColumnNumber(InnerLoc) ==
99  SM.getExpansionColumnNumber(NextLoc)) {
100  diag(NextLoc, "misleading indentation: statement is indented too deeply");
101  diag(OuterLoc, "did you mean this line to be inside this '%0'",
102  DiagnosticIDs::Note)
103  << StmtNames[StmtKind];
104  }
105  }
106 }
107 
108 void MisleadingIndentationCheck::registerMatchers(MatchFinder *Finder) {
109  Finder->addMatcher(ifStmt(hasElse(stmt())).bind("if"), this);
110  Finder->addMatcher(
111  compoundStmt(has(stmt(anyOf(ifStmt(), forStmt(), whileStmt()))))
112  .bind("compound"),
113  this);
114 }
115 
116 void MisleadingIndentationCheck::check(const MatchFinder::MatchResult &Result) {
117  if (const auto *If = Result.Nodes.getNodeAs<IfStmt>("if"))
118  danglingElseCheck(*Result.SourceManager, Result.Context, If);
119 
120  if (const auto *CStmt = Result.Nodes.getNodeAs<CompoundStmt>("compound"))
121  missingBracesCheck(*Result.SourceManager, CStmt);
122 }
123 
124 } // namespace readability
125 } // namespace tidy
126 } // namespace clang
clang::ast_matchers
Definition: AbseilMatcher.h:14
Inner
std::pair< Context, Canceler > Inner
Definition: CancellationTests.cpp:49
clang::clangd::check
bool check(llvm::StringRef File, llvm::function_ref< bool(const Position &)> ShouldCheckLine, const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts, bool EnableCodeCompletion)
Definition: Check.cpp:259
MisleadingIndentationCheck.h
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::readability::getPrecedingIf
static const IfStmt * getPrecedingIf(const SourceManager &SM, ASTContext *Context, const IfStmt *If)
Definition: MisleadingIndentationCheck.cpp:19