7
7
// ===----------------------------------------------------------------------===//
8
8
9
9
#include " BracesAroundStatementsCheck.h"
10
+ #include " ../utils/BracesAroundStatement.h"
10
11
#include " ../utils/LexerUtils.h"
11
12
#include " clang/AST/ASTContext.h"
12
13
#include " clang/ASTMatchers/ASTMatchers.h"
@@ -17,12 +18,10 @@ using namespace clang::ast_matchers;
17
18
namespace clang ::tidy::readability {
18
19
19
20
static tok::TokenKind getTokenKind (SourceLocation Loc, const SourceManager &SM,
20
- const ASTContext *Context ) {
21
+ const LangOptions &LangOpts ) {
21
22
Token Tok;
22
- SourceLocation Beginning =
23
- Lexer::GetBeginningOfToken (Loc, SM, Context->getLangOpts ());
24
- const bool Invalid =
25
- Lexer::getRawToken (Beginning, Tok, SM, Context->getLangOpts ());
23
+ SourceLocation Beginning = Lexer::GetBeginningOfToken (Loc, SM, LangOpts);
24
+ const bool Invalid = Lexer::getRawToken (Beginning, Tok, SM, LangOpts);
26
25
assert (!Invalid && " Expected a valid token." );
27
26
28
27
if (Invalid)
@@ -33,64 +32,21 @@ static tok::TokenKind getTokenKind(SourceLocation Loc, const SourceManager &SM,
33
32
34
33
static SourceLocation
35
34
forwardSkipWhitespaceAndComments (SourceLocation Loc, const SourceManager &SM,
36
- const ASTContext *Context ) {
35
+ const LangOptions &LangOpts ) {
37
36
assert (Loc.isValid ());
38
37
for (;;) {
39
38
while (isWhitespace (*SM.getCharacterData (Loc)))
40
39
Loc = Loc.getLocWithOffset (1 );
41
40
42
- tok::TokenKind TokKind = getTokenKind (Loc, SM, Context );
41
+ tok::TokenKind TokKind = getTokenKind (Loc, SM, LangOpts );
43
42
if (TokKind != tok::comment)
44
43
return Loc;
45
44
46
45
// Fast-forward current token.
47
- Loc = Lexer::getLocForEndOfToken (Loc, 0 , SM, Context-> getLangOpts () );
46
+ Loc = Lexer::getLocForEndOfToken (Loc, 0 , SM, LangOpts );
48
47
}
49
48
}
50
49
51
- static SourceLocation findEndLocation (const Stmt &S, const SourceManager &SM,
52
- const ASTContext *Context) {
53
- SourceLocation Loc =
54
- utils::lexer::getUnifiedEndLoc (S, SM, Context->getLangOpts ());
55
- if (!Loc.isValid ())
56
- return Loc;
57
-
58
- // Start searching right after S.
59
- Loc = Loc.getLocWithOffset (1 );
60
-
61
- for (;;) {
62
- assert (Loc.isValid ());
63
- while (isHorizontalWhitespace (*SM.getCharacterData (Loc))) {
64
- Loc = Loc.getLocWithOffset (1 );
65
- }
66
-
67
- if (isVerticalWhitespace (*SM.getCharacterData (Loc))) {
68
- // EOL, insert brace before.
69
- break ;
70
- }
71
- tok::TokenKind TokKind = getTokenKind (Loc, SM, Context);
72
- if (TokKind != tok::comment) {
73
- // Non-comment token, insert brace before.
74
- break ;
75
- }
76
-
77
- SourceLocation TokEndLoc =
78
- Lexer::getLocForEndOfToken (Loc, 0 , SM, Context->getLangOpts ());
79
- SourceRange TokRange (Loc, TokEndLoc);
80
- StringRef Comment = Lexer::getSourceText (
81
- CharSourceRange::getTokenRange (TokRange), SM, Context->getLangOpts ());
82
- if (Comment.starts_with (" /*" ) && Comment.contains (' \n ' )) {
83
- // Multi-line block comment, insert brace before.
84
- break ;
85
- }
86
- // else: Trailing comment, insert brace after the newline.
87
-
88
- // Fast-forward current token.
89
- Loc = TokEndLoc;
90
- }
91
- return Loc;
92
- }
93
-
94
50
BracesAroundStatementsCheck::BracesAroundStatementsCheck (
95
51
StringRef Name, ClangTidyContext *Context)
96
52
: ClangTidyCheck(Name, Context),
@@ -124,7 +80,7 @@ void BracesAroundStatementsCheck::check(
124
80
} else if (const auto *S = Result.Nodes .getNodeAs <DoStmt>(" do" )) {
125
81
checkStmt (Result, S->getBody (), S->getDoLoc (), S->getWhileLoc ());
126
82
} else if (const auto *S = Result.Nodes .getNodeAs <WhileStmt>(" while" )) {
127
- SourceLocation StartLoc = findRParenLoc (S, SM, Context);
83
+ SourceLocation StartLoc = findRParenLoc (S, SM, Context-> getLangOpts () );
128
84
if (StartLoc.isInvalid ())
129
85
return ;
130
86
checkStmt (Result, S->getBody (), StartLoc);
@@ -133,7 +89,7 @@ void BracesAroundStatementsCheck::check(
133
89
if (S->isConsteval ())
134
90
return ;
135
91
136
- SourceLocation StartLoc = findRParenLoc (S, SM, Context);
92
+ SourceLocation StartLoc = findRParenLoc (S, SM, Context-> getLangOpts () );
137
93
if (StartLoc.isInvalid ())
138
94
return ;
139
95
if (ForceBracesStmts.erase (S))
@@ -156,7 +112,7 @@ template <typename IfOrWhileStmt>
156
112
SourceLocation
157
113
BracesAroundStatementsCheck::findRParenLoc (const IfOrWhileStmt *S,
158
114
const SourceManager &SM,
159
- const ASTContext *Context ) {
115
+ const LangOptions &LangOpts ) {
160
116
// Skip macros.
161
117
if (S->getBeginLoc ().isMacroID ())
162
118
return {};
@@ -170,14 +126,14 @@ BracesAroundStatementsCheck::findRParenLoc(const IfOrWhileStmt *S,
170
126
}
171
127
172
128
SourceLocation PastCondEndLoc =
173
- Lexer::getLocForEndOfToken (CondEndLoc, 0 , SM, Context-> getLangOpts () );
129
+ Lexer::getLocForEndOfToken (CondEndLoc, 0 , SM, LangOpts );
174
130
if (PastCondEndLoc.isInvalid ())
175
131
return {};
176
132
SourceLocation RParenLoc =
177
- forwardSkipWhitespaceAndComments (PastCondEndLoc, SM, Context );
133
+ forwardSkipWhitespaceAndComments (PastCondEndLoc, SM, LangOpts );
178
134
if (RParenLoc.isInvalid ())
179
135
return {};
180
- tok::TokenKind TokKind = getTokenKind (RParenLoc, SM, Context );
136
+ tok::TokenKind TokKind = getTokenKind (RParenLoc, SM, LangOpts );
181
137
if (TokKind != tok::r_paren)
182
138
return {};
183
139
return RParenLoc;
@@ -188,86 +144,23 @@ BracesAroundStatementsCheck::findRParenLoc(const IfOrWhileStmt *S,
188
144
bool BracesAroundStatementsCheck::checkStmt (
189
145
const MatchFinder::MatchResult &Result, const Stmt *S,
190
146
SourceLocation StartLoc, SourceLocation EndLocHint) {
191
-
192
147
while (const auto *AS = dyn_cast<AttributedStmt>(S))
193
148
S = AS->getSubStmt ();
194
149
195
- const SourceManager &SM = *Result.SourceManager ;
196
- const ASTContext *Context = Result.Context ;
197
-
198
- // 1) If there's a corresponding "else" or "while", the check inserts "} "
199
- // right before that token.
200
- // 2) If there's a multi-line block comment starting on the same line after
201
- // the location we're inserting the closing brace at, or there's a non-comment
202
- // token, the check inserts "\n}" right before that token.
203
- // 3) Otherwise the check finds the end of line (possibly after some block or
204
- // line comments) and inserts "\n}" right before that EOL.
205
- if (!S || isa<CompoundStmt>(S)) {
206
- // Already inside braces.
207
- return false ;
208
- }
209
-
210
- // When TreeTransform, Stmt in constexpr IfStmt will be transform to NullStmt.
211
- // This NullStmt can be detected according to beginning token.
212
- const SourceLocation StmtBeginLoc = S->getBeginLoc ();
213
- if (isa<NullStmt>(S) && StmtBeginLoc.isValid () &&
214
- getTokenKind (StmtBeginLoc, SM, Context) == tok::l_brace)
215
- return false ;
216
-
217
- if (StartLoc.isInvalid ())
218
- return false ;
219
-
220
- // Convert StartLoc to file location, if it's on the same macro expansion
221
- // level as the start of the statement. We also need file locations for
222
- // Lexer::getLocForEndOfToken working properly.
223
- StartLoc = Lexer::makeFileCharRange (
224
- CharSourceRange::getCharRange (StartLoc, S->getBeginLoc ()), SM,
225
- Context->getLangOpts ())
226
- .getBegin ();
227
- if (StartLoc.isInvalid ())
228
- return false ;
229
- StartLoc =
230
- Lexer::getLocForEndOfToken (StartLoc, 0 , SM, Context->getLangOpts ());
231
-
232
- // StartLoc points at the location of the opening brace to be inserted.
233
- SourceLocation EndLoc;
234
- std::string ClosingInsertion;
235
- if (EndLocHint.isValid ()) {
236
- EndLoc = EndLocHint;
237
- ClosingInsertion = " } " ;
238
- } else {
239
- EndLoc = findEndLocation (*S, SM, Context);
240
- ClosingInsertion = " \n }" ;
241
- }
242
-
243
- assert (StartLoc.isValid ());
244
-
245
- // Don't require braces for statements spanning less than certain number of
246
- // lines.
247
- if (ShortStatementLines && !ForceBracesStmts.erase (S)) {
248
- unsigned StartLine = SM.getSpellingLineNumber (StartLoc);
249
- unsigned EndLine = SM.getSpellingLineNumber (EndLoc);
250
- if (EndLine - StartLine < ShortStatementLines)
150
+ const auto BraceInsertionHints = utils::getBraceInsertionsHints (
151
+ S, Result.Context ->getLangOpts (), *Result.SourceManager , StartLoc,
152
+ EndLocHint);
153
+ if (BraceInsertionHints) {
154
+ if (ShortStatementLines && !ForceBracesStmts.erase (S) &&
155
+ BraceInsertionHints.resultingCompoundLineExtent (*Result.SourceManager ) <
156
+ ShortStatementLines)
251
157
return false ;
158
+ auto Diag = diag (BraceInsertionHints.DiagnosticPos ,
159
+ " statement should be inside braces" );
160
+ if (BraceInsertionHints.offersFixIts ())
161
+ Diag << BraceInsertionHints.openingBraceFixIt ()
162
+ << BraceInsertionHints.closingBraceFixIt ();
252
163
}
253
-
254
- auto Diag = diag (StartLoc, " statement should be inside braces" );
255
-
256
- // Change only if StartLoc and EndLoc are on the same macro expansion level.
257
- // This will also catch invalid EndLoc.
258
- // Example: LLVM_DEBUG( for(...) do_something() );
259
- // In this case fix-it cannot be provided as the semicolon which is not
260
- // visible here is part of the macro. Adding braces here would require adding
261
- // another semicolon.
262
- if (Lexer::makeFileCharRange (
263
- CharSourceRange::getTokenRange (SourceRange (
264
- SM.getSpellingLoc (StartLoc), SM.getSpellingLoc (EndLoc))),
265
- SM, Context->getLangOpts ())
266
- .isInvalid ())
267
- return false ;
268
-
269
- Diag << FixItHint::CreateInsertion (StartLoc, " {" )
270
- << FixItHint::CreateInsertion (EndLoc, ClosingInsertion);
271
164
return true ;
272
165
}
273
166
0 commit comments