10
10
#include " clang/AST/ASTContext.h"
11
11
#include " clang/ASTMatchers/ASTMatchFinder.h"
12
12
#include " clang/Lex/Lexer.h"
13
+ #include " clang/Lex/Preprocessor.h"
13
14
#include " clang/Tooling/FixIt.h"
14
15
#include " llvm/ADT/SmallVector.h"
15
16
@@ -19,10 +20,30 @@ namespace clang {
19
20
namespace tidy {
20
21
namespace readability {
21
22
22
- static const char ReturnStr[] = " return" ;
23
- static const char ContinueStr[] = " continue" ;
24
- static const char BreakStr[] = " break" ;
25
- static const char ThrowStr[] = " throw" ;
23
+ namespace {
24
+
25
+ class PPConditionalCollector : public PPCallbacks {
26
+ public:
27
+ PPConditionalCollector (
28
+ ElseAfterReturnCheck::ConditionalBranchMap &Collections,
29
+ const SourceManager &SM)
30
+ : Collections(Collections), SM(SM) {}
31
+ void Endif (SourceLocation Loc, SourceLocation IfLoc) override {
32
+ if (!SM.isWrittenInSameFile (Loc, IfLoc))
33
+ return ;
34
+ SmallVectorImpl<SourceRange> &Collection = Collections[SM.getFileID (Loc)];
35
+ assert (Collection.empty () || Collection.back ().getEnd () < Loc);
36
+ Collection.emplace_back (IfLoc, Loc);
37
+ }
38
+
39
+ private:
40
+ ElseAfterReturnCheck::ConditionalBranchMap &Collections;
41
+ const SourceManager &SM;
42
+ };
43
+
44
+ } // namespace
45
+
46
+ static const char InterruptingStr[] = " interrupting" ;
26
47
static const char WarningMessage[] = " do not use 'else' after '%0'" ;
27
48
static const char WarnOnUnfixableStr[] = " WarnOnUnfixable" ;
28
49
static const char WarnOnConditionVariablesStr[] = " WarnOnConditionVariables" ;
@@ -140,11 +161,18 @@ void ElseAfterReturnCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
140
161
Options.store (Opts, WarnOnConditionVariablesStr, WarnOnConditionVariables);
141
162
}
142
163
164
+ void ElseAfterReturnCheck::registerPPCallbacks (const SourceManager &SM,
165
+ Preprocessor *PP,
166
+ Preprocessor *ModuleExpanderPP) {
167
+ PP->addPPCallbacks (
168
+ std::make_unique<PPConditionalCollector>(this ->PPConditionals , SM));
169
+ }
170
+
143
171
void ElseAfterReturnCheck::registerMatchers (MatchFinder *Finder) {
144
- const auto InterruptsControlFlow =
145
- stmt ( anyOf ( returnStmt ().bind (ReturnStr ), continueStmt ().bind (ContinueStr ),
146
- breakStmt ().bind (BreakStr ),
147
- expr (ignoringImplicit (cxxThrowExpr ().bind (ThrowStr )))));
172
+ const auto InterruptsControlFlow = stmt ( anyOf (
173
+ returnStmt ().bind (InterruptingStr ), continueStmt ().bind (InterruptingStr ),
174
+ breakStmt ().bind (InterruptingStr ),
175
+ expr (ignoringImplicit (cxxThrowExpr ().bind (InterruptingStr )))));
148
176
Finder->addMatcher (
149
177
compoundStmt (
150
178
forEach (ifStmt (unless (isConstexpr ()),
@@ -157,21 +185,72 @@ void ElseAfterReturnCheck::registerMatchers(MatchFinder *Finder) {
157
185
this );
158
186
}
159
187
188
+ static bool hasPreprocessorBranchEndBetweenLocations (
189
+ const ElseAfterReturnCheck::ConditionalBranchMap &ConditionalBranchMap,
190
+ const SourceManager &SM, SourceLocation StartLoc, SourceLocation EndLoc) {
191
+
192
+ SourceLocation ExpandedStartLoc = SM.getExpansionLoc (StartLoc);
193
+ SourceLocation ExpandedEndLoc = SM.getExpansionLoc (EndLoc);
194
+ if (!SM.isWrittenInSameFile (ExpandedStartLoc, ExpandedEndLoc))
195
+ return false ;
196
+
197
+ // StartLoc and EndLoc expand to the same macro.
198
+ if (ExpandedStartLoc == ExpandedEndLoc)
199
+ return false ;
200
+
201
+ assert (ExpandedStartLoc < ExpandedEndLoc);
202
+
203
+ auto Iter = ConditionalBranchMap.find (SM.getFileID (ExpandedEndLoc));
204
+
205
+ if (Iter == ConditionalBranchMap.end () || Iter->getSecond ().empty ())
206
+ return false ;
207
+
208
+ const SmallVectorImpl<SourceRange> &ConditionalBranches = Iter->getSecond ();
209
+
210
+ assert (llvm::is_sorted (ConditionalBranches,
211
+ [](const SourceRange &LHS, const SourceRange &RHS) {
212
+ return LHS.getEnd () < RHS.getEnd ();
213
+ }));
214
+
215
+ // First conditional block that ends after ExpandedStartLoc.
216
+ const auto *Begin =
217
+ llvm::lower_bound (ConditionalBranches, ExpandedStartLoc,
218
+ [](const SourceRange &LHS, const SourceLocation &RHS) {
219
+ return LHS.getEnd () < RHS;
220
+ });
221
+ const auto *End = ConditionalBranches.end ();
222
+ for (; Begin != End && Begin->getEnd () < ExpandedEndLoc; ++Begin)
223
+ if (Begin->getBegin () < ExpandedStartLoc)
224
+ return true ;
225
+ return false ;
226
+ }
227
+
228
+ static StringRef getControlFlowString (const Stmt &Stmt) {
229
+ if (isa<ReturnStmt>(Stmt))
230
+ return " return" ;
231
+ if (isa<ContinueStmt>(Stmt))
232
+ return " continue" ;
233
+ if (isa<BreakStmt>(Stmt))
234
+ return " break" ;
235
+ if (isa<CXXThrowExpr>(Stmt))
236
+ return " throw" ;
237
+ llvm_unreachable (" Unknown control flow interruptor" );
238
+ }
239
+
160
240
void ElseAfterReturnCheck::check (const MatchFinder::MatchResult &Result) {
161
241
const auto *If = Result.Nodes .getNodeAs <IfStmt>(" if" );
162
242
const auto *Else = Result.Nodes .getNodeAs <Stmt>(" else" );
163
243
const auto *OuterScope = Result.Nodes .getNodeAs <CompoundStmt>(" cs" );
164
-
165
- bool IsLastInScope = OuterScope->body_back () == If;
244
+ const auto *Interrupt = Result.Nodes .getNodeAs <Stmt>(InterruptingStr);
166
245
SourceLocation ElseLoc = If->getElseLoc ();
167
246
168
- auto ControlFlowInterruptor = [&]() -> llvm::StringRef {
169
- for (llvm::StringRef BindingName :
170
- {ReturnStr, ContinueStr, BreakStr, ThrowStr} )
171
- if (Result. Nodes . getNodeAs <Stmt>(BindingName))
172
- return BindingName;
173
- return {} ;
174
- }( );
247
+ if ( hasPreprocessorBranchEndBetweenLocations (
248
+ PPConditionals, *Result. SourceManager , Interrupt-> getBeginLoc (),
249
+ ElseLoc) )
250
+ return ;
251
+
252
+ bool IsLastInScope = OuterScope-> body_back () == If ;
253
+ StringRef ControlFlowInterruptor = getControlFlowString (*Interrupt );
175
254
176
255
if (!IsLastInScope && containsDeclInScope (Else)) {
177
256
if (WarnOnUnfixable) {
0 commit comments