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;
102struct LambdaProperties {
112 BindArgument &B,
const Expr *
E);
115 BindArgument &B,
const Expr *
E);
118 if (
const auto *T = dyn_cast<UnaryOperator>(
E))
121 const Expr *F =
E->IgnoreImplicit();
129 if (
const auto *T = dyn_cast<CXXConstructExpr>(
E))
132 const Expr *F =
E->IgnoreImplicit();
141 return Lexer::getSourceText(
142 CharSourceRange::getTokenRange(
E->getBeginLoc(),
E->getEndLoc()),
143 *Result.SourceManager, Result.Context->getLangOpts());
147 const auto *
CE = dyn_cast<CallExpr>(
E->IgnoreImplicit());
150 const auto *ND = dyn_cast<NamedDecl>(
CE->getCalleeDecl());
153 return ND->getQualifiedNameAsString() ==
Name;
158 BindArgument &B,
const CallExpr *
CE,
159 unsigned &CaptureIndex) {
169 B.CE = CE_InitExpression;
170 B.UsageIdentifier =
"capture" + llvm::utostr(CaptureIndex++);
176 B.Kind = BK_CallExpr;
178 B.CE = CE_InitExpression;
179 B.UsageIdentifier =
"capture" + llvm::utostr(CaptureIndex++);
181 B.CaptureIdentifier = B.UsageIdentifier;
185 if (
const auto *
DeclRef = dyn_cast<DeclRefExpr>(Statement)) {
187 if (
const auto *Var = dyn_cast_or_null<VarDecl>(
Decl)) {
188 if (Var->isLocalVarDeclOrParm())
191 }
else if (isa<CXXThisExpr>(Statement))
198 BindArgument &B,
const Expr *
E) {
199 if (
const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(
E)) {
200 if (
const auto *
CE = dyn_cast<CXXConstructExpr>(BTE->getSubExpr()))
205 const auto *DRE = dyn_cast<DeclRefExpr>(
E->IgnoreImplicit());
209 const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
210 if (!VD || !VD->isLocalVarDeclOrParm())
215 B.CaptureIdentifier = B.UsageIdentifier;
220 BindArgument &B,
const Expr *
E) {
221 if (
const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(
E)) {
222 if (
const auto *
CE = dyn_cast<CXXConstructExpr>(BTE->getSubExpr()))
227 E =
E->IgnoreImplicit();
228 if (isa<CXXThisExpr>(
E)) {
232 B.CaptureIdentifier =
"this";
236 const auto *ME = dyn_cast<MemberExpr>(
E);
240 if (!ME->isLValue() || !isa<FieldDecl>(ME->getMemberDecl()))
243 if (isa<CXXThisExpr>(ME->getBase())) {
247 B.CaptureIdentifier =
"this";
254static SmallVector<BindArgument, 4>
256 const CallableInfo &Callable) {
258 static llvm::Regex MatchPlaceholder(
"^_([0-9]+)$");
260 const auto *BindCall = Result.Nodes.getNodeAs<CallExpr>(
"bind");
263 unsigned CaptureIndex = 0;
264 for (
size_t I = 1, ArgCount = BindCall->getNumArgs(); I < ArgCount; ++I) {
266 const Expr *
E = BindCall->getArg(I);
269 size_t ArgIndex = I - 1;
270 if (
Callable.Type == CT_MemberFunction)
273 bool IsObjectPtr = (I == 1 &&
Callable.Type == CT_MemberFunction);
277 if (!
Callable.Decl || ArgIndex < Callable.Decl->getNumParams() ||
281 SmallVector<StringRef, 2> Matches;
282 const auto *DRE = dyn_cast<DeclRefExpr>(
E);
283 if (MatchPlaceholder.match(B.SourceTokens, &Matches) ||
285 (DRE && MatchPlaceholder.match(DRE->getDecl()->getName(), &Matches))) {
286 B.Kind = BK_Placeholder;
287 B.PlaceHolderIndex = std::stoi(std::string(Matches[1]));
288 B.UsageIdentifier =
"PH" + llvm::utostr(B.PlaceHolderIndex);
289 B.CaptureIdentifier = B.UsageIdentifier;
307 B.CE = CE_InitExpression;
309 B.UsageIdentifier =
"ObjectPtr";
310 B.CaptureIdentifier = B.UsageIdentifier;
312 B.CE = CE_InitExpression;
314 B.CaptureIdentifier =
"capture" + llvm::utostr(CaptureIndex++);
315 B.UsageIdentifier = B.CaptureIdentifier;
322 size_t PlaceholderIndex) {
323 for (
size_t I = 0; I <
Args.size(); ++I)
331 llvm::raw_ostream &Stream,
332 bool PermissiveParameterList) {
334 ArrayRef<BindArgument>
Args = LP.BindArguments;
336 const auto *MaxPlaceholderIt =
337 std::max_element(
Args.begin(),
Args.end(),
338 [](
const BindArgument &B1,
const BindArgument &B2) {
339 return B1.PlaceHolderIndex < B2.PlaceHolderIndex;
343 if (!PermissiveParameterList && (MaxPlaceholderIt ==
Args.end() ||
344 MaxPlaceholderIt->PlaceHolderIndex == 0))
347 size_t PlaceholderCount = MaxPlaceholderIt->PlaceHolderIndex;
349 StringRef Delimiter =
"";
350 for (
size_t I = 1; I <= PlaceholderCount; ++I) {
351 Stream << Delimiter <<
"auto &&";
356 Stream <<
" " <<
Args[ArgIndex].UsageIdentifier;
359 if (PermissiveParameterList)
360 Stream << Delimiter <<
"auto && ...";
365 llvm::raw_ostream &Stream) {
366 StringRef Delimiter =
"";
368 for (
const BindArgument &B :
Args) {
371 if (B.Kind == BK_Placeholder) {
372 Stream <<
"std::forward<decltype(" << B.UsageIdentifier <<
")>";
373 Stream <<
"(" << B.UsageIdentifier <<
")";
374 }
else if (B.CM != CM_None)
375 Stream << B.UsageIdentifier;
377 Stream << B.SourceTokens;
384 llvm::SmallSet<size_t, 4> PlaceHolderIndices;
385 for (
const BindArgument &B :
Args) {
386 if (B.PlaceHolderIndex) {
387 if (!PlaceHolderIndices.insert(B.PlaceHolderIndex).second)
394static std::vector<const FunctionDecl *>
396 std::vector<const FunctionDecl *> Candidates;
398 for (
const clang::CXXMethodDecl *Method : RecordDecl->methods()) {
399 OverloadedOperatorKind OOK = Method->getOverloadedOperator();
401 if (OOK != OverloadedOperatorKind::OO_Call)
404 if (Method->getNumParams() > NumArgs)
407 Candidates.push_back(Method);
411 for (
const clang::Decl *D : RecordDecl->decls()) {
412 const auto *FTD = dyn_cast<FunctionTemplateDecl>(D);
415 const FunctionDecl *FD = FTD->getTemplatedDecl();
417 OverloadedOperatorKind OOK = FD->getOverloadedOperator();
418 if (OOK != OverloadedOperatorKind::OO_Call)
421 if (FD->getNumParams() > NumArgs)
424 Candidates.push_back(FD);
431 ArrayRef<BindArgument>
Args) {
436 if (any_of(
Args, [](
const BindArgument &B) {
453 if (Callee.Type == CT_Other || Callee.Materialization == CMK_Other)
461 std::vector<const FunctionDecl *> Candidates =
463 if (Candidates.size() != 1)
466 return Candidates.front();
473 const Expr *Callee = Result.Nodes.getNodeAs<Expr>(
"ref");
476 if (
Type == CT_Object) {
477 const auto *BindCall = Result.Nodes.getNodeAs<CallExpr>(
"bind");
478 size_t NumArgs = BindCall->getNumArgs() - 1;
479 return getCallOperator(Callee->getType()->getAsCXXRecordDecl(), NumArgs);
483 if (
const auto *DRE = dyn_cast<DeclRefExpr>(CallExpression))
484 return dyn_cast<FunctionDecl>(DRE->getDecl());
493 const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>(
"ref");
495 QualType QT = CallableExpr->getType();
496 if (QT->isMemberFunctionPointerType())
497 return CT_MemberFunction;
499 if (QT->isFunctionPointerType() || QT->isFunctionReferenceType() ||
500 QT->isFunctionType())
503 if (QT->isRecordType()) {
504 const CXXRecordDecl *
Decl = QT->getAsCXXRecordDecl();
514static CallableMaterializationKind
516 const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>(
"ref");
520 const auto *
CE = dyn_cast<CXXConstructExpr>(NoTemporaries);
521 const auto *FC = dyn_cast<CXXFunctionalCastExpr>(NoTemporaries);
522 if ((isa<CallExpr>(NoTemporaries)) || (
CE && (
CE->getNumArgs() > 0)) ||
523 (FC && (FC->getCastKind() == CK_ConstructorConversion)))
526 return CMK_CallExpression;
528 if (isa<CXXFunctionalCastExpr>(NoTemporaries) ||
CE)
531 if (
const auto *DRE = dyn_cast<DeclRefExpr>(NoTemporaries)) {
532 if (isa<FunctionDecl>(DRE->getDecl()))
534 if (isa<VarDecl>(DRE->getDecl()))
535 return CMK_VariableRef;
541static LambdaProperties
543 const auto *CalleeExpr = Result.Nodes.getNodeAs<Expr>(
"ref");
547 const auto *
Bind = Result.Nodes.getNodeAs<CallExpr>(
"bind");
548 const auto *
Decl = cast<FunctionDecl>(
Bind->getCalleeDecl());
549 const auto *
NS = cast<NamespaceDecl>(
Decl->getEnclosingNamespaceContext());
550 while (
NS->isInlineNamespace())
551 NS = cast<NamespaceDecl>(
NS->getDeclContext());
552 LP.BindNamespace =
NS->getName();
558 if (LP.Callable.Decl)
560 LP.Callable.Decl->getReturnType().getCanonicalType().getTypePtr())
561 LP.Callable.DoesReturn = !
ReturnType->isVoidType();
563 if (LP.Callable.Materialization == CMK_VariableRef) {
564 LP.Callable.CE = CE_Var;
565 LP.Callable.CM = CM_ByValue;
566 LP.Callable.UsageIdentifier =
568 LP.Callable.CaptureIdentifier = std::string(
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";
585static bool emitCapture(llvm::StringSet<> &CaptureSet, StringRef Delimiter,
586 CaptureMode
CM, CaptureExpr
CE, StringRef Identifier,
587 StringRef InitExpression, raw_ostream &Stream) {
592 if (CaptureSet.count(Identifier) != 0)
599 Stream << Identifier;
600 if (
CE == CE_InitExpression)
601 Stream <<
" = " << InitExpression;
603 CaptureSet.insert(Identifier);
608 const MatchFinder::MatchResult &Result,
609 raw_ostream &Stream) {
610 llvm::StringSet<> CaptureSet;
611 bool AnyCapturesEmitted =
false;
614 CaptureSet,
"", LP.Callable.CM, LP.Callable.CE,
615 LP.Callable.CaptureIdentifier, LP.Callable.CaptureInitializer, Stream);
617 for (
const BindArgument &B : LP.BindArguments) {
618 if (B.CM == CM_None || !B.IsUsed)
621 StringRef Delimiter = AnyCapturesEmitted ?
", " :
"";
623 if (
emitCapture(CaptureSet, Delimiter, B.CM, B.CE, B.CaptureIdentifier,
624 B.SourceTokens, Stream))
625 AnyCapturesEmitted =
true;
629static ArrayRef<BindArgument>
631 ArrayRef<BindArgument>
Args = ArrayRef(P.BindArguments);
632 if (P.Callable.Type != CT_MemberFunction)
635 return Args.drop_front();
639 PermissiveParameterList(Options.get(
"PermissiveParameterList", false)) {}
642 Options.
store(Opts,
"PermissiveParameterList", PermissiveParameterList);
648 callee(namedDecl(hasAnyName(
"::boost::bind",
"::std::bind"))),
650 0, anyOf(expr(hasType(memberPointerType())).bind(
"ref"),
651 expr(hasParent(materializeTemporaryExpr().bind(
"ref"))),
652 expr().bind(
"ref"))))
658 const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>(
"bind");
662 diag(MatchedDecl->getBeginLoc(),
663 formatv(
"prefer a lambda to {0}::bind", LP.BindNamespace).str());
664 if (!LP.IsFixitSupported)
667 const auto *Ref = Result.Nodes.getNodeAs<Expr>(
"ref");
670 llvm::raw_string_ostream Stream(Buffer);
676 ArrayRef<BindArgument> FunctionCallArgs = ArrayRef(LP.BindArguments);
682 if (LP.Callable.DoesReturn) {
686 if (LP.Callable.Type == CT_Function) {
690 }
else if (LP.Callable.Type == CT_MemberFunction) {
691 const auto *MethodDecl = dyn_cast<CXXMethodDecl>(LP.Callable.Decl);
692 const BindArgument &ObjPtr = FunctionCallArgs.front();
694 if (MethodDecl->getOverloadedOperator() == OO_Call) {
695 Stream <<
"(*" << ObjPtr.UsageIdentifier <<
')';
698 Stream << ObjPtr.UsageIdentifier;
701 Stream << MethodDecl->getNameAsString();
704 switch (LP.Callable.CE) {
706 if (LP.Callable.UsageIdentifier != LP.Callable.CaptureIdentifier) {
707 Stream <<
"(" << LP.Callable.UsageIdentifier <<
")";
711 case CE_InitExpression:
712 Stream << LP.Callable.UsageIdentifier;
724 Diag << FixItHint::CreateReplacement(MatchedDecl->getSourceRange(),
SmallVector< BindArgument, 4 > BindArguments
const FunctionDecl * Decl
StringRef CaptureInitializer
CallableMaterializationKind Materialization
std::string UsageIdentifier
std::string CaptureIdentifier
llvm::SmallString< 256U > Name
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