10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "clang/Basic/LLVM.h"
13#include "clang/Basic/SourceLocation.h"
14#include "clang/Lex/Lexer.h"
15#include "llvm/ADT/ArrayRef.h"
16#include "llvm/ADT/STLExtras.h"
17#include "llvm/ADT/SmallSet.h"
18#include "llvm/ADT/SmallVector.h"
19#include "llvm/ADT/StringRef.h"
20#include "llvm/ADT/StringSet.h"
21#include "llvm/Support/FormatVariadic.h"
22#include "llvm/Support/Regex.h"
23#include "llvm/Support/raw_ostream.h"
33enum BindArgumentKind { BK_Temporary, BK_Placeholder, BK_CallExpr, BK_Other };
34enum CaptureMode { CM_None, CM_ByRef, CM_ByValue };
35enum CaptureExpr { CE_None, CE_Var, CE_InitExpression };
44enum CallableMaterializationKind {
54 BindArgumentKind Kind = BK_Other;
58 CaptureMode CM = CM_None;
62 CaptureExpr CE = CE_None;
65 StringRef SourceTokens;
70 std::string CaptureIdentifier;
74 std::string UsageIdentifier;
77 size_t PlaceHolderIndex = 0;
83 const Expr *E =
nullptr;
87 CallableType Type = CT_Other;
88 CallableMaterializationKind Materialization = CMK_Other;
89 CaptureMode CM = CM_None;
90 CaptureExpr CE = CE_None;
91 StringRef SourceTokens;
92 std::string CaptureIdentifier;
93 std::string UsageIdentifier;
94 StringRef CaptureInitializer;
95 const FunctionDecl *Decl =
nullptr;
96 bool DoesReturn =
false;
99struct LambdaProperties {
100 CallableInfo Callable;
101 SmallVector<BindArgument, 4> BindArguments;
102 StringRef BindNamespace;
103 bool IsFixitSupported =
false;
109 BindArgument &B,
const Expr *E);
112 BindArgument &B,
const Expr *E);
115 if (
const auto *T = dyn_cast<UnaryOperator>(E))
118 const Expr *F = E->IgnoreImplicit();
126 if (
const auto *T = dyn_cast<CXXConstructExpr>(E))
129 const Expr *F = E->IgnoreImplicit();
138 return Lexer::getSourceText(
139 CharSourceRange::getTokenRange(E->getBeginLoc(), E->getEndLoc()),
140 *Result.SourceManager, Result.Context->getLangOpts());
144 const auto *CE = dyn_cast<CallExpr>(E->IgnoreImplicit());
147 const auto *ND = dyn_cast<NamedDecl>(CE->getCalleeDecl());
150 return ND->getQualifiedNameAsString() == Name;
155 BindArgument &B,
const CallExpr *CE,
156 unsigned &CaptureIndex) {
166 B.CE = CE_InitExpression;
167 B.UsageIdentifier =
"capture" + llvm::utostr(CaptureIndex++);
173 B.Kind = BK_CallExpr;
175 B.CE = CE_InitExpression;
176 B.UsageIdentifier =
"capture" + llvm::utostr(CaptureIndex++);
178 B.CaptureIdentifier = B.UsageIdentifier;
182 if (
const auto *DeclRef = dyn_cast<DeclRefExpr>(Statement)) {
183 const ValueDecl *Decl = DeclRef->getDecl();
184 if (
const auto *Var = dyn_cast_or_null<VarDecl>(Decl)) {
185 if (Var->isLocalVarDeclOrParm())
188 }
else if (isa<CXXThisExpr>(Statement))
195 BindArgument &B,
const Expr *E) {
196 if (
const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E)) {
197 if (
const auto *CE = dyn_cast<CXXConstructExpr>(BTE->getSubExpr()))
202 const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreImplicit());
206 const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
207 if (!VD || !VD->isLocalVarDeclOrParm())
212 B.CaptureIdentifier = B.UsageIdentifier;
217 BindArgument &B,
const Expr *E) {
218 if (
const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E)) {
219 if (
const auto *CE = dyn_cast<CXXConstructExpr>(BTE->getSubExpr()))
224 E = E->IgnoreImplicit();
225 if (isa<CXXThisExpr>(E)) {
229 B.CaptureIdentifier =
"this";
233 const auto *ME = dyn_cast<MemberExpr>(E);
237 if (!ME->isLValue() || !isa<FieldDecl>(ME->getMemberDecl()))
240 if (isa<CXXThisExpr>(ME->getBase())) {
244 B.CaptureIdentifier =
"this";
251static SmallVector<BindArgument, 4>
253 const CallableInfo &Callable) {
254 SmallVector<BindArgument, 4> BindArguments;
255 static llvm::Regex MatchPlaceholder(
"^_([0-9]+)$");
257 const auto *BindCall = Result.Nodes.getNodeAs<CallExpr>(
"bind");
260 unsigned CaptureIndex = 0;
261 for (
size_t I = 1, ArgCount = BindCall->getNumArgs(); I < ArgCount; ++I) {
263 const Expr *E = BindCall->getArg(I);
264 BindArgument &B = BindArguments.emplace_back();
266 size_t ArgIndex = I - 1;
267 if (Callable.Type == CT_MemberFunction)
270 bool IsObjectPtr = (I == 1 && Callable.Type == CT_MemberFunction);
274 if (!Callable.Decl || ArgIndex < Callable.Decl->getNumParams() ||
278 SmallVector<StringRef, 2> Matches;
279 const auto *DRE = dyn_cast<DeclRefExpr>(E);
280 if (MatchPlaceholder.match(B.SourceTokens, &Matches) ||
282 (DRE && MatchPlaceholder.match(DRE->getDecl()->getName(), &Matches))) {
283 B.Kind = BK_Placeholder;
284 B.PlaceHolderIndex = std::stoi(std::string(Matches[1]));
285 B.UsageIdentifier =
"PH" + llvm::utostr(B.PlaceHolderIndex);
286 B.CaptureIdentifier = B.UsageIdentifier;
304 B.CE = CE_InitExpression;
306 B.UsageIdentifier =
"ObjectPtr";
307 B.CaptureIdentifier = B.UsageIdentifier;
309 B.CE = CE_InitExpression;
311 B.CaptureIdentifier =
"capture" + llvm::utostr(CaptureIndex++);
312 B.UsageIdentifier = B.CaptureIdentifier;
315 return BindArguments;
319 size_t PlaceholderIndex) {
320 for (
size_t I = 0; I < Args.size(); ++I)
321 if (Args[I].PlaceHolderIndex == PlaceholderIndex)
328 llvm::raw_ostream &Stream,
329 bool PermissiveParameterList) {
331 ArrayRef<BindArgument> Args = LP.BindArguments;
333 const auto *MaxPlaceholderIt = llvm::max_element(
334 Args, [](
const BindArgument &B1,
const BindArgument &B2) {
335 return B1.PlaceHolderIndex < B2.PlaceHolderIndex;
339 if (!PermissiveParameterList && (MaxPlaceholderIt == Args.end() ||
340 MaxPlaceholderIt->PlaceHolderIndex == 0))
343 size_t PlaceholderCount = MaxPlaceholderIt->PlaceHolderIndex;
345 StringRef Delimiter =
"";
346 for (
size_t I = 1; I <= PlaceholderCount; ++I) {
347 Stream << Delimiter <<
"auto &&";
351 if (ArgIndex != -1 && Args[ArgIndex].IsUsed)
352 Stream <<
" " << Args[ArgIndex].UsageIdentifier;
355 if (PermissiveParameterList)
356 Stream << Delimiter <<
"auto && ...";
361 llvm::raw_ostream &Stream) {
362 StringRef Delimiter =
"";
364 for (
const BindArgument &B : Args) {
367 if (B.Kind == BK_Placeholder) {
368 Stream <<
"std::forward<decltype(" << B.UsageIdentifier <<
")>";
369 Stream <<
"(" << B.UsageIdentifier <<
")";
370 }
else if (B.CM != CM_None)
371 Stream << B.UsageIdentifier;
373 Stream << B.SourceTokens;
380 llvm::SmallSet<size_t, 4> PlaceHolderIndices;
381 for (
const BindArgument &B : Args) {
382 if (B.PlaceHolderIndex) {
383 if (!PlaceHolderIndices.insert(B.PlaceHolderIndex).second)
390static std::vector<const FunctionDecl *>
392 std::vector<const FunctionDecl *> Candidates;
394 for (
const clang::CXXMethodDecl *Method : RecordDecl->methods()) {
395 OverloadedOperatorKind OOK = Method->getOverloadedOperator();
397 if (OOK != OverloadedOperatorKind::OO_Call)
400 if (Method->getNumParams() > NumArgs)
403 Candidates.push_back(Method);
407 for (
const clang::Decl *D : RecordDecl->decls()) {
408 const auto *FTD = dyn_cast<FunctionTemplateDecl>(D);
411 const FunctionDecl *FD = FTD->getTemplatedDecl();
413 OverloadedOperatorKind OOK = FD->getOverloadedOperator();
414 if (OOK != OverloadedOperatorKind::OO_Call)
417 if (FD->getNumParams() > NumArgs)
420 Candidates.push_back(FD);
427 ArrayRef<BindArgument> Args) {
432 if (any_of(Args, [](
const BindArgument &B) {
449 if (Callee.Type == CT_Other || Callee.Materialization == CMK_Other)
457 std::vector<const FunctionDecl *> Candidates =
459 if (Candidates.size() != 1)
462 return Candidates.front();
465static const FunctionDecl *
467 CallableMaterializationKind Materialization) {
469 const Expr *Callee = Result.Nodes.getNodeAs<Expr>(
"ref");
472 if (Type == CT_Object) {
473 const auto *BindCall = Result.Nodes.getNodeAs<CallExpr>(
"bind");
474 size_t NumArgs = BindCall->getNumArgs() - 1;
475 return getCallOperator(Callee->getType()->getAsCXXRecordDecl(), NumArgs);
478 if (Materialization == CMK_Function) {
479 if (
const auto *DRE = dyn_cast<DeclRefExpr>(CallExpression))
480 return dyn_cast<FunctionDecl>(DRE->getDecl());
489 const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>(
"ref");
491 QualType QT = CallableExpr->getType();
492 if (QT->isMemberFunctionPointerType())
493 return CT_MemberFunction;
495 if (QT->isFunctionPointerType() || QT->isFunctionReferenceType() ||
496 QT->isFunctionType())
499 if (QT->isRecordType()) {
500 const CXXRecordDecl *Decl = QT->getAsCXXRecordDecl();
510static CallableMaterializationKind
512 const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>(
"ref");
516 const auto *CE = dyn_cast<CXXConstructExpr>(NoTemporaries);
517 const auto *FC = dyn_cast<CXXFunctionalCastExpr>(NoTemporaries);
518 if ((isa<CallExpr>(NoTemporaries)) || (CE && (CE->getNumArgs() > 0)) ||
519 (FC && (FC->getCastKind() == CK_ConstructorConversion)))
522 return CMK_CallExpression;
524 if (isa<CXXFunctionalCastExpr>(NoTemporaries) || CE)
527 if (
const auto *DRE = dyn_cast<DeclRefExpr>(NoTemporaries)) {
528 if (isa<FunctionDecl>(DRE->getDecl()))
530 if (isa<VarDecl>(DRE->getDecl()))
531 return CMK_VariableRef;
537static LambdaProperties
539 const auto *CalleeExpr = Result.Nodes.getNodeAs<Expr>(
"ref");
543 const auto *
Bind = Result.Nodes.getNodeAs<CallExpr>(
"bind");
544 const auto *Decl = cast<FunctionDecl>(
Bind->getCalleeDecl());
545 const auto *
NS = cast<NamespaceDecl>(Decl->getEnclosingNamespaceContext());
546 while (
NS->isInlineNamespace())
547 NS = cast<NamespaceDecl>(
NS->getDeclContext());
548 LP.BindNamespace =
NS->getName();
554 if (LP.Callable.Decl)
555 if (
const Type *ReturnType =
556 LP.Callable.Decl->getReturnType().getCanonicalType().getTypePtr())
557 LP.Callable.DoesReturn = !ReturnType->isVoidType();
559 if (LP.Callable.Materialization == CMK_VariableRef) {
560 LP.Callable.CE = CE_Var;
561 LP.Callable.CM = CM_ByValue;
562 LP.Callable.UsageIdentifier =
564 LP.Callable.CaptureIdentifier = std::string(
566 }
else if (LP.Callable.Materialization == CMK_CallExpression) {
567 LP.Callable.CE = CE_InitExpression;
568 LP.Callable.CM = CM_ByValue;
569 LP.Callable.UsageIdentifier =
"Func";
570 LP.Callable.CaptureIdentifier =
"Func";
581static bool emitCapture(llvm::StringSet<> &CaptureSet, StringRef Delimiter,
582 CaptureMode CM, CaptureExpr CE, StringRef Identifier,
583 StringRef InitExpression, raw_ostream &Stream) {
588 if (CaptureSet.contains(Identifier))
595 Stream << Identifier;
596 if (CE == CE_InitExpression)
597 Stream <<
" = " << InitExpression;
599 CaptureSet.insert(Identifier);
604 const MatchFinder::MatchResult &Result,
605 raw_ostream &Stream) {
606 llvm::StringSet<> CaptureSet;
607 bool AnyCapturesEmitted =
false;
610 CaptureSet,
"", LP.Callable.CM, LP.Callable.CE,
611 LP.Callable.CaptureIdentifier, LP.Callable.CaptureInitializer, Stream);
613 for (
const BindArgument &B : LP.BindArguments) {
614 if (B.CM == CM_None || !B.IsUsed)
617 StringRef Delimiter = AnyCapturesEmitted ?
", " :
"";
619 if (
emitCapture(CaptureSet, Delimiter, B.CM, B.CE, B.CaptureIdentifier,
620 B.SourceTokens, Stream))
621 AnyCapturesEmitted =
true;
625static ArrayRef<BindArgument>
627 ArrayRef<BindArgument> Args = ArrayRef(
P.BindArguments);
628 if (
P.Callable.Type != CT_MemberFunction)
631 return Args.drop_front();
635 PermissiveParameterList(Options.get(
"PermissiveParameterList", false)) {}
638 Options.store(Opts,
"PermissiveParameterList", PermissiveParameterList);
644 callee(namedDecl(hasAnyName(
"::boost::bind",
"::std::bind"))),
646 0, anyOf(expr(hasType(memberPointerType())).bind(
"ref"),
647 expr(hasParent(materializeTemporaryExpr().bind(
"ref"))),
648 expr().bind(
"ref"))))
654 const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>(
"bind");
658 diag(MatchedDecl->getBeginLoc(),
659 formatv(
"prefer a lambda to {0}::bind", LP.BindNamespace).str());
660 if (!LP.IsFixitSupported)
663 const auto *Ref = Result.Nodes.getNodeAs<Expr>(
"ref");
666 llvm::raw_string_ostream Stream(Buffer);
672 ArrayRef<BindArgument> FunctionCallArgs = ArrayRef(LP.BindArguments);
678 if (LP.Callable.DoesReturn) {
682 if (LP.Callable.Type == CT_Function) {
683 StringRef SourceTokens = LP.Callable.SourceTokens;
684 SourceTokens.consume_front(
"&");
685 Stream << SourceTokens;
686 }
else if (LP.Callable.Type == CT_MemberFunction) {
687 const auto *MethodDecl = dyn_cast<CXXMethodDecl>(LP.Callable.Decl);
688 const BindArgument &ObjPtr = FunctionCallArgs.front();
690 if (MethodDecl->getOverloadedOperator() == OO_Call) {
691 Stream <<
"(*" << ObjPtr.UsageIdentifier <<
')';
694 Stream << ObjPtr.UsageIdentifier;
697 Stream << MethodDecl->getNameAsString();
700 switch (LP.Callable.CE) {
702 if (LP.Callable.UsageIdentifier != LP.Callable.CaptureIdentifier) {
703 Stream <<
"(" << LP.Callable.UsageIdentifier <<
")";
707 case CE_InitExpression:
708 Stream << LP.Callable.UsageIdentifier;
720 Diag << FixItHint::CreateReplacement(MatchedDecl->getSourceRange(),
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
AvoidBindCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
static bool tryCaptureAsMemberVariable(const MatchFinder::MatchResult &Result, BindArgument &B, const Expr *E)
static void initializeBindArgumentForCallExpr(const MatchFinder::MatchResult &Result, BindArgument &B, const CallExpr *CE, unsigned &CaptureIndex)
static constexpr char Bind[]
static bool emitCapture(llvm::StringSet<> &CaptureSet, StringRef Delimiter, CaptureMode CM, CaptureExpr CE, StringRef Identifier, StringRef InitExpression, raw_ostream &Stream)
static bool tryCaptureAsLocalVariable(const MatchFinder::MatchResult &Result, BindArgument &B, const Expr *E)
static bool anyDescendantIsLocal(const Stmt *Statement)
static int findPositionOfPlaceholderUse(ArrayRef< BindArgument > Args, size_t PlaceholderIndex)
static void addPlaceholderArgs(const LambdaProperties &LP, llvm::raw_ostream &Stream, bool PermissiveParameterList)
static SmallVector< BindArgument, 4 > buildBindArguments(const MatchFinder::MatchResult &Result, const CallableInfo &Callable)
static StringRef getSourceTextForExpr(const MatchFinder::MatchResult &Result, const Expr *E)
static const FunctionDecl * getCallOperator(const CXXRecordDecl *Callable, size_t NumArgs)
static CallableType getCallableType(const MatchFinder::MatchResult &Result)
static const FunctionDecl * getCallMethodDecl(const MatchFinder::MatchResult &Result, CallableType Type, CallableMaterializationKind Materialization)
static void emitCaptureList(const LambdaProperties &LP, const MatchFinder::MatchResult &Result, raw_ostream &Stream)
static void addFunctionCallArgs(ArrayRef< BindArgument > Args, llvm::raw_ostream &Stream)
static bool isPlaceHolderIndexRepeated(const ArrayRef< BindArgument > Args)
static const Expr * ignoreTemporariesAndPointers(const Expr *E)
static bool isCallExprNamed(const Expr *E, StringRef Name)
static const Expr * ignoreTemporariesAndConstructors(const Expr *E)
static LambdaProperties getLambdaProperties(const MatchFinder::MatchResult &Result)
static ArrayRef< BindArgument > getForwardedArgumentList(const LambdaProperties &P)
static std::vector< const FunctionDecl * > findCandidateCallOperators(const CXXRecordDecl *RecordDecl, size_t NumArgs)
static CallableMaterializationKind getCallableMaterialization(const MatchFinder::MatchResult &Result)
static bool isFixitSupported(const CallableInfo &Callee, ArrayRef< BindArgument > Args)
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccess P
llvm::StringMap< ClangTidyValue > OptionMap