12#include "clang/AST/ASTContext.h"
13#include "clang/ASTMatchers/ASTMatchers.h"
14#include "clang/Lex/Lexer.h"
20static tok::TokenKind
getTokenKind(SourceLocation Loc,
const SourceManager &SM,
21 const LangOptions &LangOpts) {
23 const SourceLocation Beginning =
24 Lexer::GetBeginningOfToken(Loc, SM, LangOpts);
25 const bool Invalid = Lexer::getRawToken(Beginning, Tok, SM, LangOpts);
26 assert(!Invalid &&
"Expected a valid token.");
29 return tok::NUM_TOKENS;
36 const LangOptions &LangOpts) {
37 assert(Loc.isValid());
39 while (isWhitespace(*SM.getCharacterData(Loc)))
40 Loc = Loc.getLocWithOffset(1);
42 const tok::TokenKind TokKind =
getTokenKind(Loc, SM, LangOpts);
43 if (TokKind != tok::comment)
47 Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts);
55 ShortStatementLines(Options.get(
"ShortStatementLines", 0U)) {}
59 Options.store(Opts,
"ShortStatementLines", ShortStatementLines);
63 Finder->addMatcher(ifStmt().bind(
"if"),
this);
64 Finder->addMatcher(whileStmt().bind(
"while"),
this);
65 Finder->addMatcher(doStmt().bind(
"do"),
this);
66 Finder->addMatcher(forStmt().bind(
"for"),
this);
67 Finder->addMatcher(cxxForRangeStmt().bind(
"for-range"),
this);
71 const MatchFinder::MatchResult &Result) {
72 const SourceManager &SM = *Result.SourceManager;
73 const ASTContext *Context = Result.Context;
76 if (
const auto *S = Result.Nodes.getNodeAs<ForStmt>(
"for")) {
77 checkStmt(Result, S->getBody(), S->getRParenLoc());
78 }
else if (
const auto *S =
79 Result.Nodes.getNodeAs<CXXForRangeStmt>(
"for-range")) {
80 checkStmt(Result, S->getBody(), S->getRParenLoc());
81 }
else if (
const auto *S = Result.Nodes.getNodeAs<DoStmt>(
"do")) {
82 checkStmt(Result, S->getBody(), S->getDoLoc(), S->getWhileLoc());
83 }
else if (
const auto *S = Result.Nodes.getNodeAs<WhileStmt>(
"while")) {
84 const SourceLocation StartLoc =
85 findRParenLoc(S, SM, Context->getLangOpts());
86 if (StartLoc.isInvalid())
88 checkStmt(Result, S->getBody(), StartLoc);
89 }
else if (
const auto *S = Result.Nodes.getNodeAs<IfStmt>(
"if")) {
94 const SourceLocation StartLoc =
95 findRParenLoc(S, SM, Context->getLangOpts());
96 if (StartLoc.isInvalid())
98 if (ForceBracesStmts.erase(S))
99 ForceBracesStmts.insert(S->getThen());
100 const bool BracedIf =
101 checkStmt(Result, S->getThen(), StartLoc, S->getElseLoc());
102 const Stmt *Else = S->getElse();
103 if (Else && BracedIf)
104 ForceBracesStmts.insert(Else);
105 if (Else && !isa<IfStmt>(Else)) {
107 checkStmt(Result, Else, S->getElseLoc());
110 llvm_unreachable(
"Invalid match");
115template <
typename IfOrWhileStmt>
117BracesAroundStatementsCheck::findRParenLoc(
const IfOrWhileStmt *S,
118 const SourceManager &SM,
119 const LangOptions &LangOpts) {
121 if (S->getBeginLoc().isMacroID())
124 SourceLocation CondEndLoc = S->getCond()->getEndLoc();
125 if (
const DeclStmt *CondVar = S->getConditionVariableDeclStmt())
126 CondEndLoc = CondVar->getEndLoc();
128 if (!CondEndLoc.isValid()) {
132 const SourceLocation PastCondEndLoc =
133 Lexer::getLocForEndOfToken(CondEndLoc, 0, SM, LangOpts);
134 if (PastCondEndLoc.isInvalid())
136 SourceLocation RParenLoc =
138 if (RParenLoc.isInvalid())
140 const tok::TokenKind TokKind =
getTokenKind(RParenLoc, SM, LangOpts);
141 if (TokKind != tok::r_paren)
148bool BracesAroundStatementsCheck::checkStmt(
149 const MatchFinder::MatchResult &Result,
const Stmt *S,
150 SourceLocation StartLoc, SourceLocation EndLocHint) {
151 while (
const auto *AS = dyn_cast<AttributedStmt>(S))
152 S = AS->getSubStmt();
155 S, Result.Context->getLangOpts(), *Result.SourceManager, StartLoc,
157 if (BraceInsertionHints) {
158 if (ShortStatementLines && !ForceBracesStmts.erase(S) &&
159 BraceInsertionHints.resultingCompoundLineExtent(*Result.SourceManager) <
162 auto Diag = diag(BraceInsertionHints.DiagnosticPos,
163 "statement should be inside braces");
164 if (BraceInsertionHints.offersFixIts())
165 Diag << BraceInsertionHints.openingBraceFixIt()
166 << BraceInsertionHints.closingBraceFixIt();
172 ForceBracesStmts.clear();
This file provides utilities to put braces around a statement.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
BracesAroundStatementsCheck(StringRef Name, ClangTidyContext *Context)
void onEndOfTranslationUnit() override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
static SourceLocation forwardSkipWhitespaceAndComments(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
static tok::TokenKind getTokenKind(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
BraceInsertionHints getBraceInsertionsHints(const Stmt *const S, const LangOptions &LangOpts, const SourceManager &SM, SourceLocation StartLoc, SourceLocation EndLocHint)
Create fix-it hints for braces that wrap the given statement when applied.
llvm::StringMap< ClangTidyValue > OptionMap