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/Support/Casting.h" 22 #include "llvm/Support/FormatVariadic.h" 23 #include "llvm/Support/Regex.h" 24 #include "llvm/Support/raw_ostream.h" 38 enum CaptureMode { CM_None, CM_ByRef, CM_ByValue, CM_InitExpression };
57 BindArgumentKind
Kind = BK_Other;
61 CaptureMode
CM = CM_None;
82 const Expr *
E =
nullptr;
86 CallableType
Type = CT_Other;
88 CaptureMode
CM = CM_None;
93 const FunctionDecl *
Decl =
nullptr;
96 struct LambdaProperties {
106 if (
const auto *T = dyn_cast<UnaryOperator>(E))
109 const Expr *F = E->IgnoreImplicit();
117 if (
const auto *T = dyn_cast<CXXConstructExpr>(E))
120 const Expr *F = E->IgnoreImplicit();
129 return Lexer::getSourceText(
131 *Result.SourceManager, Result.Context->getLangOpts());
135 const auto *CE = dyn_cast<CallExpr>(E->IgnoreImplicit());
138 const auto *ND = dyn_cast<NamedDecl>(CE->getCalleeDecl());
141 return ND->getQualifiedNameAsString() ==
Name;
146 BindArgument &B,
const CallExpr *CE,
147 unsigned &CaptureIndex) {
154 B.Kind = BK_CallExpr;
155 B.CM = CM_InitExpression;
156 B.UsageIdentifier =
"capture" + llvm::utostr(CaptureIndex++);
158 B.CaptureIdentifier = B.UsageIdentifier;
162 if (
const auto *
DeclRef = dyn_cast<DeclRefExpr>(Statement)) {
164 if (
const auto *Var = dyn_cast_or_null<VarDecl>(Decl)) {
165 if (Var->isLocalVarDeclOrParm())
168 }
else if (isa<CXXThisExpr>(Statement))
175 BindArgument &B,
const Expr *
E) {
176 if (
const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E)) {
177 if (
const auto *CE = dyn_cast<CXXConstructExpr>(BTE->getSubExpr()))
182 const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreImplicit());
186 const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
187 if (!VD || !VD->isLocalVarDeclOrParm())
192 B.CaptureIdentifier = B.UsageIdentifier;
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 E = E->IgnoreImplicit();
205 if (isa<CXXThisExpr>(E)) {
208 B.CaptureIdentifier =
"this";
212 const auto *ME = dyn_cast<MemberExpr>(
E);
216 if (!ME->isLValue() || !isa<FieldDecl>(ME->getMemberDecl()))
221 B.CaptureIdentifier =
"this";
225 static SmallVector<BindArgument, 4>
229 llvm::Regex MatchPlaceholder(
"^_([0-9]+)$");
231 const auto *BindCall = Result.Nodes.getNodeAs<CallExpr>(
"bind");
234 unsigned CaptureIndex = 0;
235 for (
size_t I = 1, ArgCount = BindCall->getNumArgs(); I < ArgCount; ++I) {
237 const Expr *
E = BindCall->getArg(I);
238 BindArgument &B = BindArguments.emplace_back();
240 size_t ArgIndex = I - 1;
241 if (Callable.Type == CT_MemberFunction)
244 bool IsObjectPtr = (I == 1 && Callable.Type == CT_MemberFunction);
248 if (!Callable.Decl || ArgIndex < Callable.Decl->getNumParams() ||
252 SmallVector<StringRef, 2> Matches;
253 if (MatchPlaceholder.match(B.SourceTokens, &Matches)) {
254 B.Kind = BK_Placeholder;
255 B.PlaceHolderIndex = std::stoi(Matches[1]);
256 B.UsageIdentifier =
"PH" + llvm::utostr(B.PlaceHolderIndex);
257 B.CaptureIdentifier = B.UsageIdentifier;
275 B.CM = CM_InitExpression;
276 B.UsageIdentifier =
"ObjectPtr";
277 B.CaptureIdentifier = B.UsageIdentifier;
279 B.CM = CM_InitExpression;
280 B.CaptureIdentifier =
"capture" + llvm::utostr(CaptureIndex++);
281 B.UsageIdentifier = B.CaptureIdentifier;
288 size_t PlaceholderIndex) {
289 for (
size_t I = 0; I < Args.size(); ++I)
297 llvm::raw_ostream &Stream,
298 bool PermissiveParameterList) {
300 ArrayRef<BindArgument> Args = LP.BindArguments;
302 auto MaxPlaceholderIt =
303 std::max_element(Args.begin(), Args.end(),
304 [](
const BindArgument &B1,
const BindArgument &B2) {
305 return B1.PlaceHolderIndex < B2.PlaceHolderIndex;
309 if (!PermissiveParameterList && (MaxPlaceholderIt == Args.end() ||
310 MaxPlaceholderIt->PlaceHolderIndex == 0))
313 size_t PlaceholderCount = MaxPlaceholderIt->PlaceHolderIndex;
315 StringRef Delimiter =
"";
316 for (
size_t I = 1; I <= PlaceholderCount; ++I) {
317 Stream << Delimiter <<
"auto &&";
321 if (ArgIndex != -1 && Args[ArgIndex].
IsUsed)
322 Stream <<
" " << Args[ArgIndex].UsageIdentifier;
325 if (PermissiveParameterList)
326 Stream << Delimiter <<
"auto && ...";
331 llvm::raw_ostream &Stream) {
332 StringRef Delimiter =
"";
334 for (
int I = 0, Size = Args.size(); I < Size; ++I) {
335 const BindArgument &B = Args[I];
339 if (B.Kind == BK_Placeholder || B.CM != CM_None)
340 Stream << B.UsageIdentifier;
341 else if (B.CM == CM_None)
342 Stream << B.SourceTokens;
349 llvm::SmallSet<size_t, 4> PlaceHolderIndices;
350 for (
const BindArgument &B : Args) {
351 if (B.PlaceHolderIndex) {
352 if (!PlaceHolderIndices.insert(B.PlaceHolderIndex).second)
359 static std::vector<const CXXMethodDecl *>
361 std::vector<const CXXMethodDecl *> Candidates;
363 for (
const clang::CXXMethodDecl *Method : RecordDecl->methods()) {
364 OverloadedOperatorKind OOK = Method->getOverloadedOperator();
366 if (OOK != OverloadedOperatorKind::OO_Call)
369 if (Method->getNumParams() > NumArgs)
372 Candidates.push_back(Method);
379 ArrayRef<BindArgument> Args) {
384 if (any_of(Args, [](
const BindArgument &B) {
401 if (Callee.Type == CT_Other || Callee.Materialization == CMK_Other)
409 std::vector<const CXXMethodDecl *> Candidates =
411 if (Candidates.size() != 1)
414 return Candidates.front();
421 const Expr *Callee = Result.Nodes.getNodeAs<Expr>(
"ref");
424 if (Type == CT_Object) {
425 const auto *BindCall = Result.Nodes.getNodeAs<CallExpr>(
"bind");
426 size_t NumArgs = BindCall->getNumArgs() - 1;
427 return getCallOperator(Callee->getType()->getAsCXXRecordDecl(), NumArgs);
430 if (Materialization == CMK_Function) {
431 if (
const auto *DRE = dyn_cast<DeclRefExpr>(CallExpression))
432 return dyn_cast<FunctionDecl>(DRE->getDecl());
441 const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>(
"ref");
443 QualType QT = CallableExpr->getType();
444 if (QT->isMemberFunctionPointerType())
445 return CT_MemberFunction;
447 if (QT->isFunctionPointerType() || QT->isFunctionReferenceType() ||
448 QT->isFunctionType())
451 if (QT->isRecordType()) {
452 const CXXRecordDecl *
Decl = QT->getAsCXXRecordDecl();
462 static CallableMaterializationKind
464 const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>(
"ref");
468 if (isa<CallExpr>(NoTemporaries))
469 return CMK_CallExpression;
471 if (isa<CXXFunctionalCastExpr>(NoTemporaries) ||
472 isa<CXXConstructExpr>(NoTemporaries))
475 if (
const auto *DRE = dyn_cast<DeclRefExpr>(NoTemporaries)) {
476 if (isa<FunctionDecl>(DRE->getDecl()))
478 if (isa<VarDecl>(DRE->getDecl()))
479 return CMK_VariableRef;
485 static LambdaProperties
487 const auto *CalleeExpr = Result.Nodes.getNodeAs<Expr>(
"ref");
491 const auto *Bind = Result.Nodes.getNodeAs<CallExpr>(
"bind");
492 const auto *
Decl = dyn_cast<FunctionDecl>(Bind->getCalleeDecl());
494 dyn_cast<NamespaceDecl>(Decl->getEnclosingNamespaceContext());
495 while (NS->isInlineNamespace())
496 NS = dyn_cast<NamespaceDecl>(NS->getDeclContext());
497 LP.BindNamespace = NS->getName();
504 if (LP.Callable.Materialization == CMK_VariableRef) {
505 LP.Callable.CM = CM_ByValue;
507 LP.Callable.CaptureIdentifier =
509 }
else if (LP.Callable.Materialization == CMK_CallExpression) {
510 LP.Callable.CM = CM_InitExpression;
511 LP.Callable.UsageIdentifier =
"Func";
512 LP.Callable.CaptureIdentifier =
"Func";
523 static bool emitCapture(llvm::StringSet<> &CaptureSet, StringRef Delimiter,
524 CaptureMode
CM, StringRef Identifier,
525 StringRef InitExpression, raw_ostream &Stream) {
530 if (CaptureSet.count(Identifier) != 0)
537 Stream << Identifier;
538 if (CM == CM_InitExpression)
539 Stream <<
" = " << InitExpression;
541 CaptureSet.insert(Identifier);
546 const MatchFinder::MatchResult &Result,
547 raw_ostream &Stream) {
548 llvm::StringSet<> CaptureSet;
549 bool AnyCapturesEmitted =
false;
551 AnyCapturesEmitted =
emitCapture(CaptureSet,
"", LP.Callable.CM,
552 LP.Callable.CaptureIdentifier,
553 LP.Callable.CaptureInitializer, Stream);
555 for (
const BindArgument &B : LP.BindArguments) {
556 if (B.CM == CM_None || !B.IsUsed)
559 StringRef Delimiter = AnyCapturesEmitted ?
", " :
"";
561 if (
emitCapture(CaptureSet, Delimiter, B.CM, B.CaptureIdentifier,
562 B.SourceTokens, Stream))
563 AnyCapturesEmitted =
true;
567 static ArrayRef<BindArgument>
569 ArrayRef<BindArgument> Args = makeArrayRef(P.BindArguments);
570 if (P.Callable.Type != CT_MemberFunction)
573 return Args.drop_front();
577 PermissiveParameterList(Options.get(
"PermissiveParameterList", 0) != 0) {}
586 anyOf(hasName(
"::boost::bind"), hasName(
"::std::bind")))),
588 0, anyOf(expr(hasType(memberPointerType())).bind(
"ref"),
589 expr(hasParent(materializeTemporaryExpr().bind(
"ref"))),
590 expr().bind(
"ref"))))
596 const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>(
"bind");
600 diag(MatchedDecl->getBeginLoc(),
601 formatv(
"prefer a lambda to {0}::bind", LP.BindNamespace).str());
602 if (!LP.IsFixitSupported)
605 const auto *Ref = Result.Nodes.getNodeAs<Expr>(
"ref");
608 llvm::raw_string_ostream Stream(Buffer);
614 ArrayRef<BindArgument> FunctionCallArgs = makeArrayRef(LP.BindArguments);
618 if (LP.Callable.Type == CT_Function) {
620 SourceTokens.consume_front(
"&");
622 }
else if (LP.Callable.Type == CT_MemberFunction) {
623 const auto *MethodDecl = dyn_cast<CXXMethodDecl>(LP.Callable.Decl);
624 const BindArgument &ObjPtr = FunctionCallArgs.front();
628 Stream << ObjPtr.UsageIdentifier;
632 Stream << MethodDecl->getName();
634 Stream <<
" { return ";
635 switch (LP.Callable.CM) {
638 if (LP.Callable.UsageIdentifier != LP.Callable.CaptureIdentifier) {
639 Stream <<
"(" << LP.Callable.UsageIdentifier <<
")";
643 case CM_InitExpression:
644 Stream << LP.Callable.UsageIdentifier;
647 Ref->printPretty(Stream,
nullptr, Result.Context->getPrintingPolicy());
656 Diag << FixItHint::CreateReplacement(MatchedDecl->getSourceRange(),
const FunctionDecl * getCallOperator(const CXXRecordDecl *Callable, size_t NumArgs)
static CallableType getCallableType(const MatchFinder::MatchResult &Result)
const FunctionDecl * Decl
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
static void addFunctionCallArgs(ArrayRef< BindArgument > Args, llvm::raw_ostream &Stream)
CallableMaterializationKind
StringRef CaptureInitializer
SmallVector< BindArgument, 4 > BindArguments
static void initializeBindArgumentForCallExpr(const MatchFinder::MatchResult &Result, BindArgument &B, const CallExpr *CE, unsigned &CaptureIndex)
static bool tryCaptureAsLocalVariable(const MatchFinder::MatchResult &Result, BindArgument &B, const Expr *E)
static SmallVector< BindArgument, 4 > buildBindArguments(const MatchFinder::MatchResult &Result, const CallableInfo &Callable)
static const Expr * ignoreTemporariesAndConstructors(const Expr *E)
CallableMaterializationKind Materialization
Base class for all clang-tidy checks.
const LangOptions & getLangOpts() const
Returns the language options from the context.
static bool anyDescendantIsLocal(const Stmt *Statement)
static bool isPlaceHolderIndexRepeated(const ArrayRef< BindArgument > Args)
static void addPlaceholderArgs(const LambdaProperties &LP, llvm::raw_ostream &Stream, bool PermissiveParameterList)
static bool tryCaptureAsMemberVariable(const MatchFinder::MatchResult &Result, BindArgument &B, const Expr *E)
std::string UsageIdentifier
static ArrayRef< BindArgument > getForwardedArgumentList(const LambdaProperties &P)
const FunctionDecl * getCallMethodDecl(const MatchFinder::MatchResult &Result, CallableType Type, CallableMaterializationKind Materialization)
static int findPositionOfPlaceholderUse(ArrayRef< BindArgument > Args, size_t PlaceholderIndex)
static constexpr llvm::StringLiteral Name
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
static LambdaProperties getLambdaProperties(const MatchFinder::MatchResult &Result)
static bool isFixitSupported(const CallableInfo &Callee, ArrayRef< BindArgument > Args)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
static CallableMaterializationKind getCallableMaterialization(const MatchFinder::MatchResult &Result)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::string CaptureIdentifier
static StringRef getSourceTextForExpr(const MatchFinder::MatchResult &Result, const Expr *E)
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
static bool isCallExprNamed(const Expr *E, StringRef Name)
static bool emitCapture(llvm::StringSet<> &CaptureSet, StringRef Delimiter, CaptureMode CM, StringRef Identifier, StringRef InitExpression, raw_ostream &Stream)
const DeclRefExpr * DeclRef
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
static const Expr * ignoreTemporariesAndPointers(const Expr *E)
static void emitCaptureList(const LambdaProperties &LP, const MatchFinder::MatchResult &Result, raw_ostream &Stream)
static std::vector< const CXXMethodDecl * > findCandidateCallOperators(const CXXRecordDecl *RecordDecl, size_t NumArgs)