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"
36enum BindArgumentKind { BK_Temporary, BK_Placeholder, BK_CallExpr, BK_Other };
37enum CaptureMode { CM_None, CM_ByRef, CM_ByValue };
38enum CaptureExpr { CE_None, CE_Var, CE_InitExpression };
47enum CallableMaterializationKind {
57 BindArgumentKind
Kind = BK_Other;
61 CaptureMode
CM = CM_None;
65 CaptureExpr
CE = CE_None;
86 const Expr *
E =
nullptr;
90 CallableType
Type = CT_Other;
92 CaptureMode
CM = CM_None;
93 CaptureExpr
CE = CE_None;
98 const FunctionDecl *
Decl =
nullptr;
101struct LambdaProperties {
111 BindArgument &B,
const Expr *
E);
114 BindArgument &B,
const Expr *
E);
117 if (
const auto *T = dyn_cast<UnaryOperator>(
E))
120 const Expr *F =
E->IgnoreImplicit();
128 if (
const auto *T = dyn_cast<CXXConstructExpr>(
E))
131 const Expr *F =
E->IgnoreImplicit();
140 return Lexer::getSourceText(
141 CharSourceRange::getTokenRange(
E->getBeginLoc(),
E->getEndLoc()),
142 *Result.SourceManager, Result.Context->getLangOpts());
146 const auto *
CE = dyn_cast<CallExpr>(
E->IgnoreImplicit());
149 const auto *ND = dyn_cast<NamedDecl>(
CE->getCalleeDecl());
152 return ND->getQualifiedNameAsString() ==
Name;
157 BindArgument &B,
const CallExpr *
CE,
158 unsigned &CaptureIndex) {
168 B.CE = CE_InitExpression;
169 B.UsageIdentifier =
"capture" + llvm::utostr(CaptureIndex++);
175 B.Kind = BK_CallExpr;
177 B.CE = CE_InitExpression;
178 B.UsageIdentifier =
"capture" + llvm::utostr(CaptureIndex++);
180 B.CaptureIdentifier = B.UsageIdentifier;
184 if (
const auto *
DeclRef = dyn_cast<DeclRefExpr>(Statement)) {
186 if (
const auto *Var = dyn_cast_or_null<VarDecl>(
Decl)) {
187 if (Var->isLocalVarDeclOrParm())
190 }
else if (isa<CXXThisExpr>(Statement))
197 BindArgument &B,
const Expr *
E) {
198 if (
const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(
E)) {
199 if (
const auto *
CE = dyn_cast<CXXConstructExpr>(BTE->getSubExpr()))
204 const auto *DRE = dyn_cast<DeclRefExpr>(
E->IgnoreImplicit());
208 const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
209 if (!VD || !VD->isLocalVarDeclOrParm())
214 B.CaptureIdentifier = B.UsageIdentifier;
219 BindArgument &B,
const Expr *
E) {
220 if (
const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(
E)) {
221 if (
const auto *
CE = dyn_cast<CXXConstructExpr>(BTE->getSubExpr()))
226 E =
E->IgnoreImplicit();
227 if (isa<CXXThisExpr>(
E)) {
231 B.CaptureIdentifier =
"this";
235 const auto *ME = dyn_cast<MemberExpr>(
E);
239 if (!ME->isLValue() || !isa<FieldDecl>(ME->getMemberDecl()))
242 if (isa<CXXThisExpr>(ME->getBase())) {
246 B.CaptureIdentifier =
"this";
253static SmallVector<BindArgument, 4>
257 static llvm::Regex MatchPlaceholder(
"^_([0-9]+)$");
259 const auto *BindCall = Result.Nodes.getNodeAs<CallExpr>(
"bind");
262 unsigned CaptureIndex = 0;
263 for (
size_t I = 1, ArgCount = BindCall->getNumArgs(); I < ArgCount; ++I) {
265 const Expr *
E = BindCall->getArg(I);
268 size_t ArgIndex = I - 1;
269 if (
Callable.Type == CT_MemberFunction)
272 bool IsObjectPtr = (I == 1 &&
Callable.Type == CT_MemberFunction);
276 if (!
Callable.Decl || ArgIndex < Callable.Decl->getNumParams() ||
280 SmallVector<StringRef, 2> Matches;
281 const auto *DRE = dyn_cast<DeclRefExpr>(
E);
282 if (MatchPlaceholder.match(B.SourceTokens, &Matches) ||
284 (DRE && MatchPlaceholder.match(DRE->getDecl()->getName(), &Matches))) {
285 B.Kind = BK_Placeholder;
286 B.PlaceHolderIndex = std::stoi(std::string(Matches[1]));
287 B.UsageIdentifier =
"PH" + llvm::utostr(B.PlaceHolderIndex);
288 B.CaptureIdentifier = B.UsageIdentifier;
306 B.CE = CE_InitExpression;
308 B.UsageIdentifier =
"ObjectPtr";
309 B.CaptureIdentifier = B.UsageIdentifier;
311 B.CE = CE_InitExpression;
313 B.CaptureIdentifier =
"capture" + llvm::utostr(CaptureIndex++);
314 B.UsageIdentifier = B.CaptureIdentifier;
321 size_t PlaceholderIndex) {
322 for (
size_t I = 0; I <
Args.size(); ++I)
330 llvm::raw_ostream &Stream,
331 bool PermissiveParameterList) {
333 ArrayRef<BindArgument>
Args = LP.BindArguments;
335 const auto *MaxPlaceholderIt =
336 std::max_element(
Args.begin(),
Args.end(),
337 [](
const BindArgument &B1,
const BindArgument &B2) {
338 return B1.PlaceHolderIndex < B2.PlaceHolderIndex;
342 if (!PermissiveParameterList && (MaxPlaceholderIt ==
Args.end() ||
343 MaxPlaceholderIt->PlaceHolderIndex == 0))
346 size_t PlaceholderCount = MaxPlaceholderIt->PlaceHolderIndex;
348 StringRef Delimiter =
"";
349 for (
size_t I = 1; I <= PlaceholderCount; ++I) {
350 Stream << Delimiter <<
"auto &&";
355 Stream <<
" " <<
Args[ArgIndex].UsageIdentifier;
358 if (PermissiveParameterList)
359 Stream << Delimiter <<
"auto && ...";
364 llvm::raw_ostream &Stream) {
365 StringRef Delimiter =
"";
367 for (
int I = 0, Size =
Args.size(); I < Size; ++I) {
368 const BindArgument &B =
Args[I];
372 if (B.Kind == BK_Placeholder) {
373 Stream <<
"std::forward<decltype(" << B.UsageIdentifier <<
")>";
374 Stream <<
"(" << B.UsageIdentifier <<
")";
375 }
else if (B.CM != CM_None)
376 Stream << B.UsageIdentifier;
378 Stream << B.SourceTokens;
385 llvm::SmallSet<size_t, 4> PlaceHolderIndices;
386 for (
const BindArgument &B :
Args) {
387 if (B.PlaceHolderIndex) {
388 if (!PlaceHolderIndices.insert(B.PlaceHolderIndex).second)
395static std::vector<const FunctionDecl *>
397 std::vector<const FunctionDecl *> Candidates;
399 for (
const clang::CXXMethodDecl *Method : RecordDecl->methods()) {
400 OverloadedOperatorKind OOK = Method->getOverloadedOperator();
402 if (OOK != OverloadedOperatorKind::OO_Call)
405 if (Method->getNumParams() > NumArgs)
408 Candidates.push_back(Method);
412 for (
const clang::Decl *D : RecordDecl->decls()) {
413 const auto *FTD = dyn_cast<FunctionTemplateDecl>(D);
416 const FunctionDecl *FD = FTD->getTemplatedDecl();
418 OverloadedOperatorKind OOK = FD->getOverloadedOperator();
419 if (OOK != OverloadedOperatorKind::OO_Call)
422 if (FD->getNumParams() > NumArgs)
425 Candidates.push_back(FD);
432 ArrayRef<BindArgument>
Args) {
437 if (any_of(
Args, [](
const BindArgument &B) {
454 if (Callee.Type == CT_Other || Callee.Materialization == CMK_Other)
462 std::vector<const FunctionDecl *> Candidates =
464 if (Candidates.size() != 1)
467 return Candidates.front();
474 const Expr *Callee = Result.Nodes.getNodeAs<Expr>(
"ref");
477 if (
Type == CT_Object) {
478 const auto *BindCall = Result.Nodes.getNodeAs<CallExpr>(
"bind");
479 size_t NumArgs = BindCall->getNumArgs() - 1;
480 return getCallOperator(Callee->getType()->getAsCXXRecordDecl(), NumArgs);
484 if (
const auto *DRE = dyn_cast<DeclRefExpr>(CallExpression))
485 return dyn_cast<FunctionDecl>(DRE->getDecl());
494 const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>(
"ref");
496 QualType QT = CallableExpr->getType();
497 if (QT->isMemberFunctionPointerType())
498 return CT_MemberFunction;
500 if (QT->isFunctionPointerType() || QT->isFunctionReferenceType() ||
501 QT->isFunctionType())
504 if (QT->isRecordType()) {
505 const CXXRecordDecl *
Decl = QT->getAsCXXRecordDecl();
515static CallableMaterializationKind
517 const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>(
"ref");
521 const auto *
CE = dyn_cast<CXXConstructExpr>(NoTemporaries);
522 const auto *FC = dyn_cast<CXXFunctionalCastExpr>(NoTemporaries);
523 if ((isa<CallExpr>(NoTemporaries)) || (
CE && (
CE->getNumArgs() > 0)) ||
524 (FC && (FC->getCastKind() == CK_ConstructorConversion)))
527 return CMK_CallExpression;
529 if (isa<CXXFunctionalCastExpr>(NoTemporaries) ||
CE)
532 if (
const auto *DRE = dyn_cast<DeclRefExpr>(NoTemporaries)) {
533 if (isa<FunctionDecl>(DRE->getDecl()))
535 if (isa<VarDecl>(DRE->getDecl()))
536 return CMK_VariableRef;
542static LambdaProperties
544 const auto *CalleeExpr = Result.Nodes.getNodeAs<Expr>(
"ref");
548 const auto *
Bind = Result.Nodes.getNodeAs<CallExpr>(
"bind");
549 const auto *
Decl = cast<FunctionDecl>(
Bind->getCalleeDecl());
550 const auto *
NS = cast<NamespaceDecl>(
Decl->getEnclosingNamespaceContext());
551 while (
NS->isInlineNamespace())
552 NS = cast<NamespaceDecl>(
NS->getDeclContext());
553 LP.BindNamespace =
NS->getName();
560 if (LP.Callable.Materialization == CMK_VariableRef) {
561 LP.Callable.CE = CE_Var;
562 LP.Callable.CM = CM_ByValue;
563 LP.Callable.UsageIdentifier =
565 LP.Callable.CaptureIdentifier = std::string(
567 }
else if (LP.Callable.Materialization == CMK_CallExpression) {
568 LP.Callable.CE = CE_InitExpression;
569 LP.Callable.CM = CM_ByValue;
570 LP.Callable.UsageIdentifier =
"Func";
571 LP.Callable.CaptureIdentifier =
"Func";
582static bool emitCapture(llvm::StringSet<> &CaptureSet, StringRef Delimiter,
583 CaptureMode
CM, CaptureExpr
CE, StringRef Identifier,
584 StringRef InitExpression, raw_ostream &Stream) {
589 if (CaptureSet.count(Identifier) != 0)
596 Stream << Identifier;
597 if (
CE == CE_InitExpression)
598 Stream <<
" = " << InitExpression;
600 CaptureSet.insert(Identifier);
605 const MatchFinder::MatchResult &Result,
606 raw_ostream &Stream) {
607 llvm::StringSet<> CaptureSet;
608 bool AnyCapturesEmitted =
false;
611 CaptureSet,
"", LP.Callable.CM, LP.Callable.CE,
612 LP.Callable.CaptureIdentifier, LP.Callable.CaptureInitializer, Stream);
614 for (
const BindArgument &B : LP.BindArguments) {
615 if (B.CM == CM_None || !B.IsUsed)
618 StringRef Delimiter = AnyCapturesEmitted ?
", " :
"";
620 if (
emitCapture(CaptureSet, Delimiter, B.CM, B.CE, B.CaptureIdentifier,
621 B.SourceTokens, Stream))
622 AnyCapturesEmitted =
true;
626static ArrayRef<BindArgument>
628 ArrayRef<BindArgument>
Args = ArrayRef(P.BindArguments);
629 if (P.Callable.Type != CT_MemberFunction)
632 return Args.drop_front();
636 PermissiveParameterList(Options.get(
"PermissiveParameterList", false)) {}
639 Options.
store(Opts,
"PermissiveParameterList", PermissiveParameterList);
645 callee(namedDecl(hasAnyName(
"::boost::bind",
"::std::bind"))),
647 0, anyOf(expr(hasType(memberPointerType())).bind(
"ref"),
648 expr(hasParent(materializeTemporaryExpr().bind(
"ref"))),
649 expr().bind(
"ref"))))
655 const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>(
"bind");
659 diag(MatchedDecl->getBeginLoc(),
660 formatv(
"prefer a lambda to {0}::bind", LP.BindNamespace).str());
661 if (!LP.IsFixitSupported)
664 const auto *Ref = Result.Nodes.getNodeAs<Expr>(
"ref");
667 llvm::raw_string_ostream Stream(Buffer);
673 ArrayRef<BindArgument> FunctionCallArgs = ArrayRef(LP.BindArguments);
677 if (LP.Callable.Type == CT_Function) {
681 }
else if (LP.Callable.Type == CT_MemberFunction) {
682 const auto *MethodDecl = dyn_cast<CXXMethodDecl>(LP.Callable.Decl);
683 const BindArgument &ObjPtr = FunctionCallArgs.front();
687 Stream << ObjPtr.UsageIdentifier;
691 Stream << MethodDecl->getName();
693 Stream <<
" { return ";
694 switch (LP.Callable.CE) {
696 if (LP.Callable.UsageIdentifier != LP.Callable.CaptureIdentifier) {
697 Stream <<
"(" << LP.Callable.UsageIdentifier <<
")";
701 case CE_InitExpression:
702 Stream << LP.Callable.UsageIdentifier;
714 Diag << FixItHint::CreateReplacement(MatchedDecl->getSourceRange(),
SmallVector< BindArgument, 4 > BindArguments
const FunctionDecl * Decl
StringRef CaptureInitializer
CallableMaterializationKind Materialization
std::string UsageIdentifier
std::string CaptureIdentifier
const DeclRefExpr * DeclRef
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
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
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
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)
const FunctionDecl * getCallOperator(const CXXRecordDecl *Callable, size_t NumArgs)
static CallableType getCallableType(const MatchFinder::MatchResult &Result)
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)
const FunctionDecl * getCallMethodDecl(const MatchFinder::MatchResult &Result, CallableType Type, CallableMaterializationKind Materialization)
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)
llvm::StringMap< ClangTidyValue > OptionMap