123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718 |
- //===--- AvoidBindCheck.cpp - clang-tidy-----------------------------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #include "AvoidBindCheck.h"
- #include "clang/AST/ASTContext.h"
- #include "clang/ASTMatchers/ASTMatchFinder.h"
- #include "clang/Basic/LLVM.h"
- #include "clang/Basic/LangOptions.h"
- #include "clang/Basic/SourceLocation.h"
- #include "clang/Lex/Lexer.h"
- #include "llvm/ADT/ArrayRef.h"
- #include "llvm/ADT/STLExtras.h"
- #include "llvm/ADT/SmallSet.h"
- #include "llvm/ADT/SmallVector.h"
- #include "llvm/ADT/StringRef.h"
- #include "llvm/ADT/StringSet.h"
- #include "llvm/Support/Casting.h"
- #include "llvm/Support/FormatVariadic.h"
- #include "llvm/Support/Regex.h"
- #include "llvm/Support/raw_ostream.h"
- #include <algorithm>
- #include <cstddef>
- #include <string>
- using namespace clang::ast_matchers;
- namespace clang::tidy::modernize {
- namespace {
- enum BindArgumentKind { BK_Temporary, BK_Placeholder, BK_CallExpr, BK_Other };
- enum CaptureMode { CM_None, CM_ByRef, CM_ByValue };
- enum CaptureExpr { CE_None, CE_Var, CE_InitExpression };
- enum CallableType {
- CT_Other, // unknown
- CT_Function, // global or static function
- CT_MemberFunction, // member function with implicit this
- CT_Object, // object with operator()
- };
- enum CallableMaterializationKind {
- CMK_Other, // unknown
- CMK_Function, // callable is the name of a member or non-member function.
- CMK_VariableRef, // callable is a simple expression involving a global or
- // local variable.
- CMK_CallExpression, // callable is obtained as the result of a call expression
- };
- struct BindArgument {
- // A rough classification of the type of expression this argument was.
- BindArgumentKind Kind = BK_Other;
- // If this argument required a capture, a value indicating how it was
- // captured.
- CaptureMode CM = CM_None;
- // Whether the argument is a simple variable (we can capture it directly),
- // or an expression (we must introduce a capture variable).
- CaptureExpr CE = CE_None;
- // The exact spelling of this argument in the source code.
- StringRef SourceTokens;
- // The identifier of the variable within the capture list. This may be
- // different from UsageIdentifier for example in the expression *d, where the
- // variable is captured as d, but referred to as *d.
- std::string CaptureIdentifier;
- // If this is a placeholder or capture init expression, contains the tokens
- // used to refer to this parameter from within the body of the lambda.
- std::string UsageIdentifier;
- // If Kind == BK_Placeholder, the index of the placeholder.
- size_t PlaceHolderIndex = 0;
- // True if the argument is used inside the lambda, false otherwise.
- bool IsUsed = false;
- // The actual Expr object representing this expression.
- const Expr *E = nullptr;
- };
- struct CallableInfo {
- CallableType Type = CT_Other;
- CallableMaterializationKind Materialization = CMK_Other;
- CaptureMode CM = CM_None;
- CaptureExpr CE = CE_None;
- StringRef SourceTokens;
- std::string CaptureIdentifier;
- std::string UsageIdentifier;
- StringRef CaptureInitializer;
- const FunctionDecl *Decl = nullptr;
- };
- struct LambdaProperties {
- CallableInfo Callable;
- SmallVector<BindArgument, 4> BindArguments;
- StringRef BindNamespace;
- bool IsFixitSupported = false;
- };
- } // end namespace
- static bool tryCaptureAsLocalVariable(const MatchFinder::MatchResult &Result,
- BindArgument &B, const Expr *E);
- static bool tryCaptureAsMemberVariable(const MatchFinder::MatchResult &Result,
- BindArgument &B, const Expr *E);
- static const Expr *ignoreTemporariesAndPointers(const Expr *E) {
- if (const auto *T = dyn_cast<UnaryOperator>(E))
- return ignoreTemporariesAndPointers(T->getSubExpr());
- const Expr *F = E->IgnoreImplicit();
- if (E != F)
- return ignoreTemporariesAndPointers(F);
- return E;
- }
- static const Expr *ignoreTemporariesAndConstructors(const Expr *E) {
- if (const auto *T = dyn_cast<CXXConstructExpr>(E))
- return ignoreTemporariesAndConstructors(T->getArg(0));
- const Expr *F = E->IgnoreImplicit();
- if (E != F)
- return ignoreTemporariesAndPointers(F);
- return E;
- }
- static StringRef getSourceTextForExpr(const MatchFinder::MatchResult &Result,
- const Expr *E) {
- return Lexer::getSourceText(
- CharSourceRange::getTokenRange(E->getBeginLoc(), E->getEndLoc()),
- *Result.SourceManager, Result.Context->getLangOpts());
- }
- static bool isCallExprNamed(const Expr *E, StringRef Name) {
- const auto *CE = dyn_cast<CallExpr>(E->IgnoreImplicit());
- if (!CE)
- return false;
- const auto *ND = dyn_cast<NamedDecl>(CE->getCalleeDecl());
- if (!ND)
- return false;
- return ND->getQualifiedNameAsString() == Name;
- }
- static void
- initializeBindArgumentForCallExpr(const MatchFinder::MatchResult &Result,
- BindArgument &B, const CallExpr *CE,
- unsigned &CaptureIndex) {
- // std::ref(x) means to capture x by reference.
- if (isCallExprNamed(CE, "boost::ref") || isCallExprNamed(CE, "std::ref")) {
- B.Kind = BK_Other;
- if (tryCaptureAsLocalVariable(Result, B, CE->getArg(0)) ||
- tryCaptureAsMemberVariable(Result, B, CE->getArg(0))) {
- B.CE = CE_Var;
- } else {
- // The argument to std::ref is an expression that produces a reference.
- // Create a capture reference to hold it.
- B.CE = CE_InitExpression;
- B.UsageIdentifier = "capture" + llvm::utostr(CaptureIndex++);
- }
- // Strip off the reference wrapper.
- B.SourceTokens = getSourceTextForExpr(Result, CE->getArg(0));
- B.CM = CM_ByRef;
- } else {
- B.Kind = BK_CallExpr;
- B.CM = CM_ByValue;
- B.CE = CE_InitExpression;
- B.UsageIdentifier = "capture" + llvm::utostr(CaptureIndex++);
- }
- B.CaptureIdentifier = B.UsageIdentifier;
- }
- static bool anyDescendantIsLocal(const Stmt *Statement) {
- if (const auto *DeclRef = dyn_cast<DeclRefExpr>(Statement)) {
- const ValueDecl *Decl = DeclRef->getDecl();
- if (const auto *Var = dyn_cast_or_null<VarDecl>(Decl)) {
- if (Var->isLocalVarDeclOrParm())
- return true;
- }
- } else if (isa<CXXThisExpr>(Statement))
- return true;
- return any_of(Statement->children(), anyDescendantIsLocal);
- }
- static bool tryCaptureAsLocalVariable(const MatchFinder::MatchResult &Result,
- BindArgument &B, const Expr *E) {
- if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E)) {
- if (const auto *CE = dyn_cast<CXXConstructExpr>(BTE->getSubExpr()))
- return tryCaptureAsLocalVariable(Result, B, CE->getArg(0));
- return false;
- }
- const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreImplicit());
- if (!DRE)
- return false;
- const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
- if (!VD || !VD->isLocalVarDeclOrParm())
- return false;
- B.CM = CM_ByValue;
- B.UsageIdentifier = std::string(getSourceTextForExpr(Result, E));
- B.CaptureIdentifier = B.UsageIdentifier;
- return true;
- }
- static bool tryCaptureAsMemberVariable(const MatchFinder::MatchResult &Result,
- BindArgument &B, const Expr *E) {
- if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E)) {
- if (const auto *CE = dyn_cast<CXXConstructExpr>(BTE->getSubExpr()))
- return tryCaptureAsMemberVariable(Result, B, CE->getArg(0));
- return false;
- }
- E = E->IgnoreImplicit();
- if (isa<CXXThisExpr>(E)) {
- // E is a direct use of "this".
- B.CM = CM_ByValue;
- B.UsageIdentifier = std::string(getSourceTextForExpr(Result, E));
- B.CaptureIdentifier = "this";
- return true;
- }
- const auto *ME = dyn_cast<MemberExpr>(E);
- if (!ME)
- return false;
- if (!ME->isLValue() || !isa<FieldDecl>(ME->getMemberDecl()))
- return false;
- if (isa<CXXThisExpr>(ME->getBase())) {
- // E refers to a data member without an explicit "this".
- B.CM = CM_ByValue;
- B.UsageIdentifier = std::string(getSourceTextForExpr(Result, E));
- B.CaptureIdentifier = "this";
- return true;
- }
- return false;
- }
- static SmallVector<BindArgument, 4>
- buildBindArguments(const MatchFinder::MatchResult &Result,
- const CallableInfo &Callable) {
- SmallVector<BindArgument, 4> BindArguments;
- static llvm::Regex MatchPlaceholder("^_([0-9]+)$");
- const auto *BindCall = Result.Nodes.getNodeAs<CallExpr>("bind");
- // Start at index 1 as first argument to bind is the function name.
- unsigned CaptureIndex = 0;
- for (size_t I = 1, ArgCount = BindCall->getNumArgs(); I < ArgCount; ++I) {
- const Expr *E = BindCall->getArg(I);
- BindArgument &B = BindArguments.emplace_back();
- size_t ArgIndex = I - 1;
- if (Callable.Type == CT_MemberFunction)
- --ArgIndex;
- bool IsObjectPtr = (I == 1 && Callable.Type == CT_MemberFunction);
- B.E = E;
- B.SourceTokens = getSourceTextForExpr(Result, E);
- if (!Callable.Decl || ArgIndex < Callable.Decl->getNumParams() ||
- IsObjectPtr)
- B.IsUsed = true;
- SmallVector<StringRef, 2> Matches;
- const auto *DRE = dyn_cast<DeclRefExpr>(E);
- if (MatchPlaceholder.match(B.SourceTokens, &Matches) ||
- // Check for match with qualifiers removed.
- (DRE && MatchPlaceholder.match(DRE->getDecl()->getName(), &Matches))) {
- B.Kind = BK_Placeholder;
- B.PlaceHolderIndex = std::stoi(std::string(Matches[1]));
- B.UsageIdentifier = "PH" + llvm::utostr(B.PlaceHolderIndex);
- B.CaptureIdentifier = B.UsageIdentifier;
- continue;
- }
- if (const auto *CE =
- dyn_cast<CallExpr>(ignoreTemporariesAndConstructors(E))) {
- initializeBindArgumentForCallExpr(Result, B, CE, CaptureIndex);
- continue;
- }
- if (tryCaptureAsLocalVariable(Result, B, B.E) ||
- tryCaptureAsMemberVariable(Result, B, B.E))
- continue;
- // If it's not something we recognize, capture it by init expression to be
- // safe.
- B.Kind = BK_Other;
- if (IsObjectPtr) {
- B.CE = CE_InitExpression;
- B.CM = CM_ByValue;
- B.UsageIdentifier = "ObjectPtr";
- B.CaptureIdentifier = B.UsageIdentifier;
- } else if (anyDescendantIsLocal(B.E)) {
- B.CE = CE_InitExpression;
- B.CM = CM_ByValue;
- B.CaptureIdentifier = "capture" + llvm::utostr(CaptureIndex++);
- B.UsageIdentifier = B.CaptureIdentifier;
- }
- }
- return BindArguments;
- }
- static int findPositionOfPlaceholderUse(ArrayRef<BindArgument> Args,
- size_t PlaceholderIndex) {
- for (size_t I = 0; I < Args.size(); ++I)
- if (Args[I].PlaceHolderIndex == PlaceholderIndex)
- return I;
- return -1;
- }
- static void addPlaceholderArgs(const LambdaProperties &LP,
- llvm::raw_ostream &Stream,
- bool PermissiveParameterList) {
- ArrayRef<BindArgument> Args = LP.BindArguments;
- const auto *MaxPlaceholderIt =
- std::max_element(Args.begin(), Args.end(),
- [](const BindArgument &B1, const BindArgument &B2) {
- return B1.PlaceHolderIndex < B2.PlaceHolderIndex;
- });
- // Placeholders (if present) have index 1 or greater.
- if (!PermissiveParameterList && (MaxPlaceholderIt == Args.end() ||
- MaxPlaceholderIt->PlaceHolderIndex == 0))
- return;
- size_t PlaceholderCount = MaxPlaceholderIt->PlaceHolderIndex;
- Stream << "(";
- StringRef Delimiter = "";
- for (size_t I = 1; I <= PlaceholderCount; ++I) {
- Stream << Delimiter << "auto &&";
- int ArgIndex = findPositionOfPlaceholderUse(Args, I);
- if (ArgIndex != -1 && Args[ArgIndex].IsUsed)
- Stream << " " << Args[ArgIndex].UsageIdentifier;
- Delimiter = ", ";
- }
- if (PermissiveParameterList)
- Stream << Delimiter << "auto && ...";
- Stream << ")";
- }
- static void addFunctionCallArgs(ArrayRef<BindArgument> Args,
- llvm::raw_ostream &Stream) {
- StringRef Delimiter = "";
- for (int I = 0, Size = Args.size(); I < Size; ++I) {
- const BindArgument &B = Args[I];
- Stream << Delimiter;
- if (B.Kind == BK_Placeholder) {
- Stream << "std::forward<decltype(" << B.UsageIdentifier << ")>";
- Stream << "(" << B.UsageIdentifier << ")";
- } else if (B.CM != CM_None)
- Stream << B.UsageIdentifier;
- else
- Stream << B.SourceTokens;
- Delimiter = ", ";
- }
- }
- static bool isPlaceHolderIndexRepeated(const ArrayRef<BindArgument> Args) {
- llvm::SmallSet<size_t, 4> PlaceHolderIndices;
- for (const BindArgument &B : Args) {
- if (B.PlaceHolderIndex) {
- if (!PlaceHolderIndices.insert(B.PlaceHolderIndex).second)
- return true;
- }
- }
- return false;
- }
- static std::vector<const FunctionDecl *>
- findCandidateCallOperators(const CXXRecordDecl *RecordDecl, size_t NumArgs) {
- std::vector<const FunctionDecl *> Candidates;
- for (const clang::CXXMethodDecl *Method : RecordDecl->methods()) {
- OverloadedOperatorKind OOK = Method->getOverloadedOperator();
- if (OOK != OverloadedOperatorKind::OO_Call)
- continue;
- if (Method->getNumParams() > NumArgs)
- continue;
- Candidates.push_back(Method);
- }
- // Find templated operator(), if any.
- for (const clang::Decl *D : RecordDecl->decls()) {
- const auto *FTD = dyn_cast<FunctionTemplateDecl>(D);
- if (!FTD)
- continue;
- const FunctionDecl *FD = FTD->getTemplatedDecl();
- OverloadedOperatorKind OOK = FD->getOverloadedOperator();
- if (OOK != OverloadedOperatorKind::OO_Call)
- continue;
- if (FD->getNumParams() > NumArgs)
- continue;
- Candidates.push_back(FD);
- }
- return Candidates;
- }
- static bool isFixitSupported(const CallableInfo &Callee,
- ArrayRef<BindArgument> Args) {
- // Do not attempt to create fixits for nested std::bind or std::ref.
- // Supporting nested std::bind will be more difficult due to placeholder
- // sharing between outer and inner std::bind invocations, and std::ref
- // requires us to capture some parameters by reference instead of by value.
- if (any_of(Args, [](const BindArgument &B) {
- return isCallExprNamed(B.E, "boost::bind") ||
- isCallExprNamed(B.E, "std::bind");
- })) {
- return false;
- }
- // Do not attempt to create fixits when placeholders are reused.
- // Unused placeholders are supported by requiring C++14 generic lambdas.
- // FIXME: Support this case by deducing the common type.
- if (isPlaceHolderIndexRepeated(Args))
- return false;
- // If we can't determine the Decl being used, don't offer a fixit.
- if (!Callee.Decl)
- return false;
- if (Callee.Type == CT_Other || Callee.Materialization == CMK_Other)
- return false;
- return true;
- }
- const FunctionDecl *getCallOperator(const CXXRecordDecl *Callable,
- size_t NumArgs) {
- std::vector<const FunctionDecl *> Candidates =
- findCandidateCallOperators(Callable, NumArgs);
- if (Candidates.size() != 1)
- return nullptr;
- return Candidates.front();
- }
- const FunctionDecl *
- getCallMethodDecl(const MatchFinder::MatchResult &Result, CallableType Type,
- CallableMaterializationKind Materialization) {
- const Expr *Callee = Result.Nodes.getNodeAs<Expr>("ref");
- const Expr *CallExpression = ignoreTemporariesAndPointers(Callee);
- if (Type == CT_Object) {
- const auto *BindCall = Result.Nodes.getNodeAs<CallExpr>("bind");
- size_t NumArgs = BindCall->getNumArgs() - 1;
- return getCallOperator(Callee->getType()->getAsCXXRecordDecl(), NumArgs);
- }
- if (Materialization == CMK_Function) {
- if (const auto *DRE = dyn_cast<DeclRefExpr>(CallExpression))
- return dyn_cast<FunctionDecl>(DRE->getDecl());
- }
- // Maybe this is an indirect call through a function pointer or something
- // where we can't determine the exact decl.
- return nullptr;
- }
- static CallableType getCallableType(const MatchFinder::MatchResult &Result) {
- const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>("ref");
- QualType QT = CallableExpr->getType();
- if (QT->isMemberFunctionPointerType())
- return CT_MemberFunction;
- if (QT->isFunctionPointerType() || QT->isFunctionReferenceType() ||
- QT->isFunctionType())
- return CT_Function;
- if (QT->isRecordType()) {
- const CXXRecordDecl *Decl = QT->getAsCXXRecordDecl();
- if (!Decl)
- return CT_Other;
- return CT_Object;
- }
- return CT_Other;
- }
- static CallableMaterializationKind
- getCallableMaterialization(const MatchFinder::MatchResult &Result) {
- const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>("ref");
- const auto *NoTemporaries = ignoreTemporariesAndPointers(CallableExpr);
- const auto *CE = dyn_cast<CXXConstructExpr>(NoTemporaries);
- const auto *FC = dyn_cast<CXXFunctionalCastExpr>(NoTemporaries);
- if ((isa<CallExpr>(NoTemporaries)) || (CE && (CE->getNumArgs() > 0)) ||
- (FC && (FC->getCastKind() == CK_ConstructorConversion)))
- // CE is something that looks like a call, with arguments - either
- // a function call or a constructor invocation.
- return CMK_CallExpression;
- if (isa<CXXFunctionalCastExpr>(NoTemporaries) || CE)
- return CMK_Function;
- if (const auto *DRE = dyn_cast<DeclRefExpr>(NoTemporaries)) {
- if (isa<FunctionDecl>(DRE->getDecl()))
- return CMK_Function;
- if (isa<VarDecl>(DRE->getDecl()))
- return CMK_VariableRef;
- }
- return CMK_Other;
- }
- static LambdaProperties
- getLambdaProperties(const MatchFinder::MatchResult &Result) {
- const auto *CalleeExpr = Result.Nodes.getNodeAs<Expr>("ref");
- LambdaProperties LP;
- const auto *Bind = Result.Nodes.getNodeAs<CallExpr>("bind");
- const auto *Decl = cast<FunctionDecl>(Bind->getCalleeDecl());
- const auto *NS = cast<NamespaceDecl>(Decl->getEnclosingNamespaceContext());
- while (NS->isInlineNamespace())
- NS = cast<NamespaceDecl>(NS->getDeclContext());
- LP.BindNamespace = NS->getName();
- LP.Callable.Type = getCallableType(Result);
- LP.Callable.Materialization = getCallableMaterialization(Result);
- LP.Callable.Decl =
- getCallMethodDecl(Result, LP.Callable.Type, LP.Callable.Materialization);
- LP.Callable.SourceTokens = getSourceTextForExpr(Result, CalleeExpr);
- if (LP.Callable.Materialization == CMK_VariableRef) {
- LP.Callable.CE = CE_Var;
- LP.Callable.CM = CM_ByValue;
- LP.Callable.UsageIdentifier =
- std::string(getSourceTextForExpr(Result, CalleeExpr));
- LP.Callable.CaptureIdentifier = std::string(
- getSourceTextForExpr(Result, ignoreTemporariesAndPointers(CalleeExpr)));
- } else if (LP.Callable.Materialization == CMK_CallExpression) {
- LP.Callable.CE = CE_InitExpression;
- LP.Callable.CM = CM_ByValue;
- LP.Callable.UsageIdentifier = "Func";
- LP.Callable.CaptureIdentifier = "Func";
- LP.Callable.CaptureInitializer = getSourceTextForExpr(Result, CalleeExpr);
- }
- LP.BindArguments = buildBindArguments(Result, LP.Callable);
- LP.IsFixitSupported = isFixitSupported(LP.Callable, LP.BindArguments);
- return LP;
- }
- static bool emitCapture(llvm::StringSet<> &CaptureSet, StringRef Delimiter,
- CaptureMode CM, CaptureExpr CE, StringRef Identifier,
- StringRef InitExpression, raw_ostream &Stream) {
- if (CM == CM_None)
- return false;
- // This capture has already been emitted.
- if (CaptureSet.count(Identifier) != 0)
- return false;
- Stream << Delimiter;
- if (CM == CM_ByRef)
- Stream << "&";
- Stream << Identifier;
- if (CE == CE_InitExpression)
- Stream << " = " << InitExpression;
- CaptureSet.insert(Identifier);
- return true;
- }
- static void emitCaptureList(const LambdaProperties &LP,
- const MatchFinder::MatchResult &Result,
- raw_ostream &Stream) {
- llvm::StringSet<> CaptureSet;
- bool AnyCapturesEmitted = false;
- AnyCapturesEmitted = emitCapture(
- CaptureSet, "", LP.Callable.CM, LP.Callable.CE,
- LP.Callable.CaptureIdentifier, LP.Callable.CaptureInitializer, Stream);
- for (const BindArgument &B : LP.BindArguments) {
- if (B.CM == CM_None || !B.IsUsed)
- continue;
- StringRef Delimiter = AnyCapturesEmitted ? ", " : "";
- if (emitCapture(CaptureSet, Delimiter, B.CM, B.CE, B.CaptureIdentifier,
- B.SourceTokens, Stream))
- AnyCapturesEmitted = true;
- }
- }
- static ArrayRef<BindArgument>
- getForwardedArgumentList(const LambdaProperties &P) {
- ArrayRef<BindArgument> Args = ArrayRef(P.BindArguments);
- if (P.Callable.Type != CT_MemberFunction)
- return Args;
- return Args.drop_front();
- }
- AvoidBindCheck::AvoidBindCheck(StringRef Name, ClangTidyContext *Context)
- : ClangTidyCheck(Name, Context),
- PermissiveParameterList(Options.get("PermissiveParameterList", false)) {}
- void AvoidBindCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
- Options.store(Opts, "PermissiveParameterList", PermissiveParameterList);
- }
- void AvoidBindCheck::registerMatchers(MatchFinder *Finder) {
- Finder->addMatcher(
- callExpr(
- callee(namedDecl(hasAnyName("::boost::bind", "::std::bind"))),
- hasArgument(
- 0, anyOf(expr(hasType(memberPointerType())).bind("ref"),
- expr(hasParent(materializeTemporaryExpr().bind("ref"))),
- expr().bind("ref"))))
- .bind("bind"),
- this);
- }
- void AvoidBindCheck::check(const MatchFinder::MatchResult &Result) {
- const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>("bind");
- LambdaProperties LP = getLambdaProperties(Result);
- auto Diag =
- diag(MatchedDecl->getBeginLoc(),
- formatv("prefer a lambda to {0}::bind", LP.BindNamespace).str());
- if (!LP.IsFixitSupported)
- return;
- const auto *Ref = Result.Nodes.getNodeAs<Expr>("ref");
- std::string Buffer;
- llvm::raw_string_ostream Stream(Buffer);
- Stream << "[";
- emitCaptureList(LP, Result, Stream);
- Stream << "]";
- ArrayRef<BindArgument> FunctionCallArgs = ArrayRef(LP.BindArguments);
- addPlaceholderArgs(LP, Stream, PermissiveParameterList);
- if (LP.Callable.Type == CT_Function) {
- StringRef SourceTokens = LP.Callable.SourceTokens;
- SourceTokens.consume_front("&");
- Stream << " { return " << SourceTokens;
- } else if (LP.Callable.Type == CT_MemberFunction) {
- const auto *MethodDecl = dyn_cast<CXXMethodDecl>(LP.Callable.Decl);
- const BindArgument &ObjPtr = FunctionCallArgs.front();
- Stream << " { ";
- if (!isa<CXXThisExpr>(ignoreTemporariesAndPointers(ObjPtr.E))) {
- Stream << ObjPtr.UsageIdentifier;
- Stream << "->";
- }
- Stream << MethodDecl->getName();
- } else {
- Stream << " { return ";
- switch (LP.Callable.CE) {
- case CE_Var:
- if (LP.Callable.UsageIdentifier != LP.Callable.CaptureIdentifier) {
- Stream << "(" << LP.Callable.UsageIdentifier << ")";
- break;
- }
- [[fallthrough]];
- case CE_InitExpression:
- Stream << LP.Callable.UsageIdentifier;
- break;
- default:
- Stream << getSourceTextForExpr(Result, Ref);
- }
- }
- Stream << "(";
- addFunctionCallArgs(getForwardedArgumentList(LP), Stream);
- Stream << "); }";
- Diag << FixItHint::CreateReplacement(MatchedDecl->getSourceRange(),
- Stream.str());
- }
- } // namespace clang::tidy::modernize
|