Bug Summary

File:clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.cpp
Warning:line 553, column 31
Called C++ object pointer is null

Annotated Source Code

Press '?' to see keyboard shortcuts

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//===--- AvoidBindCheck.cpp - clang-tidy-----------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
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
30using namespace clang::ast_matchers;
31
32namespace clang {
33namespace tidy {
34namespace modernize {
35
36namespace {
37
38enum BindArgumentKind { BK_Temporary, BK_Placeholder, BK_CallExpr, BK_Other };
39enum CaptureMode { CM_None, CM_ByRef, CM_ByValue };
40enum CaptureExpr { CE_None, CE_Var, CE_InitExpression };
41
42enum CallableType {
43 CT_Other, // unknown
44 CT_Function, // global or static function
45 CT_MemberFunction, // member function with implicit this
46 CT_Object, // object with operator()
47};
48
49enum CallableMaterializationKind {
50 CMK_Other, // unknown
51 CMK_Function, // callable is the name of a member or non-member function.
52 CMK_VariableRef, // callable is a simple expression involving a global or
53 // local variable.
54 CMK_CallExpression, // callable is obtained as the result of a call expression
55};
56
57struct BindArgument {
58 // A rough classification of the type of expression this argument was.
59 BindArgumentKind Kind = BK_Other;
60
61 // If this argument required a capture, a value indicating how it was
62 // captured.
63 CaptureMode CM = CM_None;
64
65 // Whether the argument is a simple variable (we can capture it directly),
66 // or an expression (we must introduce a capture variable).
67 CaptureExpr CE = CE_None;
68
69 // The exact spelling of this argument in the source code.
70 StringRef SourceTokens;
71
72 // The identifier of the variable within the capture list. This may be
73 // different from UsageIdentifier for example in the expression *d, where the
74 // variable is captured as d, but referred to as *d.
75 std::string CaptureIdentifier;
76
77 // If this is a placeholder or capture init expression, contains the tokens
78 // used to refer to this parameter from within the body of the lambda.
79 std::string UsageIdentifier;
80
81 // If Kind == BK_Placeholder, the index of the placeholder.
82 size_t PlaceHolderIndex = 0;
83
84 // True if the argument is used inside the lambda, false otherwise.
85 bool IsUsed = false;
86
87 // The actual Expr object representing this expression.
88 const Expr *E = nullptr;
89};
90
91struct 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
103struct LambdaProperties {
104 CallableInfo Callable;
105 SmallVector<BindArgument, 4> BindArguments;
106 StringRef BindNamespace;
107 bool IsFixitSupported = false;
108};
109
110} // end namespace
111
112static bool tryCaptureAsLocalVariable(const MatchFinder::MatchResult &Result,
113 BindArgument &B, const Expr *E);
114
115static bool tryCaptureAsMemberVariable(const MatchFinder::MatchResult &Result,
116 BindArgument &B, const Expr *E);
117
118static 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
129static 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
140static 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
147static 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
157static void
158initializeBindArgumentForCallExpr(const MatchFinder::MatchResult &Result,
159 BindArgument &B, const CallExpr *CE,
160 unsigned &CaptureIndex) {
161 // std::ref(x) means to capture x by reference.
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 // The argument to std::ref is an expression that produces a reference.
169 // Create a capture reference to hold it.
170 B.CE = CE_InitExpression;
171 B.UsageIdentifier = "capture" + llvm::utostr(CaptureIndex++);
172 }
173 // Strip off the reference wrapper.
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
185static 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
198static 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
220static 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 // E is a direct use of "this".
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 // E refers to a data member without an explicit "this".
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
255static SmallVector<BindArgument, 4>
256buildBindArguments(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 // Start at index 1 as first argument to bind is the function name.
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 // Check for match with qualifiers removed.
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 // If it's not something we recognize, capture it by init expression to be
305 // safe.
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
322static 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
331static 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 // Placeholders (if present) have index 1 or greater.
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
365static 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
386static 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
397static std::vector<const FunctionDecl *>
398findCandidateCallOperators(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 // Find templated operator(), if any.
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
433static bool isFixitSupported(const CallableInfo &Callee,
434 ArrayRef<BindArgument> Args) {
435 // Do not attempt to create fixits for nested std::bind or std::ref.
436 // Supporting nested std::bind will be more difficult due to placeholder
437 // sharing between outer and inner std::bind invocations, and std::ref
438 // requires us to capture some parameters by reference instead of by value.
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 // Do not attempt to create fixits when placeholders are reused.
447 // Unused placeholders are supported by requiring C++14 generic lambdas.
448 // FIXME: Support this case by deducing the common type.
449 if (isPlaceHolderIndexRepeated(Args))
450 return false;
451
452 // If we can't determine the Decl being used, don't offer a fixit.
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
462const 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
472const FunctionDecl *
473getCallMethodDecl(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 // Maybe this is an indirect call through a function pointer or something
491 // where we can't determine the exact decl.
492 return nullptr;
493}
494
495static 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
517static CallableMaterializationKind
518getCallableMaterialization(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 // CE is something that looks like a call, with arguments - either
528 // a function call or a constructor invocation.
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
544static LambdaProperties
545getLambdaProperties(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
585static 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 // This capture has already been emitted.
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
607static 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
629static ArrayRef<BindArgument>
630getForwardedArgumentList(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}
637AvoidBindCheck::AvoidBindCheck(StringRef Name, ClangTidyContext *Context)
638 : ClangTidyCheck(Name, Context),
639 PermissiveParameterList(Options.get("PermissiveParameterList", false)) {}
640
641void AvoidBindCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
642 Options.store(Opts, "PermissiveParameterList", PermissiveParameterList);
643}
644
645void 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
657void 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[[gnu::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} // namespace modernize
722} // namespace tidy
723} // namespace clang