10 #include "../utils/LexerUtils.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchers.h"
13 #include "clang/Lex/Lexer.h"
19 namespace readability {
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;
38 const ASTContext *Context) {
39 assert(
Loc.isValid());
41 while (isWhitespace(*SM.getCharacterData(
Loc)))
42 Loc =
Loc.getLocWithOffset(1);
45 if (TokKind != tok::comment)
49 Loc = Lexer::getLocForEndOfToken(
Loc, 0, SM, Context->getLangOpts());
54 const ASTContext *Context) {
61 Loc =
Loc.getLocWithOffset(1);
64 assert(
Loc.isValid());
65 while (isHorizontalWhitespace(*SM.getCharacterData(
Loc))) {
66 Loc =
Loc.getLocWithOffset(1);
69 if (isVerticalWhitespace(*SM.getCharacterData(
Loc))) {
74 if (TokKind != tok::comment) {
79 SourceLocation TokEndLoc =
80 Lexer::getLocForEndOfToken(
Loc, 0, SM, Context->getLangOpts());
81 SourceRange TokRange(
Loc, TokEndLoc);
83 CharSourceRange::getTokenRange(TokRange), SM, Context->getLangOpts());
84 if (Comment.startswith(
"/*") && Comment.contains(
'\n')) {
96 BracesAroundStatementsCheck::BracesAroundStatementsCheck(
100 ShortStatementLines(Options.get(
"ShortStatementLines", 0U)) {}
104 Options.
store(Opts,
"ShortStatementLines", ShortStatementLines);
108 Finder->addMatcher(ifStmt().bind(
"if"),
this);
109 Finder->addMatcher(whileStmt().bind(
"while"),
this);
110 Finder->addMatcher(doStmt().bind(
"do"),
this);
111 Finder->addMatcher(forStmt().bind(
"for"),
this);
112 Finder->addMatcher(cxxForRangeStmt().bind(
"for-range"),
this);
116 const MatchFinder::MatchResult &Result) {
117 const SourceManager &SM = *Result.SourceManager;
118 const ASTContext *Context = Result.Context;
121 if (
const auto *S = Result.Nodes.getNodeAs<ForStmt>(
"for")) {
122 checkStmt(Result, S->getBody(), S->getRParenLoc());
123 }
else if (
const auto *S =
124 Result.Nodes.getNodeAs<CXXForRangeStmt>(
"for-range")) {
125 checkStmt(Result, S->getBody(), S->getRParenLoc());
126 }
else if (
const auto *S = Result.Nodes.getNodeAs<DoStmt>(
"do")) {
127 checkStmt(Result, S->getBody(), S->getDoLoc(), S->getWhileLoc());
128 }
else if (
const auto *S = Result.Nodes.getNodeAs<WhileStmt>(
"while")) {
129 SourceLocation StartLoc = findRParenLoc(S, SM, Context);
130 if (StartLoc.isInvalid())
132 checkStmt(Result, S->getBody(), StartLoc);
133 }
else if (
const auto *S = Result.Nodes.getNodeAs<IfStmt>(
"if")) {
134 SourceLocation StartLoc = findRParenLoc(S, SM, Context);
135 if (StartLoc.isInvalid())
137 if (ForceBracesStmts.erase(S))
138 ForceBracesStmts.insert(S->getThen());
139 bool BracedIf = checkStmt(Result, S->getThen(), StartLoc, S->getElseLoc());
140 const Stmt *Else = S->getElse();
141 if (Else && BracedIf)
142 ForceBracesStmts.insert(Else);
143 if (Else && !isa<IfStmt>(Else)) {
145 checkStmt(Result, Else, S->getElseLoc());
148 llvm_unreachable(
"Invalid match");
153 template <
typename IfOrWhileStmt>
155 BracesAroundStatementsCheck::findRParenLoc(
const IfOrWhileStmt *S,
156 const SourceManager &SM,
157 const ASTContext *Context) {
159 if (S->getBeginLoc().isMacroID())
160 return SourceLocation();
162 SourceLocation CondEndLoc = S->getCond()->getEndLoc();
163 if (
const DeclStmt *CondVar = S->getConditionVariableDeclStmt())
164 CondEndLoc = CondVar->getEndLoc();
166 if (!CondEndLoc.isValid()) {
167 return SourceLocation();
170 SourceLocation PastCondEndLoc =
171 Lexer::getLocForEndOfToken(CondEndLoc, 0, SM, Context->getLangOpts());
172 if (PastCondEndLoc.isInvalid())
173 return SourceLocation();
174 SourceLocation RParenLoc =
176 if (RParenLoc.isInvalid())
177 return SourceLocation();
178 tok::TokenKind TokKind =
getTokenKind(RParenLoc, SM, Context);
179 if (TokKind != tok::r_paren)
180 return SourceLocation();
186 bool BracesAroundStatementsCheck::checkStmt(
187 const MatchFinder::MatchResult &Result,
const Stmt *S,
188 SourceLocation InitialLoc, SourceLocation EndLocHint) {
190 while (
const auto *AS = dyn_cast<AttributedStmt>(S))
191 S = AS->getSubStmt();
200 if (!S || isa<CompoundStmt>(S)) {
205 if (!InitialLoc.isValid())
207 const SourceManager &SM = *Result.SourceManager;
208 const ASTContext *Context = Result.Context;
213 InitialLoc = Lexer::makeFileCharRange(
214 CharSourceRange::getCharRange(InitialLoc, S->getBeginLoc()),
215 SM, Context->getLangOpts())
217 if (InitialLoc.isInvalid())
219 SourceLocation StartLoc =
220 Lexer::getLocForEndOfToken(InitialLoc, 0, SM, Context->getLangOpts());
223 SourceLocation EndLoc;
224 std::string ClosingInsertion;
225 if (EndLocHint.isValid()) {
227 ClosingInsertion =
"} ";
230 ClosingInsertion =
"\n}";
233 assert(StartLoc.isValid());
237 if (ShortStatementLines && !ForceBracesStmts.erase(S)) {
238 unsigned StartLine = SM.getSpellingLineNumber(StartLoc);
239 unsigned EndLine = SM.getSpellingLineNumber(EndLoc);
240 if (EndLine - StartLine < ShortStatementLines)
244 auto Diag =
diag(StartLoc,
"statement should be inside braces");
252 if (Lexer::makeFileCharRange(
253 CharSourceRange::getTokenRange(SourceRange(
254 SM.getSpellingLoc(StartLoc), SM.getSpellingLoc(EndLoc))),
255 SM, Context->getLangOpts())
259 Diag << FixItHint::CreateInsertion(StartLoc,
" {")
260 << FixItHint::CreateInsertion(EndLoc, ClosingInsertion);
265 ForceBracesStmts.clear();