10#include "../utils/BracesAroundStatement.h"
11#include "../utils/LexerUtils.h"
12#include "clang/AST/ASTContext.h"
13#include "clang/ASTMatchers/ASTMatchers.h"
14#include "clang/Lex/Lexer.h"
21 const LangOptions &LangOpts) {
23 SourceLocation Beginning = Lexer::GetBeginningOfToken(
Loc, SM, LangOpts);
24 const bool Invalid = Lexer::getRawToken(Beginning, Tok, SM, LangOpts);
25 assert(!Invalid &&
"Expected a valid token.");
28 return tok::NUM_TOKENS;
35 const LangOptions &LangOpts) {
36 assert(
Loc.isValid());
38 while (isWhitespace(*SM.getCharacterData(
Loc)))
39 Loc =
Loc.getLocWithOffset(1);
42 if (TokKind != tok::comment)
46 Loc = Lexer::getLocForEndOfToken(
Loc, 0, SM, LangOpts);
54 ShortStatementLines(Options.get(
"ShortStatementLines", 0U)) {}
58 Options.
store(Opts,
"ShortStatementLines", ShortStatementLines);
62 Finder->addMatcher(ifStmt().bind(
"if"),
this);
63 Finder->addMatcher(whileStmt().bind(
"while"),
this);
64 Finder->addMatcher(doStmt().bind(
"do"),
this);
65 Finder->addMatcher(forStmt().bind(
"for"),
this);
66 Finder->addMatcher(cxxForRangeStmt().bind(
"for-range"),
this);
70 const MatchFinder::MatchResult &Result) {
71 const SourceManager &SM = *Result.SourceManager;
72 const ASTContext *Context = Result.Context;
75 if (
const auto *S = Result.Nodes.getNodeAs<ForStmt>(
"for")) {
76 checkStmt(Result, S->getBody(), S->getRParenLoc());
77 }
else if (
const auto *S =
78 Result.Nodes.getNodeAs<CXXForRangeStmt>(
"for-range")) {
79 checkStmt(Result, S->getBody(), S->getRParenLoc());
80 }
else if (
const auto *S = Result.Nodes.getNodeAs<DoStmt>(
"do")) {
81 checkStmt(Result, S->getBody(), S->getDoLoc(), S->getWhileLoc());
82 }
else if (
const auto *S = Result.Nodes.getNodeAs<WhileStmt>(
"while")) {
83 SourceLocation StartLoc = findRParenLoc(S, SM, Context->getLangOpts());
84 if (StartLoc.isInvalid())
86 checkStmt(Result, S->getBody(), StartLoc);
87 }
else if (
const auto *S = Result.Nodes.getNodeAs<IfStmt>(
"if")) {
92 SourceLocation StartLoc = findRParenLoc(S, SM, Context->getLangOpts());
93 if (StartLoc.isInvalid())
95 if (ForceBracesStmts.erase(S))
96 ForceBracesStmts.insert(S->getThen());
97 bool BracedIf = checkStmt(Result, S->getThen(), StartLoc, S->getElseLoc());
98 const Stmt *Else = S->getElse();
100 ForceBracesStmts.insert(Else);
101 if (Else && !isa<IfStmt>(Else)) {
103 checkStmt(Result, Else, S->getElseLoc());
106 llvm_unreachable(
"Invalid match");
111template <
typename IfOrWhileStmt>
113BracesAroundStatementsCheck::findRParenLoc(
const IfOrWhileStmt *S,
114 const SourceManager &SM,
115 const LangOptions &LangOpts) {
117 if (S->getBeginLoc().isMacroID())
120 SourceLocation CondEndLoc = S->getCond()->getEndLoc();
121 if (
const DeclStmt *CondVar = S->getConditionVariableDeclStmt())
122 CondEndLoc = CondVar->getEndLoc();
124 if (!CondEndLoc.isValid()) {
128 SourceLocation PastCondEndLoc =
129 Lexer::getLocForEndOfToken(CondEndLoc, 0, SM, LangOpts);
130 if (PastCondEndLoc.isInvalid())
132 SourceLocation RParenLoc =
134 if (RParenLoc.isInvalid())
136 tok::TokenKind TokKind =
getTokenKind(RParenLoc, SM, LangOpts);
137 if (TokKind != tok::r_paren)
144bool BracesAroundStatementsCheck::checkStmt(
145 const MatchFinder::MatchResult &Result,
const Stmt *S,
146 SourceLocation StartLoc, SourceLocation EndLocHint) {
147 while (
const auto *AS = dyn_cast<AttributedStmt>(S))
148 S = AS->getSubStmt();
151 S, Result.Context->getLangOpts(), *Result.SourceManager, StartLoc,
153 if (BraceInsertionHints) {
154 if (ShortStatementLines && !ForceBracesStmts.erase(S) &&
155 BraceInsertionHints.resultingCompoundLineExtent(*Result.SourceManager) <
158 auto Diag =
diag(BraceInsertionHints.DiagnosticPos,
159 "statement should be inside braces");
160 if (BraceInsertionHints.offersFixIts())
161 Diag << BraceInsertionHints.openingBraceFixIt()
162 << BraceInsertionHints.closingBraceFixIt();
168 ForceBracesStmts.clear();
llvm::SmallString< 256U > Name
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
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
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
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