summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clang-tidy
diff options
context:
space:
mode:
authorZachary Turner <zturner@roblox.com>2019-11-20 11:27:14 -0800
committerZachary Turner <zturner@roblox.com>2019-12-02 15:36:26 -0800
commit64f74bf72eb484aa32e1104050cb54745116decf (patch)
tree16787356f6c7c67b23b603098880a12eb55fca28 /clang-tools-extra/clang-tidy
parent87f146767ed709f6e354fe46f325c5b6848ad428 (diff)
downloadbcm5719-llvm-64f74bf72eb484aa32e1104050cb54745116decf.tar.gz
bcm5719-llvm-64f74bf72eb484aa32e1104050cb54745116decf.zip
[clang-tidy] Rewrite modernize-avoid-bind check.
This represents largely a full re-write of modernize-avoid-bind, adding significant new functionality in the process. In particular: * Both boost::bind and std::bind are now supported * Function objects are supported in addition to functions * Member functions are supported * Nested calls are supported using capture-init syntax * std::ref() and boost::ref() are now recognized, and will capture by reference. * Rather than capturing with a global =, we now build up an individual capture list that is both necessary and sufficient for the call. * Fixits are supported in a much larger variety of scenarios than before. All previous tests pass under the re-write, but a large number of new tests have been added as well. Differential Revision: https://reviews.llvm.org/D70368
Diffstat (limited to 'clang-tools-extra/clang-tidy')
-rw-r--r--clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.cpp598
-rw-r--r--clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.h6
2 files changed, 544 insertions, 60 deletions
diff --git a/clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.cpp b/clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.cpp
index 2d4475c991c..d1994073bd0 100644
--- a/clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.cpp
@@ -14,11 +14,12 @@
#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/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
+#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/Regex.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
@@ -34,45 +35,270 @@ namespace modernize {
namespace {
enum BindArgumentKind { BK_Temporary, BK_Placeholder, BK_CallExpr, BK_Other };
+enum CaptureMode { CM_None, CM_ByRef, CM_ByValue, CM_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 {
- StringRef Tokens;
+ // 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 CaptureMode = CM_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 CaptureMode = CM_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 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;
+ B.CaptureMode = CM_ByRef;
+ B.UsageIdentifier = getSourceTextForExpr(Result, CE->getArg(0));
+ } else {
+ B.Kind = BK_CallExpr;
+ B.CaptureMode = CM_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.CaptureMode = CM_ByValue;
+ B.UsageIdentifier = 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)) {
+ B.CaptureMode = CM_ByValue;
+ B.UsageIdentifier = 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;
+
+ B.CaptureMode = CM_ByValue;
+ B.UsageIdentifier = getSourceTextForExpr(Result, E);
+ B.CaptureIdentifier = "this";
+ return true;
+}
+
static SmallVector<BindArgument, 4>
-buildBindArguments(const MatchFinder::MatchResult &Result, const CallExpr *C) {
+buildBindArguments(const MatchFinder::MatchResult &Result,
+ const CallableInfo &Callable) {
SmallVector<BindArgument, 4> BindArguments;
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.
- for (size_t I = 1, ArgCount = C->getNumArgs(); I < ArgCount; ++I) {
- const Expr *E = C->getArg(I);
- BindArgument B;
- if (const auto *M = dyn_cast<MaterializeTemporaryExpr>(E)) {
- const auto *TE = M->getSubExpr();
- B.Kind = isa<CallExpr>(TE) ? BK_CallExpr : BK_Temporary;
- }
+ 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);
- B.Tokens = Lexer::getSourceText(
- CharSourceRange::getTokenRange(E->getBeginLoc(), E->getEndLoc()),
- *Result.SourceManager, Result.Context->getLangOpts());
+ if (!Callable.Decl || ArgIndex < Callable.Decl->getNumParams() ||
+ IsObjectPtr)
+ B.IsUsed = true;
SmallVector<StringRef, 2> Matches;
- if (B.Kind == BK_Other && MatchPlaceholder.match(B.Tokens, &Matches)) {
+ if (MatchPlaceholder.match(B.SourceTokens, &Matches)) {
B.Kind = BK_Placeholder;
B.PlaceHolderIndex = std::stoi(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.CaptureMode = CM_InitExpression;
+ B.UsageIdentifier = "ObjectPtr";
+ B.CaptureIdentifier = B.UsageIdentifier;
+ } else if (anyDescendantIsLocal(B.E)) {
+ B.CaptureMode = CM_InitExpression;
+ B.CaptureIdentifier = "capture" + llvm::utostr(CaptureIndex++);
+ B.UsageIdentifier = B.CaptureIdentifier;
}
- BindArguments.push_back(B);
}
return BindArguments;
}
-static void addPlaceholderArgs(const ArrayRef<BindArgument> Args,
- llvm::raw_ostream &Stream) {
+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;
+
auto MaxPlaceholderIt =
std::max_element(Args.begin(), Args.end(),
[](const BindArgument &B1, const BindArgument &B2) {
@@ -80,27 +306,41 @@ static void addPlaceholderArgs(const ArrayRef<BindArgument> Args,
});
// Placeholders (if present) have index 1 or greater.
- if (MaxPlaceholderIt == Args.end() || MaxPlaceholderIt->PlaceHolderIndex == 0)
+ 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 && arg" << 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(const ArrayRef<BindArgument> Args,
+static void addFunctionCallArgs(ArrayRef<BindArgument> Args,
llvm::raw_ostream &Stream) {
StringRef Delimiter = "";
- for (const auto &B : Args) {
- if (B.PlaceHolderIndex)
- Stream << Delimiter << "arg" << B.PlaceHolderIndex;
- else
- Stream << Delimiter << B.Tokens;
+
+ for (int I = 0, Size = Args.size(); I < Size; ++I) {
+ const BindArgument &B = Args[I];
+
+ Stream << Delimiter;
+
+ if (B.Kind == BK_Placeholder || B.CaptureMode != CM_None)
+ Stream << B.UsageIdentifier;
+ else if (B.CaptureMode == CM_None)
+ Stream << B.SourceTokens;
+
Delimiter = ", ";
}
}
@@ -116,59 +356,301 @@ static bool isPlaceHolderIndexRepeated(const ArrayRef<BindArgument> Args) {
return false;
}
+static std::vector<const CXXMethodDecl *>
+findCandidateCallOperators(const CXXRecordDecl *RecordDecl, size_t NumArgs) {
+ std::vector<const CXXMethodDecl *> 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);
+ }
+
+ 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 CXXMethodDecl *> 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);
+
+ if (isa<CallExpr>(NoTemporaries))
+ return CMK_CallExpression;
+
+ if (isa<CXXFunctionalCastExpr>(NoTemporaries) ||
+ isa<CXXConstructExpr>(NoTemporaries))
+ return CMK_Function;
+
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(NoTemporaries)) {
+ if (const auto *FD = dyn_cast<FunctionDecl>(DRE->getDecl()))
+ return CMK_Function;
+ if (const auto *VD = dyn_cast<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 = dyn_cast<FunctionDecl>(Bind->getCalleeDecl());
+ const auto *NS =
+ dyn_cast<NamespaceDecl>(Decl->getEnclosingNamespaceContext());
+ while (NS->isInlineNamespace())
+ NS = dyn_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.CaptureMode = CM_ByValue;
+ LP.Callable.UsageIdentifier = getSourceTextForExpr(Result, CalleeExpr);
+ LP.Callable.CaptureIdentifier =
+ getSourceTextForExpr(Result, ignoreTemporariesAndPointers(CalleeExpr));
+ } else if (LP.Callable.Materialization == CMK_CallExpression) {
+ LP.Callable.CaptureMode = CM_InitExpression;
+ 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, 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 (CM == CM_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.CaptureMode,
+ LP.Callable.CaptureIdentifier,
+ LP.Callable.CaptureInitializer, Stream);
+
+ for (const BindArgument &B : LP.BindArguments) {
+ if (B.CaptureMode == CM_None || !B.IsUsed)
+ continue;
+
+ StringRef Delimiter = AnyCapturesEmitted ? ", " : "";
+
+ if (emitCapture(CaptureSet, Delimiter, B.CaptureMode, B.CaptureIdentifier,
+ B.SourceTokens, Stream))
+ AnyCapturesEmitted = true;
+ }
+}
+
+static ArrayRef<BindArgument>
+getForwardedArgumentList(const LambdaProperties &P) {
+ ArrayRef<BindArgument> Args = makeArrayRef(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", 0) != 0) {}
+
void AvoidBindCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus14) // Need C++14 for generic lambdas.
return;
Finder->addMatcher(
callExpr(
- callee(namedDecl(hasName("::std::bind"))),
- hasArgument(0, declRefExpr(to(functionDecl().bind("f"))).bind("ref")))
+ callee(namedDecl(
+ anyOf(hasName("::boost::bind"), hasName("::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");
- auto Diag = diag(MatchedDecl->getBeginLoc(), "prefer a lambda to std::bind");
-
- const auto Args = buildBindArguments(Result, MatchedDecl);
- // Do not attempt to create fixits for nested call expressions.
- // FIXME: Create lambda capture variables to capture output of calls.
- // NOTE: Supporting nested std::bind will be more difficult due to placeholder
- // sharing between outer and inner std:bind invocations.
- if (llvm::any_of(Args,
- [](const BindArgument &B) { return B.Kind == BK_CallExpr; }))
- return;
-
- // 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))
+ 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 *F = Result.Nodes.getNodeAs<FunctionDecl>("f");
-
- // std::bind can support argument count mismatch between its arguments and the
- // bound function's arguments. Do not attempt to generate a fixit for such
- // cases.
- // FIXME: Support this case by creating unused lambda capture variables.
- if (F->getNumParams() != Args.size())
- return;
+ const auto *Ref = Result.Nodes.getNodeAs<Expr>("ref");
std::string Buffer;
llvm::raw_string_ostream Stream(Buffer);
- bool HasCapturedArgument = llvm::any_of(
- Args, [](const BindArgument &B) { return B.Kind == BK_Other; });
- const auto *Ref = Result.Nodes.getNodeAs<DeclRefExpr>("ref");
- Stream << "[" << (HasCapturedArgument ? "=" : "") << "]";
- addPlaceholderArgs(Args, Stream);
- Stream << " { return ";
- Ref->printPretty(Stream, nullptr, Result.Context->getPrintingPolicy());
+ Stream << "[";
+ emitCaptureList(LP, Result, Stream);
+ Stream << "]";
+
+ ArrayRef<BindArgument> FunctionCallArgs = makeArrayRef(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.CaptureMode) {
+ case CM_ByValue:
+ case CM_ByRef:
+ if (LP.Callable.UsageIdentifier != LP.Callable.CaptureIdentifier) {
+ Stream << "(" << LP.Callable.UsageIdentifier << ")";
+ break;
+ }
+ LLVM_FALLTHROUGH;
+ case CM_InitExpression:
+ Stream << LP.Callable.UsageIdentifier;
+ break;
+ default:
+ Ref->printPretty(Stream, nullptr, Result.Context->getPrintingPolicy());
+ }
+ }
+
Stream << "(";
- addFunctionCallArgs(Args, Stream);
+
+ addFunctionCallArgs(getForwardedArgumentList(LP), Stream);
Stream << "); }";
Diag << FixItHint::CreateReplacement(MatchedDecl->getSourceRange(),
diff --git a/clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.h b/clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.h
index 4b393303b7e..5576fe6c3bd 100644
--- a/clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.h
+++ b/clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.h
@@ -23,10 +23,12 @@ namespace modernize {
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-avoid-std-bind.html
class AvoidBindCheck : public ClangTidyCheck {
public:
- AvoidBindCheck(StringRef Name, ClangTidyContext *Context)
- : ClangTidyCheck(Name, Context) {}
+ AvoidBindCheck(StringRef Name, ClangTidyContext *Context);
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ bool PermissiveParameterList = false;
};
} // namespace modernize
} // namespace tidy
OpenPOWER on IntegriCloud