clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name AvoidBindCheck.cpp -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mframe-pointer=none -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/build-llvm/tools/clang/stage2-bins -resource-dir /usr/lib/llvm-14/lib/clang/14.0.0 -D _DEBUG -D _GNU_SOURCE -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -I tools/clang/tools/extra/clang-tidy/modernize -I /build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/clang-tools-extra/clang-tidy/modernize -I tools/clang/tools/extra/clang-tidy -I /build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/clang/include -I tools/clang/include -I include -I /build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/llvm/include -D _FORTIFY_SOURCE=2 -D NDEBUG -U NDEBUG -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/backward -internal-isystem /usr/lib/llvm-14/lib/clang/14.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fmacro-prefix-map=/build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/= -fcoverage-prefix-map=/build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fcoverage-prefix-map=/build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/= -O3 -Wno-unused-command-line-argument -Wno-unused-parameter -Wwrite-strings -Wno-missing-field-initializers -Wno-long-long -Wno-maybe-uninitialized -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wno-comment -std=c++14 -fdeprecated-macro -fdebug-compilation-dir=/build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/= -ferror-limit 19 -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-01-31-132209-101875-1 -x c++ /build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.cpp
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | #include "AvoidBindCheck.h" |
10 | #include "clang/AST/ASTContext.h" |
11 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
12 | #include "clang/Basic/LLVM.h" |
13 | #include "clang/Basic/LangOptions.h" |
14 | #include "clang/Basic/SourceLocation.h" |
15 | #include "clang/Lex/Lexer.h" |
16 | #include "llvm/ADT/ArrayRef.h" |
17 | #include "llvm/ADT/STLExtras.h" |
18 | #include "llvm/ADT/SmallSet.h" |
19 | #include "llvm/ADT/SmallVector.h" |
20 | #include "llvm/ADT/StringRef.h" |
21 | #include "llvm/ADT/StringSet.h" |
22 | #include "llvm/Support/Casting.h" |
23 | #include "llvm/Support/FormatVariadic.h" |
24 | #include "llvm/Support/Regex.h" |
25 | #include "llvm/Support/raw_ostream.h" |
26 | #include <algorithm> |
27 | #include <cstddef> |
28 | #include <string> |
29 | |
30 | using namespace clang::ast_matchers; |
31 | |
32 | namespace clang { |
33 | namespace tidy { |
34 | namespace modernize { |
35 | |
36 | namespace { |
37 | |
38 | enum BindArgumentKind { BK_Temporary, BK_Placeholder, BK_CallExpr, BK_Other }; |
39 | enum CaptureMode { CM_None, CM_ByRef, CM_ByValue }; |
40 | enum CaptureExpr { CE_None, CE_Var, CE_InitExpression }; |
41 | |
42 | enum CallableType { |
43 | CT_Other, |
44 | CT_Function, |
45 | CT_MemberFunction, |
46 | CT_Object, |
47 | }; |
48 | |
49 | enum CallableMaterializationKind { |
50 | CMK_Other, |
51 | CMK_Function, |
52 | CMK_VariableRef, |
53 | |
54 | CMK_CallExpression, |
55 | }; |
56 | |
57 | struct BindArgument { |
58 | |
59 | BindArgumentKind Kind = BK_Other; |
60 | |
61 | |
62 | |
63 | CaptureMode CM = CM_None; |
64 | |
65 | |
66 | |
67 | CaptureExpr CE = CE_None; |
68 | |
69 | |
70 | StringRef SourceTokens; |
71 | |
72 | |
73 | |
74 | |
75 | std::string CaptureIdentifier; |
76 | |
77 | |
78 | |
79 | std::string UsageIdentifier; |
80 | |
81 | |
82 | size_t PlaceHolderIndex = 0; |
83 | |
84 | |
85 | bool IsUsed = false; |
86 | |
87 | |
88 | const Expr *E = nullptr; |
89 | }; |
90 | |
91 | struct CallableInfo { |
92 | CallableType Type = CT_Other; |
93 | CallableMaterializationKind Materialization = CMK_Other; |
94 | CaptureMode CM = CM_None; |
95 | CaptureExpr CE = CE_None; |
96 | StringRef SourceTokens; |
97 | std::string CaptureIdentifier; |
98 | std::string UsageIdentifier; |
99 | StringRef CaptureInitializer; |
100 | const FunctionDecl *Decl = nullptr; |
101 | }; |
102 | |
103 | struct LambdaProperties { |
104 | CallableInfo Callable; |
105 | SmallVector<BindArgument, 4> BindArguments; |
106 | StringRef BindNamespace; |
107 | bool IsFixitSupported = false; |
108 | }; |
109 | |
110 | } |
111 | |
112 | static bool tryCaptureAsLocalVariable(const MatchFinder::MatchResult &Result, |
113 | BindArgument &B, const Expr *E); |
114 | |
115 | static bool tryCaptureAsMemberVariable(const MatchFinder::MatchResult &Result, |
116 | BindArgument &B, const Expr *E); |
117 | |
118 | static const Expr *ignoreTemporariesAndPointers(const Expr *E) { |
119 | if (const auto *T = dyn_cast<UnaryOperator>(E)) |
120 | return ignoreTemporariesAndPointers(T->getSubExpr()); |
121 | |
122 | const Expr *F = E->IgnoreImplicit(); |
123 | if (E != F) |
124 | return ignoreTemporariesAndPointers(F); |
125 | |
126 | return E; |
127 | } |
128 | |
129 | static const Expr *ignoreTemporariesAndConstructors(const Expr *E) { |
130 | if (const auto *T = dyn_cast<CXXConstructExpr>(E)) |
131 | return ignoreTemporariesAndConstructors(T->getArg(0)); |
132 | |
133 | const Expr *F = E->IgnoreImplicit(); |
134 | if (E != F) |
135 | return ignoreTemporariesAndPointers(F); |
136 | |
137 | return E; |
138 | } |
139 | |
140 | static StringRef getSourceTextForExpr(const MatchFinder::MatchResult &Result, |
141 | const Expr *E) { |
142 | return Lexer::getSourceText( |
143 | CharSourceRange::getTokenRange(E->getBeginLoc(), E->getEndLoc()), |
144 | *Result.SourceManager, Result.Context->getLangOpts()); |
145 | } |
146 | |
147 | static bool isCallExprNamed(const Expr *E, StringRef Name) { |
148 | const auto *CE = dyn_cast<CallExpr>(E->IgnoreImplicit()); |
149 | if (!CE) |
150 | return false; |
151 | const auto *ND = dyn_cast<NamedDecl>(CE->getCalleeDecl()); |
152 | if (!ND) |
153 | return false; |
154 | return ND->getQualifiedNameAsString() == Name; |
155 | } |
156 | |
157 | static void |
158 | initializeBindArgumentForCallExpr(const MatchFinder::MatchResult &Result, |
159 | BindArgument &B, const CallExpr *CE, |
160 | unsigned &CaptureIndex) { |
161 | |
162 | if (isCallExprNamed(CE, "boost::ref") || isCallExprNamed(CE, "std::ref")) { |
163 | B.Kind = BK_Other; |
164 | if (tryCaptureAsLocalVariable(Result, B, CE->getArg(0)) || |
165 | tryCaptureAsMemberVariable(Result, B, CE->getArg(0))) { |
166 | B.CE = CE_Var; |
167 | } else { |
168 | |
169 | |
170 | B.CE = CE_InitExpression; |
171 | B.UsageIdentifier = "capture" + llvm::utostr(CaptureIndex++); |
172 | } |
173 | |
174 | B.SourceTokens = getSourceTextForExpr(Result, CE->getArg(0)); |
175 | B.CM = CM_ByRef; |
176 | } else { |
177 | B.Kind = BK_CallExpr; |
178 | B.CM = CM_ByValue; |
179 | B.CE = CE_InitExpression; |
180 | B.UsageIdentifier = "capture" + llvm::utostr(CaptureIndex++); |
181 | } |
182 | B.CaptureIdentifier = B.UsageIdentifier; |
183 | } |
184 | |
185 | static bool anyDescendantIsLocal(const Stmt *Statement) { |
186 | if (const auto *DeclRef = dyn_cast<DeclRefExpr>(Statement)) { |
187 | const ValueDecl *Decl = DeclRef->getDecl(); |
188 | if (const auto *Var = dyn_cast_or_null<VarDecl>(Decl)) { |
189 | if (Var->isLocalVarDeclOrParm()) |
190 | return true; |
191 | } |
192 | } else if (isa<CXXThisExpr>(Statement)) |
193 | return true; |
194 | |
195 | return any_of(Statement->children(), anyDescendantIsLocal); |
196 | } |
197 | |
198 | static bool tryCaptureAsLocalVariable(const MatchFinder::MatchResult &Result, |
199 | BindArgument &B, const Expr *E) { |
200 | if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E)) { |
201 | if (const auto *CE = dyn_cast<CXXConstructExpr>(BTE->getSubExpr())) |
202 | return tryCaptureAsLocalVariable(Result, B, CE->getArg(0)); |
203 | return false; |
204 | } |
205 | |
206 | const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreImplicit()); |
207 | if (!DRE) |
208 | return false; |
209 | |
210 | const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()); |
211 | if (!VD || !VD->isLocalVarDeclOrParm()) |
212 | return false; |
213 | |
214 | B.CM = CM_ByValue; |
215 | B.UsageIdentifier = std::string(getSourceTextForExpr(Result, E)); |
216 | B.CaptureIdentifier = B.UsageIdentifier; |
217 | return true; |
218 | } |
219 | |
220 | static bool tryCaptureAsMemberVariable(const MatchFinder::MatchResult &Result, |
221 | BindArgument &B, const Expr *E) { |
222 | if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E)) { |
223 | if (const auto *CE = dyn_cast<CXXConstructExpr>(BTE->getSubExpr())) |
224 | return tryCaptureAsMemberVariable(Result, B, CE->getArg(0)); |
225 | return false; |
226 | } |
227 | |
228 | E = E->IgnoreImplicit(); |
229 | if (isa<CXXThisExpr>(E)) { |
230 | |
231 | B.CM = CM_ByValue; |
232 | B.UsageIdentifier = std::string(getSourceTextForExpr(Result, E)); |
233 | B.CaptureIdentifier = "this"; |
234 | return true; |
235 | } |
236 | |
237 | const auto *ME = dyn_cast<MemberExpr>(E); |
238 | if (!ME) |
239 | return false; |
240 | |
241 | if (!ME->isLValue() || !isa<FieldDecl>(ME->getMemberDecl())) |
242 | return false; |
243 | |
244 | if (isa<CXXThisExpr>(ME->getBase())) { |
245 | |
246 | B.CM = CM_ByValue; |
247 | B.UsageIdentifier = std::string(getSourceTextForExpr(Result, E)); |
248 | B.CaptureIdentifier = "this"; |
249 | return true; |
250 | } |
251 | |
252 | return false; |
253 | } |
254 | |
255 | static SmallVector<BindArgument, 4> |
256 | buildBindArguments(const MatchFinder::MatchResult &Result, |
257 | const CallableInfo &Callable) { |
258 | SmallVector<BindArgument, 4> BindArguments; |
259 | static llvm::Regex MatchPlaceholder("^_([0-9]+)$"); |
260 | |
261 | const auto *BindCall = Result.Nodes.getNodeAs<CallExpr>("bind"); |
262 | |
263 | |
264 | unsigned CaptureIndex = 0; |
265 | for (size_t I = 1, ArgCount = BindCall->getNumArgs(); I < ArgCount; ++I) { |
266 | |
267 | const Expr *E = BindCall->getArg(I); |
268 | BindArgument &B = BindArguments.emplace_back(); |
269 | |
270 | size_t ArgIndex = I - 1; |
271 | if (Callable.Type == CT_MemberFunction) |
272 | --ArgIndex; |
273 | |
274 | bool IsObjectPtr = (I == 1 && Callable.Type == CT_MemberFunction); |
275 | B.E = E; |
276 | B.SourceTokens = getSourceTextForExpr(Result, E); |
277 | |
278 | if (!Callable.Decl || ArgIndex < Callable.Decl->getNumParams() || |
279 | IsObjectPtr) |
280 | B.IsUsed = true; |
281 | |
282 | SmallVector<StringRef, 2> Matches; |
283 | const auto *DRE = dyn_cast<DeclRefExpr>(E); |
284 | if (MatchPlaceholder.match(B.SourceTokens, &Matches) || |
285 | |
286 | (DRE && MatchPlaceholder.match(DRE->getDecl()->getName(), &Matches))) { |
287 | B.Kind = BK_Placeholder; |
288 | B.PlaceHolderIndex = std::stoi(std::string(Matches[1])); |
289 | B.UsageIdentifier = "PH" + llvm::utostr(B.PlaceHolderIndex); |
290 | B.CaptureIdentifier = B.UsageIdentifier; |
291 | continue; |
292 | } |
293 | |
294 | if (const auto *CE = |
295 | dyn_cast<CallExpr>(ignoreTemporariesAndConstructors(E))) { |
296 | initializeBindArgumentForCallExpr(Result, B, CE, CaptureIndex); |
297 | continue; |
298 | } |
299 | |
300 | if (tryCaptureAsLocalVariable(Result, B, B.E) || |
301 | tryCaptureAsMemberVariable(Result, B, B.E)) |
302 | continue; |
303 | |
304 | |
305 | |
306 | B.Kind = BK_Other; |
307 | if (IsObjectPtr) { |
308 | B.CE = CE_InitExpression; |
309 | B.CM = CM_ByValue; |
310 | B.UsageIdentifier = "ObjectPtr"; |
311 | B.CaptureIdentifier = B.UsageIdentifier; |
312 | } else if (anyDescendantIsLocal(B.E)) { |
313 | B.CE = CE_InitExpression; |
314 | B.CM = CM_ByValue; |
315 | B.CaptureIdentifier = "capture" + llvm::utostr(CaptureIndex++); |
316 | B.UsageIdentifier = B.CaptureIdentifier; |
317 | } |
318 | } |
319 | return BindArguments; |
320 | } |
321 | |
322 | static int findPositionOfPlaceholderUse(ArrayRef<BindArgument> Args, |
323 | size_t PlaceholderIndex) { |
324 | for (size_t I = 0; I < Args.size(); ++I) |
325 | if (Args[I].PlaceHolderIndex == PlaceholderIndex) |
326 | return I; |
327 | |
328 | return -1; |
329 | } |
330 | |
331 | static void addPlaceholderArgs(const LambdaProperties &LP, |
332 | llvm::raw_ostream &Stream, |
333 | bool PermissiveParameterList) { |
334 | |
335 | ArrayRef<BindArgument> Args = LP.BindArguments; |
336 | |
337 | const auto *MaxPlaceholderIt = |
338 | std::max_element(Args.begin(), Args.end(), |
339 | [](const BindArgument &B1, const BindArgument &B2) { |
340 | return B1.PlaceHolderIndex < B2.PlaceHolderIndex; |
341 | }); |
342 | |
343 | |
344 | if (!PermissiveParameterList && (MaxPlaceholderIt == Args.end() || |
345 | MaxPlaceholderIt->PlaceHolderIndex == 0)) |
346 | return; |
347 | |
348 | size_t PlaceholderCount = MaxPlaceholderIt->PlaceHolderIndex; |
349 | Stream << "("; |
350 | StringRef Delimiter = ""; |
351 | for (size_t I = 1; I <= PlaceholderCount; ++I) { |
352 | Stream << Delimiter << "auto &&"; |
353 | |
354 | int ArgIndex = findPositionOfPlaceholderUse(Args, I); |
355 | |
356 | if (ArgIndex != -1 && Args[ArgIndex].IsUsed) |
357 | Stream << " " << Args[ArgIndex].UsageIdentifier; |
358 | Delimiter = ", "; |
359 | } |
360 | if (PermissiveParameterList) |
361 | Stream << Delimiter << "auto && ..."; |
362 | Stream << ")"; |
363 | } |
364 | |
365 | static void addFunctionCallArgs(ArrayRef<BindArgument> Args, |
366 | llvm::raw_ostream &Stream) { |
367 | StringRef Delimiter = ""; |
368 | |
369 | for (int I = 0, Size = Args.size(); I < Size; ++I) { |
370 | const BindArgument &B = Args[I]; |
371 | |
372 | Stream << Delimiter; |
373 | |
374 | if (B.Kind == BK_Placeholder) { |
375 | Stream << "std::forward<decltype(" << B.UsageIdentifier << ")>"; |
376 | Stream << "(" << B.UsageIdentifier << ")"; |
377 | } else if (B.CM != CM_None) |
378 | Stream << B.UsageIdentifier; |
379 | else |
380 | Stream << B.SourceTokens; |
381 | |
382 | Delimiter = ", "; |
383 | } |
384 | } |
385 | |
386 | static bool isPlaceHolderIndexRepeated(const ArrayRef<BindArgument> Args) { |
387 | llvm::SmallSet<size_t, 4> PlaceHolderIndices; |
388 | for (const BindArgument &B : Args) { |
389 | if (B.PlaceHolderIndex) { |
390 | if (!PlaceHolderIndices.insert(B.PlaceHolderIndex).second) |
391 | return true; |
392 | } |
393 | } |
394 | return false; |
395 | } |
396 | |
397 | static std::vector<const FunctionDecl *> |
398 | findCandidateCallOperators(const CXXRecordDecl *RecordDecl, size_t NumArgs) { |
399 | std::vector<const FunctionDecl *> Candidates; |
400 | |
401 | for (const clang::CXXMethodDecl *Method : RecordDecl->methods()) { |
402 | OverloadedOperatorKind OOK = Method->getOverloadedOperator(); |
403 | |
404 | if (OOK != OverloadedOperatorKind::OO_Call) |
405 | continue; |
406 | |
407 | if (Method->getNumParams() > NumArgs) |
408 | continue; |
409 | |
410 | Candidates.push_back(Method); |
411 | } |
412 | |
413 | |
414 | for (const clang::Decl *D : RecordDecl->decls()) { |
415 | const auto *FTD = dyn_cast<FunctionTemplateDecl>(D); |
416 | if (!FTD) |
417 | continue; |
418 | const FunctionDecl *FD = FTD->getTemplatedDecl(); |
419 | |
420 | OverloadedOperatorKind OOK = FD->getOverloadedOperator(); |
421 | if (OOK != OverloadedOperatorKind::OO_Call) |
422 | continue; |
423 | |
424 | if (FD->getNumParams() > NumArgs) |
425 | continue; |
426 | |
427 | Candidates.push_back(FD); |
428 | } |
429 | |
430 | return Candidates; |
431 | } |
432 | |
433 | static bool isFixitSupported(const CallableInfo &Callee, |
434 | ArrayRef<BindArgument> Args) { |
435 | |
436 | |
437 | |
438 | |
439 | if (any_of(Args, [](const BindArgument &B) { |
440 | return isCallExprNamed(B.E, "boost::bind") || |
441 | isCallExprNamed(B.E, "std::bind"); |
442 | })) { |
443 | return false; |
444 | } |
445 | |
446 | |
447 | |
448 | |
449 | if (isPlaceHolderIndexRepeated(Args)) |
450 | return false; |
451 | |
452 | |
453 | if (!Callee.Decl) |
454 | return false; |
455 | |
456 | if (Callee.Type == CT_Other || Callee.Materialization == CMK_Other) |
457 | return false; |
458 | |
459 | return true; |
460 | } |
461 | |
462 | const FunctionDecl *getCallOperator(const CXXRecordDecl *Callable, |
463 | size_t NumArgs) { |
464 | std::vector<const FunctionDecl *> Candidates = |
465 | findCandidateCallOperators(Callable, NumArgs); |
466 | if (Candidates.size() != 1) |
467 | return nullptr; |
468 | |
469 | return Candidates.front(); |
470 | } |
471 | |
472 | const FunctionDecl * |
473 | getCallMethodDecl(const MatchFinder::MatchResult &Result, CallableType Type, |
474 | CallableMaterializationKind Materialization) { |
475 | |
476 | const Expr *Callee = Result.Nodes.getNodeAs<Expr>("ref"); |
477 | const Expr *CallExpression = ignoreTemporariesAndPointers(Callee); |
478 | |
479 | if (Type == CT_Object) { |
480 | const auto *BindCall = Result.Nodes.getNodeAs<CallExpr>("bind"); |
481 | size_t NumArgs = BindCall->getNumArgs() - 1; |
482 | return getCallOperator(Callee->getType()->getAsCXXRecordDecl(), NumArgs); |
483 | } |
484 | |
485 | if (Materialization == CMK_Function) { |
486 | if (const auto *DRE = dyn_cast<DeclRefExpr>(CallExpression)) |
487 | return dyn_cast<FunctionDecl>(DRE->getDecl()); |
488 | } |
489 | |
490 | |
491 | |
492 | return nullptr; |
493 | } |
494 | |
495 | static CallableType getCallableType(const MatchFinder::MatchResult &Result) { |
496 | const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>("ref"); |
497 | |
498 | QualType QT = CallableExpr->getType(); |
499 | if (QT->isMemberFunctionPointerType()) |
500 | return CT_MemberFunction; |
501 | |
502 | if (QT->isFunctionPointerType() || QT->isFunctionReferenceType() || |
503 | QT->isFunctionType()) |
504 | return CT_Function; |
505 | |
506 | if (QT->isRecordType()) { |
507 | const CXXRecordDecl *Decl = QT->getAsCXXRecordDecl(); |
508 | if (!Decl) |
509 | return CT_Other; |
510 | |
511 | return CT_Object; |
512 | } |
513 | |
514 | return CT_Other; |
515 | } |
516 | |
517 | static CallableMaterializationKind |
518 | getCallableMaterialization(const MatchFinder::MatchResult &Result) { |
519 | const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>("ref"); |
520 | |
521 | const auto *NoTemporaries = ignoreTemporariesAndPointers(CallableExpr); |
522 | |
523 | const auto *CE = dyn_cast<CXXConstructExpr>(NoTemporaries); |
524 | const auto *FC = dyn_cast<CXXFunctionalCastExpr>(NoTemporaries); |
525 | if ((isa<CallExpr>(NoTemporaries)) || (CE && (CE->getNumArgs() > 0)) || |
526 | (FC && (FC->getCastKind() == CK_ConstructorConversion))) |
527 | |
528 | |
529 | return CMK_CallExpression; |
530 | |
531 | if (isa<CXXFunctionalCastExpr>(NoTemporaries) || CE) |
532 | return CMK_Function; |
533 | |
534 | if (const auto *DRE = dyn_cast<DeclRefExpr>(NoTemporaries)) { |
535 | if (isa<FunctionDecl>(DRE->getDecl())) |
536 | return CMK_Function; |
537 | if (isa<VarDecl>(DRE->getDecl())) |
538 | return CMK_VariableRef; |
539 | } |
540 | |
541 | return CMK_Other; |
542 | } |
543 | |
544 | static LambdaProperties |
545 | getLambdaProperties(const MatchFinder::MatchResult &Result) { |
546 | const auto *CalleeExpr = Result.Nodes.getNodeAs<Expr>("ref"); |
547 | |
548 | LambdaProperties LP; |
549 | |
550 | const auto *Bind = Result.Nodes.getNodeAs<CallExpr>("bind"); |
551 | const auto *Decl = dyn_cast<FunctionDecl>(Bind->getCalleeDecl()); |
| 2 | | Assuming the object is not a 'FunctionDecl' | |
|
| 3 | | 'Decl' initialized to a null pointer value | |
|
552 | const auto *NS = |
553 | dyn_cast<NamespaceDecl>(Decl->getEnclosingNamespaceContext()); |
| 4 | | Called C++ object pointer is null |
|
554 | while (NS->isInlineNamespace()) |
555 | NS = dyn_cast<NamespaceDecl>(NS->getDeclContext()); |
556 | LP.BindNamespace = NS->getName(); |
557 | |
558 | LP.Callable.Type = getCallableType(Result); |
559 | LP.Callable.Materialization = getCallableMaterialization(Result); |
560 | LP.Callable.Decl = |
561 | getCallMethodDecl(Result, LP.Callable.Type, LP.Callable.Materialization); |
562 | LP.Callable.SourceTokens = getSourceTextForExpr(Result, CalleeExpr); |
563 | if (LP.Callable.Materialization == CMK_VariableRef) { |
564 | LP.Callable.CE = CE_Var; |
565 | LP.Callable.CM = CM_ByValue; |
566 | LP.Callable.UsageIdentifier = |
567 | std::string(getSourceTextForExpr(Result, CalleeExpr)); |
568 | LP.Callable.CaptureIdentifier = std::string( |
569 | getSourceTextForExpr(Result, ignoreTemporariesAndPointers(CalleeExpr))); |
570 | } else if (LP.Callable.Materialization == CMK_CallExpression) { |
571 | LP.Callable.CE = CE_InitExpression; |
572 | LP.Callable.CM = CM_ByValue; |
573 | LP.Callable.UsageIdentifier = "Func"; |
574 | LP.Callable.CaptureIdentifier = "Func"; |
575 | LP.Callable.CaptureInitializer = getSourceTextForExpr(Result, CalleeExpr); |
576 | } |
577 | |
578 | LP.BindArguments = buildBindArguments(Result, LP.Callable); |
579 | |
580 | LP.IsFixitSupported = isFixitSupported(LP.Callable, LP.BindArguments); |
581 | |
582 | return LP; |
583 | } |
584 | |
585 | static bool emitCapture(llvm::StringSet<> &CaptureSet, StringRef Delimiter, |
586 | CaptureMode CM, CaptureExpr CE, StringRef Identifier, |
587 | StringRef InitExpression, raw_ostream &Stream) { |
588 | if (CM == CM_None) |
589 | return false; |
590 | |
591 | |
592 | if (CaptureSet.count(Identifier) != 0) |
593 | return false; |
594 | |
595 | Stream << Delimiter; |
596 | |
597 | if (CM == CM_ByRef) |
598 | Stream << "&"; |
599 | Stream << Identifier; |
600 | if (CE == CE_InitExpression) |
601 | Stream << " = " << InitExpression; |
602 | |
603 | CaptureSet.insert(Identifier); |
604 | return true; |
605 | } |
606 | |
607 | static void emitCaptureList(const LambdaProperties &LP, |
608 | const MatchFinder::MatchResult &Result, |
609 | raw_ostream &Stream) { |
610 | llvm::StringSet<> CaptureSet; |
611 | bool AnyCapturesEmitted = false; |
612 | |
613 | AnyCapturesEmitted = emitCapture( |
614 | CaptureSet, "", LP.Callable.CM, LP.Callable.CE, |
615 | LP.Callable.CaptureIdentifier, LP.Callable.CaptureInitializer, Stream); |
616 | |
617 | for (const BindArgument &B : LP.BindArguments) { |
618 | if (B.CM == CM_None || !B.IsUsed) |
619 | continue; |
620 | |
621 | StringRef Delimiter = AnyCapturesEmitted ? ", " : ""; |
622 | |
623 | if (emitCapture(CaptureSet, Delimiter, B.CM, B.CE, B.CaptureIdentifier, |
624 | B.SourceTokens, Stream)) |
625 | AnyCapturesEmitted = true; |
626 | } |
627 | } |
628 | |
629 | static ArrayRef<BindArgument> |
630 | getForwardedArgumentList(const LambdaProperties &P) { |
631 | ArrayRef<BindArgument> Args = makeArrayRef(P.BindArguments); |
632 | if (P.Callable.Type != CT_MemberFunction) |
633 | return Args; |
634 | |
635 | return Args.drop_front(); |
636 | } |
637 | AvoidBindCheck::AvoidBindCheck(StringRef Name, ClangTidyContext *Context) |
638 | : ClangTidyCheck(Name, Context), |
639 | PermissiveParameterList(Options.get("PermissiveParameterList", false)) {} |
640 | |
641 | void AvoidBindCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { |
642 | Options.store(Opts, "PermissiveParameterList", PermissiveParameterList); |
643 | } |
644 | |
645 | void AvoidBindCheck::registerMatchers(MatchFinder *Finder) { |
646 | Finder->addMatcher( |
647 | callExpr( |
648 | callee(namedDecl(hasAnyName("::boost::bind", "::std::bind"))), |
649 | hasArgument( |
650 | 0, anyOf(expr(hasType(memberPointerType())).bind("ref"), |
651 | expr(hasParent(materializeTemporaryExpr().bind("ref"))), |
652 | expr().bind("ref")))) |
653 | .bind("bind"), |
654 | this); |
655 | } |
656 | |
657 | void AvoidBindCheck::check(const MatchFinder::MatchResult &Result) { |
658 | const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>("bind"); |
659 | |
660 | LambdaProperties LP = getLambdaProperties(Result); |
| 1 | Calling 'getLambdaProperties' | |
|
661 | auto Diag = |
662 | diag(MatchedDecl->getBeginLoc(), |
663 | formatv("prefer a lambda to {0}::bind", LP.BindNamespace).str()); |
664 | if (!LP.IsFixitSupported) |
665 | return; |
666 | |
667 | const auto *Ref = Result.Nodes.getNodeAs<Expr>("ref"); |
668 | |
669 | std::string Buffer; |
670 | llvm::raw_string_ostream Stream(Buffer); |
671 | |
672 | Stream << "["; |
673 | emitCaptureList(LP, Result, Stream); |
674 | Stream << "]"; |
675 | |
676 | ArrayRef<BindArgument> FunctionCallArgs = makeArrayRef(LP.BindArguments); |
677 | |
678 | addPlaceholderArgs(LP, Stream, PermissiveParameterList); |
679 | |
680 | if (LP.Callable.Type == CT_Function) { |
681 | StringRef SourceTokens = LP.Callable.SourceTokens; |
682 | SourceTokens.consume_front("&"); |
683 | Stream << " { return " << SourceTokens; |
684 | } else if (LP.Callable.Type == CT_MemberFunction) { |
685 | const auto *MethodDecl = dyn_cast<CXXMethodDecl>(LP.Callable.Decl); |
686 | const BindArgument &ObjPtr = FunctionCallArgs.front(); |
687 | |
688 | Stream << " { "; |
689 | if (!isa<CXXThisExpr>(ignoreTemporariesAndPointers(ObjPtr.E))) { |
690 | Stream << ObjPtr.UsageIdentifier; |
691 | Stream << "->"; |
692 | } |
693 | |
694 | Stream << MethodDecl->getName(); |
695 | } else { |
696 | Stream << " { return "; |
697 | switch (LP.Callable.CE) { |
698 | case CE_Var: |
699 | if (LP.Callable.UsageIdentifier != LP.Callable.CaptureIdentifier) { |
700 | Stream << "(" << LP.Callable.UsageIdentifier << ")"; |
701 | break; |
702 | } |
703 | LLVM_FALLTHROUGH; |
704 | case CE_InitExpression: |
705 | Stream << LP.Callable.UsageIdentifier; |
706 | break; |
707 | default: |
708 | Stream << getSourceTextForExpr(Result, Ref); |
709 | } |
710 | } |
711 | |
712 | Stream << "("; |
713 | |
714 | addFunctionCallArgs(getForwardedArgumentList(LP), Stream); |
715 | Stream << "); }"; |
716 | |
717 | Diag << FixItHint::CreateReplacement(MatchedDecl->getSourceRange(), |
718 | Stream.str()); |
719 | } |
720 | |
721 | } |
722 | } |
723 | } |