11 #include "clang/AST/ASTContext.h" 12 #include "clang/ASTMatchers/ASTMatchers.h" 13 #include "clang/Lex/Lexer.h" 19 namespace readability {
22 tok::TokenKind getTokenKind(SourceLocation
Loc,
const SourceManager &SM,
23 const ASTContext *Context) {
25 SourceLocation Beginning =
26 Lexer::GetBeginningOfToken(Loc, SM, Context->getLangOpts());
28 Lexer::getRawToken(Beginning, Tok, SM, Context->getLangOpts());
29 assert(!Invalid &&
"Expected a valid token.");
32 return tok::NUM_TOKENS;
37 SourceLocation forwardSkipWhitespaceAndComments(SourceLocation Loc,
38 const SourceManager &SM,
39 const ASTContext *Context) {
40 assert(Loc.isValid());
42 while (isWhitespace(*SM.getCharacterData(Loc)))
43 Loc = Loc.getLocWithOffset(1);
45 tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
46 if (TokKind == tok::NUM_TOKENS || TokKind != tok::comment)
50 Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
54 SourceLocation findEndLocation(SourceLocation LastTokenLoc,
55 const SourceManager &SM,
56 const ASTContext *Context) {
58 Lexer::GetBeginningOfToken(LastTokenLoc, SM, Context->getLangOpts());
61 assert(Loc.isValid());
62 bool SkipEndWhitespaceAndComments =
true;
63 tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
64 if (TokKind == tok::NUM_TOKENS || TokKind == tok::semi ||
65 TokKind == tok::r_brace) {
68 SkipEndWhitespaceAndComments =
false;
71 Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
73 if (SkipEndWhitespaceAndComments) {
74 Loc = forwardSkipWhitespaceAndComments(Loc, SM, Context);
75 tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
76 if (TokKind == tok::semi)
77 Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
81 assert(Loc.isValid());
82 while (isHorizontalWhitespace(*SM.getCharacterData(Loc))) {
83 Loc = Loc.getLocWithOffset(1);
86 if (isVerticalWhitespace(*SM.getCharacterData(Loc))) {
90 tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
91 if (TokKind != tok::comment) {
96 SourceLocation TokEndLoc =
97 Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
98 SourceRange TokRange(Loc, TokEndLoc);
99 StringRef Comment = Lexer::getSourceText(
100 CharSourceRange::getTokenRange(TokRange), SM, Context->getLangOpts());
101 if (Comment.startswith(
"/*") && Comment.find(
'\n') != StringRef::npos) {
115 BracesAroundStatementsCheck::BracesAroundStatementsCheck(
119 ShortStatementLines(Options.get(
"ShortStatementLines", 0U)) {}
123 Options.
store(Opts,
"ShortStatementLines", ShortStatementLines);
127 Finder->addMatcher(ifStmt().bind(
"if"),
this);
128 Finder->addMatcher(whileStmt().bind(
"while"),
this);
129 Finder->addMatcher(doStmt().bind(
"do"),
this);
130 Finder->addMatcher(forStmt().bind(
"for"),
this);
131 Finder->addMatcher(cxxForRangeStmt().bind(
"for-range"),
this);
135 const MatchFinder::MatchResult &Result) {
136 const SourceManager &SM = *Result.SourceManager;
137 const ASTContext *Context = Result.Context;
140 if (
auto S = Result.Nodes.getNodeAs<ForStmt>(
"for")) {
141 checkStmt(Result, S->getBody(), S->getRParenLoc());
142 }
else if (
auto S = Result.Nodes.getNodeAs<CXXForRangeStmt>(
"for-range")) {
143 checkStmt(Result, S->getBody(), S->getRParenLoc());
144 }
else if (
auto S = Result.Nodes.getNodeAs<DoStmt>(
"do")) {
145 checkStmt(Result, S->getBody(), S->getDoLoc(), S->getWhileLoc());
146 }
else if (
auto S = Result.Nodes.getNodeAs<WhileStmt>(
"while")) {
147 SourceLocation StartLoc = findRParenLoc(S, SM, Context);
148 if (StartLoc.isInvalid())
150 checkStmt(Result, S->getBody(), StartLoc);
151 }
else if (
auto S = Result.Nodes.getNodeAs<IfStmt>(
"if")) {
152 SourceLocation StartLoc = findRParenLoc(S, SM, Context);
153 if (StartLoc.isInvalid())
155 if (ForceBracesStmts.erase(S))
156 ForceBracesStmts.insert(S->getThen());
157 bool BracedIf = checkStmt(Result, S->getThen(), StartLoc, S->getElseLoc());
158 const Stmt *Else = S->getElse();
159 if (Else && BracedIf)
160 ForceBracesStmts.insert(Else);
161 if (Else && !isa<IfStmt>(Else)) {
163 checkStmt(Result, Else, S->getElseLoc());
166 llvm_unreachable(
"Invalid match");
171 template <
typename IfOrWhileStmt>
173 BracesAroundStatementsCheck::findRParenLoc(
const IfOrWhileStmt *S,
174 const SourceManager &SM,
175 const ASTContext *Context) {
177 if (S->getLocStart().isMacroID())
178 return SourceLocation();
180 SourceLocation CondEndLoc = S->getCond()->getLocEnd();
181 if (
const DeclStmt *CondVar = S->getConditionVariableDeclStmt())
182 CondEndLoc = CondVar->getLocEnd();
184 if (!CondEndLoc.isValid()) {
185 return SourceLocation();
188 SourceLocation PastCondEndLoc =
189 Lexer::getLocForEndOfToken(CondEndLoc, 0, SM, Context->getLangOpts());
190 if (PastCondEndLoc.isInvalid())
191 return SourceLocation();
192 SourceLocation RParenLoc =
193 forwardSkipWhitespaceAndComments(PastCondEndLoc, SM, Context);
194 if (RParenLoc.isInvalid())
195 return SourceLocation();
196 tok::TokenKind TokKind = getTokenKind(RParenLoc, SM, Context);
197 if (TokKind != tok::r_paren)
198 return SourceLocation();
204 bool BracesAroundStatementsCheck::checkStmt(
205 const MatchFinder::MatchResult &Result,
const Stmt *S,
206 SourceLocation InitialLoc, SourceLocation EndLocHint) {
214 if (!S || isa<CompoundStmt>(S)) {
219 if (!InitialLoc.isValid())
221 const SourceManager &SM = *Result.SourceManager;
222 const ASTContext *Context = Result.Context;
225 CharSourceRange FileRange = Lexer::makeFileCharRange(
226 CharSourceRange::getTokenRange(S->getSourceRange()), SM,
227 Context->getLangOpts());
228 if (FileRange.isInvalid())
234 InitialLoc = Lexer::makeFileCharRange(
235 CharSourceRange::getCharRange(InitialLoc, S->getLocStart()),
236 SM, Context->getLangOpts())
238 if (InitialLoc.isInvalid())
240 SourceLocation StartLoc =
241 Lexer::getLocForEndOfToken(InitialLoc, 0, SM, Context->getLangOpts());
244 SourceLocation EndLoc;
245 std::string ClosingInsertion;
246 if (EndLocHint.isValid()) {
248 ClosingInsertion =
"} ";
250 const auto FREnd = FileRange.getEnd().getLocWithOffset(-1);
251 EndLoc = findEndLocation(FREnd, SM, Context);
252 ClosingInsertion =
"\n}";
255 assert(StartLoc.isValid());
256 assert(EndLoc.isValid());
259 if (ShortStatementLines && !ForceBracesStmts.erase(S)) {
260 unsigned StartLine = SM.getSpellingLineNumber(StartLoc);
261 unsigned EndLine = SM.getSpellingLineNumber(EndLoc);
262 if (EndLine - StartLine < ShortStatementLines)
266 auto Diag =
diag(StartLoc,
"statement should be inside braces");
267 Diag << FixItHint::CreateInsertion(StartLoc,
" {")
268 << FixItHint::CreateInsertion(EndLoc, ClosingInsertion);
273 ForceBracesStmts.clear();
SourceLocation Loc
'#' location in the include directive
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.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Base class for all clang-tidy checks.
void onEndOfTranslationUnit() override
std::map< std::string, std::string > OptionMap
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.