@@ -35,7 +35,8 @@ namespace modernize {
35
35
namespace {
36
36
37
37
enum BindArgumentKind { BK_Temporary, BK_Placeholder, BK_CallExpr, BK_Other };
38
- enum CaptureMode { CM_None, CM_ByRef, CM_ByValue, CM_InitExpression };
38
+ enum CaptureMode { CM_None, CM_ByRef, CM_ByValue };
39
+ enum CaptureExpr { CE_None, CE_Var, CE_InitExpression };
39
40
40
41
enum CallableType {
41
42
CT_Other, // unknown
@@ -60,6 +61,10 @@ struct BindArgument {
60
61
// captured.
61
62
CaptureMode CM = CM_None;
62
63
64
+ // Whether the argument is a simple variable (we can capture it directly),
65
+ // or an expression (we must introduce a capture variable).
66
+ CaptureExpr CE = CE_None;
67
+
63
68
// The exact spelling of this argument in the source code.
64
69
StringRef SourceTokens;
65
70
@@ -86,6 +91,7 @@ struct CallableInfo {
86
91
CallableType Type = CT_Other;
87
92
CallableMaterializationKind Materialization = CMK_Other;
88
93
CaptureMode CM = CM_None;
94
+ CaptureExpr CE = CE_None;
89
95
StringRef SourceTokens;
90
96
std::string CaptureIdentifier;
91
97
std::string UsageIdentifier;
@@ -102,6 +108,12 @@ struct LambdaProperties {
102
108
103
109
} // end namespace
104
110
111
+ static bool tryCaptureAsLocalVariable (const MatchFinder::MatchResult &Result,
112
+ BindArgument &B, const Expr *E);
113
+
114
+ static bool tryCaptureAsMemberVariable (const MatchFinder::MatchResult &Result,
115
+ BindArgument &B, const Expr *E);
116
+
105
117
static const Expr *ignoreTemporariesAndPointers (const Expr *E) {
106
118
if (const auto *T = dyn_cast<UnaryOperator>(E))
107
119
return ignoreTemporariesAndPointers (T->getSubExpr ());
@@ -148,12 +160,22 @@ initializeBindArgumentForCallExpr(const MatchFinder::MatchResult &Result,
148
160
// std::ref(x) means to capture x by reference.
149
161
if (isCallExprNamed (CE, " boost::ref" ) || isCallExprNamed (CE, " std::ref" )) {
150
162
B.Kind = BK_Other;
163
+ if (tryCaptureAsLocalVariable (Result, B, CE->getArg (0 )) ||
164
+ tryCaptureAsMemberVariable (Result, B, CE->getArg (0 ))) {
165
+ B.CE = CE_Var;
166
+ } else {
167
+ // The argument to std::ref is an expression that produces a reference.
168
+ // Create a capture reference to hold it.
169
+ B.CE = CE_InitExpression;
170
+ B.UsageIdentifier = " capture" + llvm::utostr (CaptureIndex++);
171
+ }
172
+ // Strip off the reference wrapper.
173
+ B.SourceTokens = getSourceTextForExpr (Result, CE->getArg (0 ));
151
174
B.CM = CM_ByRef;
152
- B.UsageIdentifier =
153
- std::string (getSourceTextForExpr (Result, CE->getArg (0 )));
154
175
} else {
155
176
B.Kind = BK_CallExpr;
156
- B.CM = CM_InitExpression;
177
+ B.CM = CM_ByValue;
178
+ B.CE = CE_InitExpression;
157
179
B.UsageIdentifier = " capture" + llvm::utostr (CaptureIndex++);
158
180
}
159
181
B.CaptureIdentifier = B.UsageIdentifier ;
@@ -204,6 +226,7 @@ static bool tryCaptureAsMemberVariable(const MatchFinder::MatchResult &Result,
204
226
205
227
E = E->IgnoreImplicit ();
206
228
if (isa<CXXThisExpr>(E)) {
229
+ // E is a direct use of "this".
207
230
B.CM = CM_ByValue;
208
231
B.UsageIdentifier = std::string (getSourceTextForExpr (Result, E));
209
232
B.CaptureIdentifier = " this" ;
@@ -217,10 +240,15 @@ static bool tryCaptureAsMemberVariable(const MatchFinder::MatchResult &Result,
217
240
if (!ME->isLValue () || !isa<FieldDecl>(ME->getMemberDecl ()))
218
241
return false ;
219
242
220
- B.CM = CM_ByValue;
221
- B.UsageIdentifier = std::string (getSourceTextForExpr (Result, E));
222
- B.CaptureIdentifier = " this" ;
223
- return true ;
243
+ if (isa<CXXThisExpr>(ME->getBase ())) {
244
+ // E refers to a data member without an explicit "this".
245
+ B.CM = CM_ByValue;
246
+ B.UsageIdentifier = std::string (getSourceTextForExpr (Result, E));
247
+ B.CaptureIdentifier = " this" ;
248
+ return true ;
249
+ }
250
+
251
+ return false ;
224
252
}
225
253
226
254
static SmallVector<BindArgument, 4 >
@@ -251,7 +279,10 @@ buildBindArguments(const MatchFinder::MatchResult &Result,
251
279
B.IsUsed = true ;
252
280
253
281
SmallVector<StringRef, 2 > Matches;
254
- if (MatchPlaceholder.match (B.SourceTokens , &Matches)) {
282
+ const auto *DRE = dyn_cast<DeclRefExpr>(E);
283
+ if (MatchPlaceholder.match (B.SourceTokens , &Matches) ||
284
+ // Check for match with qualifiers removed.
285
+ (DRE && MatchPlaceholder.match (DRE->getDecl ()->getName (), &Matches))) {
255
286
B.Kind = BK_Placeholder;
256
287
B.PlaceHolderIndex = std::stoi (std::string (Matches[1 ]));
257
288
B.UsageIdentifier = " PH" + llvm::utostr (B.PlaceHolderIndex );
@@ -273,11 +304,13 @@ buildBindArguments(const MatchFinder::MatchResult &Result,
273
304
// safe.
274
305
B.Kind = BK_Other;
275
306
if (IsObjectPtr) {
276
- B.CM = CM_InitExpression;
307
+ B.CE = CE_InitExpression;
308
+ B.CM = CM_ByValue;
277
309
B.UsageIdentifier = " ObjectPtr" ;
278
310
B.CaptureIdentifier = B.UsageIdentifier ;
279
311
} else if (anyDescendantIsLocal (B.E )) {
280
- B.CM = CM_InitExpression;
312
+ B.CE = CE_InitExpression;
313
+ B.CM = CM_ByValue;
281
314
B.CaptureIdentifier = " capture" + llvm::utostr (CaptureIndex++);
282
315
B.UsageIdentifier = B.CaptureIdentifier ;
283
316
}
@@ -337,9 +370,12 @@ static void addFunctionCallArgs(ArrayRef<BindArgument> Args,
337
370
338
371
Stream << Delimiter;
339
372
340
- if (B.Kind == BK_Placeholder || B.CM != CM_None)
373
+ if (B.Kind == BK_Placeholder) {
374
+ Stream << " std::forward<decltype(" << B.UsageIdentifier << " )>" ;
375
+ Stream << " (" << B.UsageIdentifier << " )" ;
376
+ } else if (B.CM != CM_None)
341
377
Stream << B.UsageIdentifier ;
342
- else if (B. CM == CM_None)
378
+ else
343
379
Stream << B.SourceTokens ;
344
380
345
381
Delimiter = " , " ;
@@ -357,9 +393,9 @@ static bool isPlaceHolderIndexRepeated(const ArrayRef<BindArgument> Args) {
357
393
return false ;
358
394
}
359
395
360
- static std::vector<const CXXMethodDecl *>
396
+ static std::vector<const FunctionDecl *>
361
397
findCandidateCallOperators (const CXXRecordDecl *RecordDecl, size_t NumArgs) {
362
- std::vector<const CXXMethodDecl *> Candidates;
398
+ std::vector<const FunctionDecl *> Candidates;
363
399
364
400
for (const clang::CXXMethodDecl *Method : RecordDecl->methods ()) {
365
401
OverloadedOperatorKind OOK = Method->getOverloadedOperator ();
@@ -373,6 +409,23 @@ findCandidateCallOperators(const CXXRecordDecl *RecordDecl, size_t NumArgs) {
373
409
Candidates.push_back (Method);
374
410
}
375
411
412
+ // Find templated operator(), if any.
413
+ for (const clang::Decl *D : RecordDecl->decls ()) {
414
+ const auto *FTD = dyn_cast<FunctionTemplateDecl>(D);
415
+ if (!FTD)
416
+ continue ;
417
+ const FunctionDecl *FD = FTD->getTemplatedDecl ();
418
+
419
+ OverloadedOperatorKind OOK = FD->getOverloadedOperator ();
420
+ if (OOK != OverloadedOperatorKind::OO_Call)
421
+ continue ;
422
+
423
+ if (FD->getNumParams () > NumArgs)
424
+ continue ;
425
+
426
+ Candidates.push_back (FD);
427
+ }
428
+
376
429
return Candidates;
377
430
}
378
431
@@ -407,7 +460,7 @@ static bool isFixitSupported(const CallableInfo &Callee,
407
460
408
461
const FunctionDecl *getCallOperator (const CXXRecordDecl *Callable,
409
462
size_t NumArgs) {
410
- std::vector<const CXXMethodDecl *> Candidates =
463
+ std::vector<const FunctionDecl *> Candidates =
411
464
findCandidateCallOperators (Callable, NumArgs);
412
465
if (Candidates.size () != 1 )
413
466
return nullptr ;
@@ -466,11 +519,15 @@ getCallableMaterialization(const MatchFinder::MatchResult &Result) {
466
519
467
520
const auto *NoTemporaries = ignoreTemporariesAndPointers (CallableExpr);
468
521
469
- if (isa<CallExpr>(NoTemporaries))
522
+ const auto *CE = dyn_cast<CXXConstructExpr>(NoTemporaries);
523
+ const auto *FC = dyn_cast<CXXFunctionalCastExpr>(NoTemporaries);
524
+ if ((isa<CallExpr>(NoTemporaries)) || (CE && (CE->getNumArgs () > 0 )) ||
525
+ (FC && (FC->getCastKind () == CK_ConstructorConversion)))
526
+ // CE is something that looks like a call, with arguments - either
527
+ // a function call or a constructor invocation.
470
528
return CMK_CallExpression;
471
529
472
- if (isa<CXXFunctionalCastExpr>(NoTemporaries) ||
473
- isa<CXXConstructExpr>(NoTemporaries))
530
+ if (isa<CXXFunctionalCastExpr>(NoTemporaries) || CE)
474
531
return CMK_Function;
475
532
476
533
if (const auto *DRE = dyn_cast<DeclRefExpr>(NoTemporaries)) {
@@ -503,13 +560,15 @@ getLambdaProperties(const MatchFinder::MatchResult &Result) {
503
560
getCallMethodDecl (Result, LP.Callable .Type , LP.Callable .Materialization );
504
561
LP.Callable .SourceTokens = getSourceTextForExpr (Result, CalleeExpr);
505
562
if (LP.Callable .Materialization == CMK_VariableRef) {
563
+ LP.Callable .CE = CE_Var;
506
564
LP.Callable .CM = CM_ByValue;
507
565
LP.Callable .UsageIdentifier =
508
566
std::string (getSourceTextForExpr (Result, CalleeExpr));
509
567
LP.Callable .CaptureIdentifier = std::string (
510
568
getSourceTextForExpr (Result, ignoreTemporariesAndPointers (CalleeExpr)));
511
569
} else if (LP.Callable .Materialization == CMK_CallExpression) {
512
- LP.Callable .CM = CM_InitExpression;
570
+ LP.Callable .CE = CE_InitExpression;
571
+ LP.Callable .CM = CM_ByValue;
513
572
LP.Callable .UsageIdentifier = " Func" ;
514
573
LP.Callable .CaptureIdentifier = " Func" ;
515
574
LP.Callable .CaptureInitializer = getSourceTextForExpr (Result, CalleeExpr);
@@ -523,7 +582,7 @@ getLambdaProperties(const MatchFinder::MatchResult &Result) {
523
582
}
524
583
525
584
static bool emitCapture (llvm::StringSet<> &CaptureSet, StringRef Delimiter,
526
- CaptureMode CM, StringRef Identifier,
585
+ CaptureMode CM, CaptureExpr CE, StringRef Identifier,
527
586
StringRef InitExpression, raw_ostream &Stream) {
528
587
if (CM == CM_None)
529
588
return false ;
@@ -537,7 +596,7 @@ static bool emitCapture(llvm::StringSet<> &CaptureSet, StringRef Delimiter,
537
596
if (CM == CM_ByRef)
538
597
Stream << " &" ;
539
598
Stream << Identifier;
540
- if (CM == CM_InitExpression )
599
+ if (CE == CE_InitExpression )
541
600
Stream << " = " << InitExpression;
542
601
543
602
CaptureSet.insert (Identifier);
@@ -550,17 +609,17 @@ static void emitCaptureList(const LambdaProperties &LP,
550
609
llvm::StringSet<> CaptureSet;
551
610
bool AnyCapturesEmitted = false ;
552
611
553
- AnyCapturesEmitted = emitCapture (CaptureSet, " " , LP. Callable . CM ,
554
- LP.Callable .CaptureIdentifier ,
555
- LP.Callable .CaptureInitializer , Stream);
612
+ AnyCapturesEmitted = emitCapture (
613
+ CaptureSet, " " , LP. Callable . CM , LP.Callable .CE ,
614
+ LP. Callable . CaptureIdentifier , LP.Callable .CaptureInitializer , Stream);
556
615
557
616
for (const BindArgument &B : LP.BindArguments ) {
558
617
if (B.CM == CM_None || !B.IsUsed )
559
618
continue ;
560
619
561
620
StringRef Delimiter = AnyCapturesEmitted ? " , " : " " ;
562
621
563
- if (emitCapture (CaptureSet, Delimiter, B.CM , B.CaptureIdentifier ,
622
+ if (emitCapture (CaptureSet, Delimiter, B.CM , B.CE , B. CaptureIdentifier ,
564
623
B.SourceTokens , Stream))
565
624
AnyCapturesEmitted = true ;
566
625
}
@@ -635,19 +694,18 @@ void AvoidBindCheck::check(const MatchFinder::MatchResult &Result) {
635
694
Stream << MethodDecl->getName ();
636
695
} else {
637
696
Stream << " { return " ;
638
- switch (LP.Callable .CM ) {
639
- case CM_ByValue:
640
- case CM_ByRef:
697
+ switch (LP.Callable .CE ) {
698
+ case CE_Var:
641
699
if (LP.Callable .UsageIdentifier != LP.Callable .CaptureIdentifier ) {
642
700
Stream << " (" << LP.Callable .UsageIdentifier << " )" ;
643
701
break ;
644
702
}
645
703
LLVM_FALLTHROUGH;
646
- case CM_InitExpression :
704
+ case CE_InitExpression :
647
705
Stream << LP.Callable .UsageIdentifier ;
648
706
break ;
649
707
default :
650
- Ref-> printPretty ( Stream, nullptr , Result. Context -> getPrintingPolicy () );
708
+ Stream << getSourceTextForExpr (Result, Ref );
651
709
}
652
710
}
653
711
0 commit comments