10#include "../utils/LexerUtils.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
19 ASTContext *Context,
const IfStmt *If) {
20 auto Parents = Context->getParents(*If);
21 if (Parents.size() != 1)
23 if (
const auto *PrecedingIf = Parents[0].get<IfStmt>()) {
24 SourceLocation PreviousElseLoc = PrecedingIf->getElseLoc();
25 if (SM.getExpansionLineNumber(PreviousElseLoc) ==
26 SM.getExpansionLineNumber(If->getIfLoc()))
32void MisleadingIndentationCheck::danglingElseCheck(
const SourceManager &SM,
35 SourceLocation IfLoc = If->getIfLoc();
36 SourceLocation ElseLoc = If->getElseLoc();
38 if (IfLoc.isMacroID() || ElseLoc.isMacroID())
41 if (SM.getExpansionLineNumber(If->getThen()->getEndLoc()) ==
42 SM.getExpansionLineNumber(ElseLoc))
46 for (
const auto *PrecedingIf =
getPrecedingIf(SM, Context, If); PrecedingIf;
48 IfLoc = PrecedingIf->getIfLoc();
50 if (SM.getExpansionColumnNumber(IfLoc) !=
51 SM.getExpansionColumnNumber(ElseLoc))
52 diag(ElseLoc,
"different indentation for 'if' and corresponding 'else'");
56 const SourceManager &SM,
57 const LangOptions &LangOpts) {
58 const SourceLocation BeforeLoc =
60 if (BeforeLoc.isInvalid())
62 return SM.getExpansionLineNumber(BeforeLoc) !=
63 SM.getExpansionLineNumber(NextLoc);
66void MisleadingIndentationCheck::missingBracesCheck(
67 const SourceManager &SM,
const CompoundStmt *CStmt,
68 const LangOptions &LangOpts) {
69 const static StringRef StmtNames[] = {
"if",
"for",
"while"};
70 for (
unsigned int I = 0; I < CStmt->size() - 1; I++) {
71 const Stmt *CurrentStmt = CStmt->body_begin()[I];
72 const Stmt *
Inner =
nullptr;
75 if (
const auto *CurrentIf = dyn_cast<IfStmt>(CurrentStmt)) {
78 CurrentIf->getElse() ? CurrentIf->getElse() : CurrentIf->getThen();
79 }
else if (
const auto *CurrentFor = dyn_cast<ForStmt>(CurrentStmt)) {
81 Inner = CurrentFor->getBody();
82 }
else if (
const auto *CurrentWhile = dyn_cast<WhileStmt>(CurrentStmt)) {
84 Inner = CurrentWhile->getBody();
89 if (isa<CompoundStmt>(
Inner))
92 SourceLocation InnerLoc =
Inner->getBeginLoc();
93 SourceLocation OuterLoc = CurrentStmt->getBeginLoc();
95 if (InnerLoc.isInvalid() || InnerLoc.isMacroID() || OuterLoc.isInvalid() ||
99 if (SM.getExpansionLineNumber(InnerLoc) ==
100 SM.getExpansionLineNumber(OuterLoc))
103 const Stmt *NextStmt = CStmt->body_begin()[I + 1];
104 SourceLocation NextLoc = NextStmt->getBeginLoc();
106 if (NextLoc.isInvalid() || NextLoc.isMacroID())
111 if (SM.getExpansionColumnNumber(InnerLoc) ==
112 SM.getExpansionColumnNumber(NextLoc)) {
113 diag(NextLoc,
"misleading indentation: statement is indented too deeply");
114 diag(OuterLoc,
"did you mean this line to be inside this '%0'",
116 << StmtNames[StmtKind];
123 ifStmt(unless(hasThen(nullStmt())), hasElse(stmt())).bind(
"if"),
this);
125 compoundStmt(has(stmt(anyOf(ifStmt(), forStmt(), whileStmt()))))
131 if (
const auto *If = Result.Nodes.getNodeAs<IfStmt>(
"if"))
132 danglingElseCheck(*Result.SourceManager, Result.Context, If);
134 if (
const auto *CStmt = Result.Nodes.getNodeAs<CompoundStmt>(
"compound"))
135 missingBracesCheck(*Result.SourceManager, CStmt,
136 Result.Context->getLangOpts());
std::pair< Context, Canceler > Inner
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
static const IfStmt * getPrecedingIf(const SourceManager &SM, ASTContext *Context, const IfStmt *If)
static bool isAtStartOfLineIncludingEmptyMacro(SourceLocation NextLoc, const SourceManager &SM, const LangOptions &LangOpts)
std::pair< Token, SourceLocation > getPreviousTokenAndStart(SourceLocation Location, const SourceManager &SM, const LangOptions &LangOpts, bool SkipComments)