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 const 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) {
262 const Expr *E = BindCall->getArg(I);
263 BindArgument &B = BindArguments.emplace_back();
265 size_t ArgIndex = I - 1;
266 if (Callable.Type == CT_MemberFunction)
269 const bool IsObjectPtr = (I == 1 && Callable.Type == CT_MemberFunction);
273 if (!Callable.Decl || ArgIndex < Callable.Decl->getNumParams() ||
277 SmallVector<StringRef, 2> Matches;
278 const auto *DRE = dyn_cast<DeclRefExpr>(E);
279 if (MatchPlaceholder.match(B.SourceTokens, &Matches) ||
281 (DRE && MatchPlaceholder.match(DRE->getDecl()->getName(), &Matches))) {
282 B.Kind = BK_Placeholder;
283 B.PlaceHolderIndex = std::stoi(std::string(Matches[1]));
284 B.UsageIdentifier =
"PH" + llvm::utostr(B.PlaceHolderIndex);
285 B.CaptureIdentifier = B.UsageIdentifier;
303 B.CE = CE_InitExpression;
305 B.UsageIdentifier =
"ObjectPtr";
306 B.CaptureIdentifier = B.UsageIdentifier;
308 B.CE = CE_InitExpression;
310 B.CaptureIdentifier =
"capture" + llvm::utostr(CaptureIndex++);
311 B.UsageIdentifier = B.CaptureIdentifier;
314 return BindArguments;
318 size_t PlaceholderIndex) {
319 for (
size_t I = 0; I < Args.size(); ++I)
320 if (Args[I].PlaceHolderIndex == PlaceholderIndex)
327 llvm::raw_ostream &Stream,
328 bool PermissiveParameterList) {
329 ArrayRef<BindArgument> Args = LP.BindArguments;
331 const auto *MaxPlaceholderIt = llvm::max_element(
332 Args, [](
const BindArgument &B1,
const BindArgument &B2) {
333 return B1.PlaceHolderIndex < B2.PlaceHolderIndex;
337 if (!PermissiveParameterList && (MaxPlaceholderIt == Args.end() ||
338 MaxPlaceholderIt->PlaceHolderIndex == 0))
341 const size_t PlaceholderCount = MaxPlaceholderIt->PlaceHolderIndex;
343 StringRef Delimiter =
"";
344 for (
size_t I = 1; I <= PlaceholderCount; ++I) {
345 Stream << Delimiter <<
"auto &&";
349 if (ArgIndex != -1 && Args[ArgIndex].IsUsed)
350 Stream <<
" " << Args[ArgIndex].UsageIdentifier;
353 if (PermissiveParameterList)
354 Stream << Delimiter <<
"auto && ...";
359 llvm::raw_ostream &Stream) {
360 StringRef Delimiter =
"";
362 for (
const BindArgument &B : Args) {
365 if (B.Kind == BK_Placeholder) {
366 Stream <<
"std::forward<decltype(" << B.UsageIdentifier <<
")>";
367 Stream <<
"(" << B.UsageIdentifier <<
")";
368 }
else if (B.CM != CM_None)
369 Stream << B.UsageIdentifier;
371 Stream << B.SourceTokens;
378 llvm::SmallSet<size_t, 4> PlaceHolderIndices;
379 for (
const BindArgument &B : Args) {
380 if (B.PlaceHolderIndex) {
381 if (!PlaceHolderIndices.insert(B.PlaceHolderIndex).second)
388static std::vector<const FunctionDecl *>
390 std::vector<const FunctionDecl *> Candidates;
392 for (
const clang::CXXMethodDecl *Method : RecordDecl->methods()) {
393 const OverloadedOperatorKind OOK = Method->getOverloadedOperator();
395 if (OOK != OverloadedOperatorKind::OO_Call)
398 if (Method->getNumParams() > NumArgs)
401 Candidates.push_back(Method);
405 for (
const clang::Decl *D : RecordDecl->decls()) {
406 const auto *FTD = dyn_cast<FunctionTemplateDecl>(D);
409 const FunctionDecl *FD = FTD->getTemplatedDecl();
411 const OverloadedOperatorKind OOK = FD->getOverloadedOperator();
412 if (OOK != OverloadedOperatorKind::OO_Call)
415 if (FD->getNumParams() > NumArgs)
418 Candidates.push_back(FD);
425 ArrayRef<BindArgument> Args) {
430 if (any_of(Args, [](
const BindArgument &B) {
447 if (Callee.Type == CT_Other || Callee.Materialization == CMK_Other)
455 std::vector<const FunctionDecl *> Candidates =
457 if (Candidates.size() != 1)
460 return Candidates.front();
463static const FunctionDecl *
465 CallableMaterializationKind Materialization) {
466 const Expr *Callee = Result.Nodes.getNodeAs<Expr>(
"ref");
469 if (Type == CT_Object) {
470 const auto *BindCall = Result.Nodes.getNodeAs<CallExpr>(
"bind");
471 const size_t NumArgs = BindCall->getNumArgs() - 1;
472 return getCallOperator(Callee->getType()->getAsCXXRecordDecl(), NumArgs);
475 if (Materialization == CMK_Function) {
476 if (
const auto *DRE = dyn_cast<DeclRefExpr>(CallExpression))
477 return dyn_cast<FunctionDecl>(DRE->getDecl());
486 const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>(
"ref");
488 const QualType QT = CallableExpr->getType();
489 if (QT->isMemberFunctionPointerType())
490 return CT_MemberFunction;
492 if (QT->isFunctionPointerType() || QT->isFunctionReferenceType() ||
493 QT->isFunctionType())
496 if (QT->isRecordType()) {
497 const CXXRecordDecl *Decl = QT->getAsCXXRecordDecl();
507static CallableMaterializationKind
509 const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>(
"ref");
513 const auto *CE = dyn_cast<CXXConstructExpr>(NoTemporaries);
514 const auto *FC = dyn_cast<CXXFunctionalCastExpr>(NoTemporaries);
515 if ((isa<CallExpr>(NoTemporaries)) || (CE && (CE->getNumArgs() > 0)) ||
516 (FC && (FC->getCastKind() == CK_ConstructorConversion)))
519 return CMK_CallExpression;
521 if (isa<CXXFunctionalCastExpr>(NoTemporaries) || CE)
524 if (
const auto *DRE = dyn_cast<DeclRefExpr>(NoTemporaries)) {
525 if (isa<FunctionDecl>(DRE->getDecl()))
527 if (isa<VarDecl>(DRE->getDecl()))
528 return CMK_VariableRef;
534static LambdaProperties
536 const auto *CalleeExpr = Result.Nodes.getNodeAs<Expr>(
"ref");
540 const auto *
Bind = Result.Nodes.getNodeAs<CallExpr>(
"bind");
541 const auto *Decl = cast<FunctionDecl>(
Bind->getCalleeDecl());
542 const auto *
NS = cast<NamespaceDecl>(Decl->getEnclosingNamespaceContext());
543 while (
NS->isInlineNamespace())
544 NS = cast<NamespaceDecl>(
NS->getDeclContext());
545 LP.BindNamespace =
NS->getName();
551 if (LP.Callable.Decl)
552 if (
const Type *ReturnType =
553 LP.Callable.Decl->getReturnType().getCanonicalType().getTypePtr())
554 LP.Callable.DoesReturn = !ReturnType->isVoidType();
556 if (LP.Callable.Materialization == CMK_VariableRef) {
557 LP.Callable.CE = CE_Var;
558 LP.Callable.CM = CM_ByValue;
559 LP.Callable.UsageIdentifier =
561 LP.Callable.CaptureIdentifier = std::string(
563 }
else if (LP.Callable.Materialization == CMK_CallExpression) {
564 LP.Callable.CE = CE_InitExpression;
565 LP.Callable.CM = CM_ByValue;
566 LP.Callable.UsageIdentifier =
"Func";
567 LP.Callable.CaptureIdentifier =
"Func";
578static bool emitCapture(llvm::StringSet<> &CaptureSet, StringRef Delimiter,
579 CaptureMode CM, CaptureExpr CE, StringRef Identifier,
580 StringRef InitExpression, raw_ostream &Stream) {
585 if (CaptureSet.contains(Identifier))
592 Stream << Identifier;
593 if (CE == CE_InitExpression)
594 Stream <<
" = " << InitExpression;
596 CaptureSet.insert(Identifier);
601 const MatchFinder::MatchResult &Result,
602 raw_ostream &Stream) {
603 llvm::StringSet<> CaptureSet;
604 bool AnyCapturesEmitted =
false;
607 CaptureSet,
"", LP.Callable.CM, LP.Callable.CE,
608 LP.Callable.CaptureIdentifier, LP.Callable.CaptureInitializer, Stream);
610 for (
const BindArgument &B : LP.BindArguments) {
611 if (B.CM == CM_None || !B.IsUsed)
614 const StringRef Delimiter = AnyCapturesEmitted ?
", " :
"";
616 if (
emitCapture(CaptureSet, Delimiter, B.CM, B.CE, B.CaptureIdentifier,
617 B.SourceTokens, Stream))
618 AnyCapturesEmitted =
true;
622static ArrayRef<BindArgument>
624 ArrayRef<BindArgument> Args = ArrayRef(
P.BindArguments);
625 if (
P.Callable.Type != CT_MemberFunction)
628 return Args.drop_front();
632 PermissiveParameterList(Options.get(
"PermissiveParameterList", false)) {}
635 Options.store(Opts,
"PermissiveParameterList", PermissiveParameterList);
641 callee(namedDecl(hasAnyName(
"::boost::bind",
"::std::bind"))),
643 0, anyOf(expr(hasType(memberPointerType())).bind(
"ref"),
644 expr(hasParent(materializeTemporaryExpr().bind(
"ref"))),
645 expr().bind(
"ref"))))
651 const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>(
"bind");
655 diag(MatchedDecl->getBeginLoc(),
656 formatv(
"prefer a lambda to {0}::bind", LP.BindNamespace).str());
657 if (!LP.IsFixitSupported)
660 const auto *Ref = Result.Nodes.getNodeAs<Expr>(
"ref");
663 llvm::raw_string_ostream Stream(Buffer);
669 const ArrayRef<BindArgument> FunctionCallArgs = ArrayRef(LP.BindArguments);
675 if (LP.Callable.DoesReturn) {
679 if (LP.Callable.Type == CT_Function) {
680 StringRef SourceTokens = LP.Callable.SourceTokens;
681 SourceTokens.consume_front(
"&");
682 Stream << SourceTokens;
683 }
else if (LP.Callable.Type == CT_MemberFunction) {
684 const auto *MethodDecl = dyn_cast<CXXMethodDecl>(LP.Callable.Decl);
685 const BindArgument &ObjPtr = FunctionCallArgs.front();
687 if (MethodDecl->getOverloadedOperator() == OO_Call) {
688 Stream <<
"(*" << ObjPtr.UsageIdentifier <<
')';
691 Stream << ObjPtr.UsageIdentifier;
694 Stream << MethodDecl->getNameAsString();
697 switch (LP.Callable.CE) {
699 if (LP.Callable.UsageIdentifier != LP.Callable.CaptureIdentifier) {
700 Stream <<
"(" << LP.Callable.UsageIdentifier <<
")";
704 case CE_InitExpression:
705 Stream << LP.Callable.UsageIdentifier;
717 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::ProBoundsAvoidUncheckedContainerAccessCheck P
llvm::StringMap< ClangTidyValue > OptionMap