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)) {
196 BindArgument &B,
const Expr *E) {
197 if (
const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E)) {
198 if (
const auto *CE = dyn_cast<CXXConstructExpr>(BTE->getSubExpr()))
203 const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreImplicit());
207 const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
208 if (!VD || !VD->isLocalVarDeclOrParm())
213 B.CaptureIdentifier = B.UsageIdentifier;
218 BindArgument &B,
const Expr *E) {
219 if (
const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E)) {
220 if (
const auto *CE = dyn_cast<CXXConstructExpr>(BTE->getSubExpr()))
225 E = E->IgnoreImplicit();
226 if (isa<CXXThisExpr>(E)) {
230 B.CaptureIdentifier =
"this";
234 const auto *ME = dyn_cast<MemberExpr>(E);
238 if (!ME->isLValue() || !isa<FieldDecl>(ME->getMemberDecl()))
241 if (isa<CXXThisExpr>(ME->getBase())) {
245 B.CaptureIdentifier =
"this";
252static SmallVector<BindArgument, 4>
254 const CallableInfo &Callable) {
255 SmallVector<BindArgument, 4> BindArguments;
256 static const llvm::Regex MatchPlaceholder(
"^_([0-9]+)$");
258 const auto *BindCall = Result.Nodes.getNodeAs<CallExpr>(
"bind");
261 unsigned CaptureIndex = 0;
262 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 const 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) {
330 ArrayRef<BindArgument> Args = LP.BindArguments;
332 const auto *MaxPlaceholderIt = llvm::max_element(
333 Args, [](
const BindArgument &B1,
const BindArgument &B2) {
334 return B1.PlaceHolderIndex < B2.PlaceHolderIndex;
338 if (!PermissiveParameterList && (MaxPlaceholderIt == Args.end() ||
339 MaxPlaceholderIt->PlaceHolderIndex == 0))
342 const size_t PlaceholderCount = MaxPlaceholderIt->PlaceHolderIndex;
344 StringRef Delimiter =
"";
345 for (
size_t I = 1; I <= PlaceholderCount; ++I) {
346 Stream << Delimiter <<
"auto &&";
350 if (ArgIndex != -1 && Args[ArgIndex].IsUsed)
351 Stream <<
" " << Args[ArgIndex].UsageIdentifier;
354 if (PermissiveParameterList)
355 Stream << Delimiter <<
"auto && ...";
360 llvm::raw_ostream &Stream) {
361 StringRef Delimiter =
"";
363 for (
const BindArgument &B : Args) {
366 if (B.Kind == BK_Placeholder) {
367 Stream <<
"std::forward<decltype(" << B.UsageIdentifier <<
")>";
368 Stream <<
"(" << B.UsageIdentifier <<
")";
369 }
else if (B.CM != CM_None) {
370 Stream << B.UsageIdentifier;
372 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 const 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 const 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) {
468 const Expr *Callee = Result.Nodes.getNodeAs<Expr>(
"ref");
471 if (Type == CT_Object) {
472 const auto *BindCall = Result.Nodes.getNodeAs<CallExpr>(
"bind");
473 const size_t NumArgs = BindCall->getNumArgs() - 1;
474 return getCallOperator(Callee->getType()->getAsCXXRecordDecl(), NumArgs);
477 if (Materialization == CMK_Function) {
478 if (
const auto *DRE = dyn_cast<DeclRefExpr>(CallExpression))
479 return dyn_cast<FunctionDecl>(DRE->getDecl());
488 const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>(
"ref");
490 const QualType QT = CallableExpr->getType();
491 if (QT->isMemberFunctionPointerType())
492 return CT_MemberFunction;
494 if (QT->isFunctionPointerType() || QT->isFunctionReferenceType() ||
495 QT->isFunctionType())
498 if (QT->isRecordType()) {
499 const CXXRecordDecl *Decl = QT->getAsCXXRecordDecl();
509static CallableMaterializationKind
511 const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>(
"ref");
515 const auto *CE = dyn_cast<CXXConstructExpr>(NoTemporaries);
516 const auto *FC = dyn_cast<CXXFunctionalCastExpr>(NoTemporaries);
517 if ((isa<CallExpr>(NoTemporaries)) || (CE && (CE->getNumArgs() > 0)) ||
518 (FC && (FC->getCastKind() == CK_ConstructorConversion)))
521 return CMK_CallExpression;
523 if (isa<CXXFunctionalCastExpr>(NoTemporaries) || CE)
526 if (
const auto *DRE = dyn_cast<DeclRefExpr>(NoTemporaries)) {
527 if (isa<FunctionDecl>(DRE->getDecl()))
529 if (isa<VarDecl>(DRE->getDecl()))
530 return CMK_VariableRef;
536static LambdaProperties
538 const auto *CalleeExpr = Result.Nodes.getNodeAs<Expr>(
"ref");
542 const auto *
Bind = Result.Nodes.getNodeAs<CallExpr>(
"bind");
543 const auto *Decl = cast<FunctionDecl>(
Bind->getCalleeDecl());
544 const auto *
NS = cast<NamespaceDecl>(Decl->getEnclosingNamespaceContext());
545 while (
NS->isInlineNamespace())
546 NS = cast<NamespaceDecl>(
NS->getDeclContext());
547 LP.BindNamespace =
NS->getName();
553 if (LP.Callable.Decl)
554 if (
const Type *ReturnType =
555 LP.Callable.Decl->getReturnType().getCanonicalType().getTypePtr())
556 LP.Callable.DoesReturn = !ReturnType->isVoidType();
558 if (LP.Callable.Materialization == CMK_VariableRef) {
559 LP.Callable.CE = CE_Var;
560 LP.Callable.CM = CM_ByValue;
561 LP.Callable.UsageIdentifier =
563 LP.Callable.CaptureIdentifier = std::string(
565 }
else if (LP.Callable.Materialization == CMK_CallExpression) {
566 LP.Callable.CE = CE_InitExpression;
567 LP.Callable.CM = CM_ByValue;
568 LP.Callable.UsageIdentifier =
"Func";
569 LP.Callable.CaptureIdentifier =
"Func";
580static bool emitCapture(llvm::StringSet<> &CaptureSet, StringRef Delimiter,
581 CaptureMode CM, CaptureExpr CE, StringRef Identifier,
582 StringRef InitExpression, raw_ostream &Stream) {
587 if (CaptureSet.contains(Identifier))
594 Stream << Identifier;
595 if (CE == CE_InitExpression)
596 Stream <<
" = " << InitExpression;
598 CaptureSet.insert(Identifier);
603 const MatchFinder::MatchResult &Result,
604 raw_ostream &Stream) {
605 llvm::StringSet<> CaptureSet;
606 bool AnyCapturesEmitted =
false;
609 CaptureSet,
"", LP.Callable.CM, LP.Callable.CE,
610 LP.Callable.CaptureIdentifier, LP.Callable.CaptureInitializer, Stream);
612 for (
const BindArgument &B : LP.BindArguments) {
613 if (B.CM == CM_None || !B.IsUsed)
616 const StringRef Delimiter = AnyCapturesEmitted ?
", " :
"";
618 if (
emitCapture(CaptureSet, Delimiter, B.CM, B.CE, B.CaptureIdentifier,
619 B.SourceTokens, Stream))
620 AnyCapturesEmitted =
true;
624static ArrayRef<BindArgument>
626 ArrayRef<BindArgument> Args = ArrayRef(
P.BindArguments);
627 if (
P.Callable.Type != CT_MemberFunction)
630 return Args.drop_front();
634 PermissiveParameterList(Options.get(
"PermissiveParameterList", false)) {}
637 Options.store(Opts,
"PermissiveParameterList", PermissiveParameterList);
643 callee(namedDecl(hasAnyName(
"::boost::bind",
"::std::bind"))),
645 0, anyOf(expr(hasType(memberPointerType())).bind(
"ref"),
646 expr(hasParent(materializeTemporaryExpr().bind(
"ref"))),
647 expr().bind(
"ref"))))
653 const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>(
"bind");
657 diag(MatchedDecl->getBeginLoc(),
658 formatv(
"prefer a lambda to {0}::bind", LP.BindNamespace).str());
659 if (!LP.IsFixitSupported)
662 const auto *Ref = Result.Nodes.getNodeAs<Expr>(
"ref");
665 llvm::raw_string_ostream Stream(Buffer);
671 const ArrayRef<BindArgument> FunctionCallArgs = ArrayRef(LP.BindArguments);
677 if (LP.Callable.DoesReturn)
680 if (LP.Callable.Type == CT_Function) {
681 StringRef SourceTokens = LP.Callable.SourceTokens;
682 SourceTokens.consume_front(
"&");
683 Stream << 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();
688 if (MethodDecl->getOverloadedOperator() == OO_Call) {
689 Stream <<
"(*" << ObjPtr.UsageIdentifier <<
')';
692 Stream << ObjPtr.UsageIdentifier;
695 Stream << MethodDecl->getNameAsString();
698 switch (LP.Callable.CE) {
700 if (LP.Callable.UsageIdentifier != LP.Callable.CaptureIdentifier) {
701 Stream <<
"(" << LP.Callable.UsageIdentifier <<
")";
705 case CE_InitExpression:
706 Stream << LP.Callable.UsageIdentifier;
718 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