10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchers.h"
12 #include "clang/Lex/Lexer.h"
18 namespace readability {
21 tok::TokenKind getTokenKind(SourceLocation
Loc,
const SourceManager &SM,
22 const ASTContext *Context) {
24 SourceLocation Beginning =
25 Lexer::GetBeginningOfToken(
Loc, SM, Context->getLangOpts());
27 Lexer::getRawToken(Beginning, Tok, SM, Context->getLangOpts());
28 assert(!Invalid &&
"Expected a valid token.");
31 return tok::NUM_TOKENS;
36 SourceLocation forwardSkipWhitespaceAndComments(SourceLocation
Loc,
37 const SourceManager &SM,
38 const ASTContext *Context) {
39 assert(
Loc.isValid());
41 while (isWhitespace(*SM.getCharacterData(
Loc)))
42 Loc =
Loc.getLocWithOffset(1);
44 tok::TokenKind TokKind = getTokenKind(
Loc, SM, Context);
45 if (TokKind != tok::comment)
49 Loc = Lexer::getLocForEndOfToken(
Loc, 0, SM, Context->getLangOpts());
53 SourceLocation findEndLocation(SourceLocation LastTokenLoc,
54 const SourceManager &SM,
55 const ASTContext *Context) {
57 Lexer::GetBeginningOfToken(LastTokenLoc, SM, Context->getLangOpts());
60 assert(
Loc.isValid());
61 bool SkipEndWhitespaceAndComments =
true;
62 tok::TokenKind TokKind = getTokenKind(
Loc, SM, Context);
63 if (TokKind == tok::NUM_TOKENS || TokKind == tok::semi ||
64 TokKind == tok::r_brace) {
67 SkipEndWhitespaceAndComments =
false;
70 Loc = Lexer::getLocForEndOfToken(
Loc, 0, SM, Context->getLangOpts());
72 if (SkipEndWhitespaceAndComments) {
73 Loc = forwardSkipWhitespaceAndComments(
Loc, SM, Context);
74 tok::TokenKind TokKind = getTokenKind(
Loc, SM, Context);
75 if (TokKind == tok::semi)
76 Loc = Lexer::getLocForEndOfToken(
Loc, 0, SM, Context->getLangOpts());
80 assert(
Loc.isValid());
81 while (isHorizontalWhitespace(*SM.getCharacterData(
Loc))) {
82 Loc =
Loc.getLocWithOffset(1);
85 if (isVerticalWhitespace(*SM.getCharacterData(
Loc))) {
89 tok::TokenKind TokKind = getTokenKind(
Loc, SM, Context);
90 if (TokKind != tok::comment) {
95 SourceLocation TokEndLoc =
96 Lexer::getLocForEndOfToken(
Loc, 0, SM, Context->getLangOpts());
97 SourceRange TokRange(
Loc, TokEndLoc);
98 StringRef Comment = Lexer::getSourceText(
99 CharSourceRange::getTokenRange(TokRange), SM, Context->getLangOpts());
100 if (Comment.startswith(
"/*") && Comment.find(
'\n') != StringRef::npos) {
114 BracesAroundStatementsCheck::BracesAroundStatementsCheck(
118 ShortStatementLines(Options.get(
"ShortStatementLines", 0U)) {}
122 Options.
store(Opts,
"ShortStatementLines", ShortStatementLines);
127 ifStmt(unless(allOf(isConstexpr(), isInTemplateInstantiation())))
130 Finder->addMatcher(whileStmt().bind(
"while"),
this);
131 Finder->addMatcher(doStmt().bind(
"do"),
this);
132 Finder->addMatcher(forStmt().bind(
"for"),
this);
133 Finder->addMatcher(cxxForRangeStmt().bind(
"for-range"),
this);
137 const MatchFinder::MatchResult &Result) {
138 const SourceManager &SM = *Result.SourceManager;
139 const ASTContext *Context = Result.Context;
142 if (
auto S = Result.Nodes.getNodeAs<ForStmt>(
"for")) {
143 checkStmt(Result, S->getBody(), S->getRParenLoc());
144 }
else if (
auto S = Result.Nodes.getNodeAs<CXXForRangeStmt>(
"for-range")) {
145 checkStmt(Result, S->getBody(), S->getRParenLoc());
146 }
else if (
auto S = Result.Nodes.getNodeAs<DoStmt>(
"do")) {
147 checkStmt(Result, S->getBody(), S->getDoLoc(), S->getWhileLoc());
148 }
else if (
auto S = Result.Nodes.getNodeAs<WhileStmt>(
"while")) {
149 SourceLocation StartLoc = findRParenLoc(S, SM, Context);
150 if (StartLoc.isInvalid())
152 checkStmt(Result, S->getBody(), StartLoc);
153 }
else if (
auto S = Result.Nodes.getNodeAs<IfStmt>(
"if")) {
154 SourceLocation StartLoc = findRParenLoc(S, SM, Context);
155 if (StartLoc.isInvalid())
157 if (ForceBracesStmts.erase(S))
158 ForceBracesStmts.insert(S->getThen());
159 bool BracedIf = checkStmt(Result, S->getThen(), StartLoc, S->getElseLoc());
160 const Stmt *Else = S->getElse();
161 if (Else && BracedIf)
162 ForceBracesStmts.insert(Else);
163 if (Else && !isa<IfStmt>(Else)) {
165 checkStmt(Result, Else, S->getElseLoc());
168 llvm_unreachable(
"Invalid match");
173 template <
typename IfOrWhileStmt>
175 BracesAroundStatementsCheck::findRParenLoc(
const IfOrWhileStmt *S,
176 const SourceManager &SM,
177 const ASTContext *Context) {
179 if (S->getBeginLoc().isMacroID())
180 return SourceLocation();
182 SourceLocation CondEndLoc = S->getCond()->getEndLoc();
183 if (
const DeclStmt *CondVar = S->getConditionVariableDeclStmt())
184 CondEndLoc = CondVar->getEndLoc();
186 if (!CondEndLoc.isValid()) {
187 return SourceLocation();
190 SourceLocation PastCondEndLoc =
191 Lexer::getLocForEndOfToken(CondEndLoc, 0, SM, Context->getLangOpts());
192 if (PastCondEndLoc.isInvalid())
193 return SourceLocation();
194 SourceLocation RParenLoc =
195 forwardSkipWhitespaceAndComments(PastCondEndLoc, SM, Context);
196 if (RParenLoc.isInvalid())
197 return SourceLocation();
198 tok::TokenKind TokKind = getTokenKind(RParenLoc, SM, Context);
199 if (TokKind != tok::r_paren)
200 return SourceLocation();
206 bool BracesAroundStatementsCheck::checkStmt(
207 const MatchFinder::MatchResult &Result,
const Stmt *S,
208 SourceLocation InitialLoc, SourceLocation EndLocHint) {
216 if (!S || isa<CompoundStmt>(S)) {
221 if (!InitialLoc.isValid())
223 const SourceManager &SM = *Result.SourceManager;
224 const ASTContext *Context = Result.Context;
227 CharSourceRange FileRange = Lexer::makeFileCharRange(
228 CharSourceRange::getTokenRange(S->getSourceRange()), SM,
229 Context->getLangOpts());
230 if (FileRange.isInvalid())
236 InitialLoc = Lexer::makeFileCharRange(
237 CharSourceRange::getCharRange(InitialLoc, S->getBeginLoc()),
238 SM, Context->getLangOpts())
240 if (InitialLoc.isInvalid())
242 SourceLocation StartLoc =
243 Lexer::getLocForEndOfToken(InitialLoc, 0, SM, Context->getLangOpts());
246 SourceLocation EndLoc;
247 std::string ClosingInsertion;
248 if (EndLocHint.isValid()) {
250 ClosingInsertion =
"} ";
252 const auto FREnd = FileRange.getEnd().getLocWithOffset(-1);
253 EndLoc = findEndLocation(FREnd, SM, Context);
254 ClosingInsertion =
"\n}";
257 assert(StartLoc.isValid());
258 assert(EndLoc.isValid());
261 if (ShortStatementLines && !ForceBracesStmts.erase(S)) {
262 unsigned StartLine = SM.getSpellingLineNumber(StartLoc);
263 unsigned EndLine = SM.getSpellingLineNumber(EndLoc);
264 if (EndLine - StartLine < ShortStatementLines)
268 auto Diag =
diag(StartLoc,
"statement should be inside braces");
269 Diag << FixItHint::CreateInsertion(StartLoc,
" {")
270 << FixItHint::CreateInsertion(EndLoc, ClosingInsertion);
275 ForceBracesStmts.clear();