clang-tools  10.0.0git
AvoidBindCheck.cpp
Go to the documentation of this file.
1 //===--- AvoidBindCheck.cpp - clang-tidy-----------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "AvoidBindCheck.h"
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"
25 #include <algorithm>
26 #include <cstddef>
27 #include <string>
28 
29 using namespace clang::ast_matchers;
30 
31 namespace clang {
32 namespace tidy {
33 namespace modernize {
34 
35 namespace {
36 
37 enum BindArgumentKind { BK_Temporary, BK_Placeholder, BK_CallExpr, BK_Other };
38 enum CaptureMode { CM_None, CM_ByRef, CM_ByValue, CM_InitExpression };
39 
41  CT_Other, // unknown
42  CT_Function, // global or static function
43  CT_MemberFunction, // member function with implicit this
44  CT_Object, // object with operator()
45 };
46 
48  CMK_Other, // unknown
49  CMK_Function, // callable is the name of a member or non-member function.
50  CMK_VariableRef, // callable is a simple expression involving a global or
51  // local variable.
52  CMK_CallExpression, // callable is obtained as the result of a call expression
53 };
54 
55 struct BindArgument {
56  // A rough classification of the type of expression this argument was.
57  BindArgumentKind Kind = BK_Other;
58 
59  // If this argument required a capture, a value indicating how it was
60  // captured.
61  CaptureMode CM = CM_None;
62 
63  // The exact spelling of this argument in the source code.
64  StringRef SourceTokens;
65 
66  // The identifier of the variable within the capture list. This may be
67  // different from UsageIdentifier for example in the expression *d, where the
68  // variable is captured as d, but referred to as *d.
69  std::string CaptureIdentifier;
70 
71  // If this is a placeholder or capture init expression, contains the tokens
72  // used to refer to this parameter from within the body of the lambda.
73  std::string UsageIdentifier;
74 
75  // If Kind == BK_Placeholder, the index of the placeholder.
76  size_t PlaceHolderIndex = 0;
77 
78  // True if the argument is used inside the lambda, false otherwise.
79  bool IsUsed = false;
80 
81  // The actual Expr object representing this expression.
82  const Expr *E = nullptr;
83 };
84 
85 struct CallableInfo {
86  CallableType Type = CT_Other;
87  CallableMaterializationKind Materialization = CMK_Other;
88  CaptureMode CM = CM_None;
89  StringRef SourceTokens;
90  std::string CaptureIdentifier;
91  std::string UsageIdentifier;
92  StringRef CaptureInitializer;
93  const FunctionDecl *Decl = nullptr;
94 };
95 
96 struct LambdaProperties {
97  CallableInfo Callable;
98  SmallVector<BindArgument, 4> BindArguments;
99  StringRef BindNamespace;
100  bool IsFixitSupported = false;
101 };
102 
103 } // end namespace
104 
105 static const Expr *ignoreTemporariesAndPointers(const Expr *E) {
106  if (const auto *T = dyn_cast<UnaryOperator>(E))
107  return ignoreTemporariesAndPointers(T->getSubExpr());
108 
109  const Expr *F = E->IgnoreImplicit();
110  if (E != F)
112 
113  return E;
114 }
115 
116 static const Expr *ignoreTemporariesAndConstructors(const Expr *E) {
117  if (const auto *T = dyn_cast<CXXConstructExpr>(E))
118  return ignoreTemporariesAndConstructors(T->getArg(0));
119 
120  const Expr *F = E->IgnoreImplicit();
121  if (E != F)
123 
124  return E;
125 }
126 
127 static StringRef getSourceTextForExpr(const MatchFinder::MatchResult &Result,
128  const Expr *E) {
129  return Lexer::getSourceText(
130  CharSourceRange::getTokenRange(E->getBeginLoc(), E->getEndLoc()),
131  *Result.SourceManager, Result.Context->getLangOpts());
132 }
133 
134 static bool isCallExprNamed(const Expr *E, StringRef Name) {
135  const auto *CE = dyn_cast<CallExpr>(E->IgnoreImplicit());
136  if (!CE)
137  return false;
138  const auto *ND = dyn_cast<NamedDecl>(CE->getCalleeDecl());
139  if (!ND)
140  return false;
141  return ND->getQualifiedNameAsString() == Name;
142 }
143 
144 static void
145 initializeBindArgumentForCallExpr(const MatchFinder::MatchResult &Result,
146  BindArgument &B, const CallExpr *CE,
147  unsigned &CaptureIndex) {
148  // std::ref(x) means to capture x by reference.
149  if (isCallExprNamed(CE, "boost::ref") || isCallExprNamed(CE, "std::ref")) {
150  B.Kind = BK_Other;
151  B.CM = CM_ByRef;
152  B.UsageIdentifier = getSourceTextForExpr(Result, CE->getArg(0));
153  } else {
154  B.Kind = BK_CallExpr;
155  B.CM = CM_InitExpression;
156  B.UsageIdentifier = "capture" + llvm::utostr(CaptureIndex++);
157  }
158  B.CaptureIdentifier = B.UsageIdentifier;
159 }
160 
161 static bool anyDescendantIsLocal(const Stmt *Statement) {
162  if (const auto *DeclRef = dyn_cast<DeclRefExpr>(Statement)) {
163  const ValueDecl *Decl = DeclRef->getDecl();
164  if (const auto *Var = dyn_cast_or_null<VarDecl>(Decl)) {
165  if (Var->isLocalVarDeclOrParm())
166  return true;
167  }
168  } else if (isa<CXXThisExpr>(Statement))
169  return true;
170 
171  return any_of(Statement->children(), anyDescendantIsLocal);
172 }
173 
174 static bool tryCaptureAsLocalVariable(const MatchFinder::MatchResult &Result,
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()))
178  return tryCaptureAsLocalVariable(Result, B, CE->getArg(0));
179  return false;
180  }
181 
182  const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreImplicit());
183  if (!DRE)
184  return false;
185 
186  const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
187  if (!VD || !VD->isLocalVarDeclOrParm())
188  return false;
189 
190  B.CM = CM_ByValue;
191  B.UsageIdentifier = getSourceTextForExpr(Result, E);
192  B.CaptureIdentifier = B.UsageIdentifier;
193  return true;
194 }
195 
196 static bool tryCaptureAsMemberVariable(const MatchFinder::MatchResult &Result,
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()))
200  return tryCaptureAsMemberVariable(Result, B, CE->getArg(0));
201  return false;
202  }
203 
204  E = E->IgnoreImplicit();
205  if (isa<CXXThisExpr>(E)) {
206  B.CM = CM_ByValue;
207  B.UsageIdentifier = getSourceTextForExpr(Result, E);
208  B.CaptureIdentifier = "this";
209  return true;
210  }
211 
212  const auto *ME = dyn_cast<MemberExpr>(E);
213  if (!ME)
214  return false;
215 
216  if (!ME->isLValue() || !isa<FieldDecl>(ME->getMemberDecl()))
217  return false;
218 
219  B.CM = CM_ByValue;
220  B.UsageIdentifier = getSourceTextForExpr(Result, E);
221  B.CaptureIdentifier = "this";
222  return true;
223 }
224 
225 static SmallVector<BindArgument, 4>
226 buildBindArguments(const MatchFinder::MatchResult &Result,
227  const CallableInfo &Callable) {
228  SmallVector<BindArgument, 4> BindArguments;
229  llvm::Regex MatchPlaceholder("^_([0-9]+)$");
230 
231  const auto *BindCall = Result.Nodes.getNodeAs<CallExpr>("bind");
232 
233  // Start at index 1 as first argument to bind is the function name.
234  unsigned CaptureIndex = 0;
235  for (size_t I = 1, ArgCount = BindCall->getNumArgs(); I < ArgCount; ++I) {
236 
237  const Expr *E = BindCall->getArg(I);
238  BindArgument &B = BindArguments.emplace_back();
239 
240  size_t ArgIndex = I - 1;
241  if (Callable.Type == CT_MemberFunction)
242  --ArgIndex;
243 
244  bool IsObjectPtr = (I == 1 && Callable.Type == CT_MemberFunction);
245  B.E = E;
246  B.SourceTokens = getSourceTextForExpr(Result, E);
247 
248  if (!Callable.Decl || ArgIndex < Callable.Decl->getNumParams() ||
249  IsObjectPtr)
250  B.IsUsed = true;
251 
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;
258  continue;
259  }
260 
261  if (const auto *CE =
262  dyn_cast<CallExpr>(ignoreTemporariesAndConstructors(E))) {
263  initializeBindArgumentForCallExpr(Result, B, CE, CaptureIndex);
264  continue;
265  }
266 
267  if (tryCaptureAsLocalVariable(Result, B, B.E) ||
268  tryCaptureAsMemberVariable(Result, B, B.E))
269  continue;
270 
271  // If it's not something we recognize, capture it by init expression to be
272  // safe.
273  B.Kind = BK_Other;
274  if (IsObjectPtr) {
275  B.CM = CM_InitExpression;
276  B.UsageIdentifier = "ObjectPtr";
277  B.CaptureIdentifier = B.UsageIdentifier;
278  } else if (anyDescendantIsLocal(B.E)) {
279  B.CM = CM_InitExpression;
280  B.CaptureIdentifier = "capture" + llvm::utostr(CaptureIndex++);
281  B.UsageIdentifier = B.CaptureIdentifier;
282  }
283  }
284  return BindArguments;
285 }
286 
287 static int findPositionOfPlaceholderUse(ArrayRef<BindArgument> Args,
288  size_t PlaceholderIndex) {
289  for (size_t I = 0; I < Args.size(); ++I)
290  if (Args[I].PlaceHolderIndex == PlaceholderIndex)
291  return I;
292 
293  return -1;
294 }
295 
296 static void addPlaceholderArgs(const LambdaProperties &LP,
297  llvm::raw_ostream &Stream,
298  bool PermissiveParameterList) {
299 
300  ArrayRef<BindArgument> Args = LP.BindArguments;
301 
302  auto MaxPlaceholderIt =
303  std::max_element(Args.begin(), Args.end(),
304  [](const BindArgument &B1, const BindArgument &B2) {
305  return B1.PlaceHolderIndex < B2.PlaceHolderIndex;
306  });
307 
308  // Placeholders (if present) have index 1 or greater.
309  if (!PermissiveParameterList && (MaxPlaceholderIt == Args.end() ||
310  MaxPlaceholderIt->PlaceHolderIndex == 0))
311  return;
312 
313  size_t PlaceholderCount = MaxPlaceholderIt->PlaceHolderIndex;
314  Stream << "(";
315  StringRef Delimiter = "";
316  for (size_t I = 1; I <= PlaceholderCount; ++I) {
317  Stream << Delimiter << "auto &&";
318 
319  int ArgIndex = findPositionOfPlaceholderUse(Args, I);
320 
321  if (ArgIndex != -1 && Args[ArgIndex].IsUsed)
322  Stream << " " << Args[ArgIndex].UsageIdentifier;
323  Delimiter = ", ";
324  }
325  if (PermissiveParameterList)
326  Stream << Delimiter << "auto && ...";
327  Stream << ")";
328 }
329 
330 static void addFunctionCallArgs(ArrayRef<BindArgument> Args,
331  llvm::raw_ostream &Stream) {
332  StringRef Delimiter = "";
333 
334  for (int I = 0, Size = Args.size(); I < Size; ++I) {
335  const BindArgument &B = Args[I];
336 
337  Stream << Delimiter;
338 
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;
343 
344  Delimiter = ", ";
345  }
346 }
347 
348 static bool isPlaceHolderIndexRepeated(const ArrayRef<BindArgument> Args) {
349  llvm::SmallSet<size_t, 4> PlaceHolderIndices;
350  for (const BindArgument &B : Args) {
351  if (B.PlaceHolderIndex) {
352  if (!PlaceHolderIndices.insert(B.PlaceHolderIndex).second)
353  return true;
354  }
355  }
356  return false;
357 }
358 
359 static std::vector<const CXXMethodDecl *>
360 findCandidateCallOperators(const CXXRecordDecl *RecordDecl, size_t NumArgs) {
361  std::vector<const CXXMethodDecl *> Candidates;
362 
363  for (const clang::CXXMethodDecl *Method : RecordDecl->methods()) {
364  OverloadedOperatorKind OOK = Method->getOverloadedOperator();
365 
366  if (OOK != OverloadedOperatorKind::OO_Call)
367  continue;
368 
369  if (Method->getNumParams() > NumArgs)
370  continue;
371 
372  Candidates.push_back(Method);
373  }
374 
375  return Candidates;
376 }
377 
378 static bool isFixitSupported(const CallableInfo &Callee,
379  ArrayRef<BindArgument> Args) {
380  // Do not attempt to create fixits for nested std::bind or std::ref.
381  // Supporting nested std::bind will be more difficult due to placeholder
382  // sharing between outer and inner std::bind invocations, and std::ref
383  // requires us to capture some parameters by reference instead of by value.
384  if (any_of(Args, [](const BindArgument &B) {
385  return isCallExprNamed(B.E, "boost::bind") ||
386  isCallExprNamed(B.E, "std::bind");
387  })) {
388  return false;
389  }
390 
391  // Do not attempt to create fixits when placeholders are reused.
392  // Unused placeholders are supported by requiring C++14 generic lambdas.
393  // FIXME: Support this case by deducing the common type.
394  if (isPlaceHolderIndexRepeated(Args))
395  return false;
396 
397  // If we can't determine the Decl being used, don't offer a fixit.
398  if (!Callee.Decl)
399  return false;
400 
401  if (Callee.Type == CT_Other || Callee.Materialization == CMK_Other)
402  return false;
403 
404  return true;
405 }
406 
407 const FunctionDecl *getCallOperator(const CXXRecordDecl *Callable,
408  size_t NumArgs) {
409  std::vector<const CXXMethodDecl *> Candidates =
410  findCandidateCallOperators(Callable, NumArgs);
411  if (Candidates.size() != 1)
412  return nullptr;
413 
414  return Candidates.front();
415 }
416 
417 const FunctionDecl *
418 getCallMethodDecl(const MatchFinder::MatchResult &Result, CallableType Type,
419  CallableMaterializationKind Materialization) {
420 
421  const Expr *Callee = Result.Nodes.getNodeAs<Expr>("ref");
422  const Expr *CallExpression = ignoreTemporariesAndPointers(Callee);
423 
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);
428  }
429 
430  if (Materialization == CMK_Function) {
431  if (const auto *DRE = dyn_cast<DeclRefExpr>(CallExpression))
432  return dyn_cast<FunctionDecl>(DRE->getDecl());
433  }
434 
435  // Maybe this is an indirect call through a function pointer or something
436  // where we can't determine the exact decl.
437  return nullptr;
438 }
439 
440 static CallableType getCallableType(const MatchFinder::MatchResult &Result) {
441  const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>("ref");
442 
443  QualType QT = CallableExpr->getType();
444  if (QT->isMemberFunctionPointerType())
445  return CT_MemberFunction;
446 
447  if (QT->isFunctionPointerType() || QT->isFunctionReferenceType() ||
448  QT->isFunctionType())
449  return CT_Function;
450 
451  if (QT->isRecordType()) {
452  const CXXRecordDecl *Decl = QT->getAsCXXRecordDecl();
453  if (!Decl)
454  return CT_Other;
455 
456  return CT_Object;
457  }
458 
459  return CT_Other;
460 }
461 
462 static CallableMaterializationKind
463 getCallableMaterialization(const MatchFinder::MatchResult &Result) {
464  const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>("ref");
465 
466  const auto *NoTemporaries = ignoreTemporariesAndPointers(CallableExpr);
467 
468  if (isa<CallExpr>(NoTemporaries))
469  return CMK_CallExpression;
470 
471  if (isa<CXXFunctionalCastExpr>(NoTemporaries) ||
472  isa<CXXConstructExpr>(NoTemporaries))
473  return CMK_Function;
474 
475  if (const auto *DRE = dyn_cast<DeclRefExpr>(NoTemporaries)) {
476  if (isa<FunctionDecl>(DRE->getDecl()))
477  return CMK_Function;
478  if (isa<VarDecl>(DRE->getDecl()))
479  return CMK_VariableRef;
480  }
481 
482  return CMK_Other;
483 }
484 
485 static LambdaProperties
486 getLambdaProperties(const MatchFinder::MatchResult &Result) {
487  const auto *CalleeExpr = Result.Nodes.getNodeAs<Expr>("ref");
488 
489  LambdaProperties LP;
490 
491  const auto *Bind = Result.Nodes.getNodeAs<CallExpr>("bind");
492  const auto *Decl = dyn_cast<FunctionDecl>(Bind->getCalleeDecl());
493  const auto *NS =
494  dyn_cast<NamespaceDecl>(Decl->getEnclosingNamespaceContext());
495  while (NS->isInlineNamespace())
496  NS = dyn_cast<NamespaceDecl>(NS->getDeclContext());
497  LP.BindNamespace = NS->getName();
498 
499  LP.Callable.Type = getCallableType(Result);
500  LP.Callable.Materialization = getCallableMaterialization(Result);
501  LP.Callable.Decl =
502  getCallMethodDecl(Result, LP.Callable.Type, LP.Callable.Materialization);
503  LP.Callable.SourceTokens = getSourceTextForExpr(Result, CalleeExpr);
504  if (LP.Callable.Materialization == CMK_VariableRef) {
505  LP.Callable.CM = CM_ByValue;
506  LP.Callable.UsageIdentifier = getSourceTextForExpr(Result, CalleeExpr);
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";
513  LP.Callable.CaptureInitializer = getSourceTextForExpr(Result, CalleeExpr);
514  }
515 
516  LP.BindArguments = buildBindArguments(Result, LP.Callable);
517 
518  LP.IsFixitSupported = isFixitSupported(LP.Callable, LP.BindArguments);
519 
520  return LP;
521 }
522 
523 static bool emitCapture(llvm::StringSet<> &CaptureSet, StringRef Delimiter,
524  CaptureMode CM, StringRef Identifier,
525  StringRef InitExpression, raw_ostream &Stream) {
526  if (CM == CM_None)
527  return false;
528 
529  // This capture has already been emitted.
530  if (CaptureSet.count(Identifier) != 0)
531  return false;
532 
533  Stream << Delimiter;
534 
535  if (CM == CM_ByRef)
536  Stream << "&";
537  Stream << Identifier;
538  if (CM == CM_InitExpression)
539  Stream << " = " << InitExpression;
540 
541  CaptureSet.insert(Identifier);
542  return true;
543 }
544 
545 static void emitCaptureList(const LambdaProperties &LP,
546  const MatchFinder::MatchResult &Result,
547  raw_ostream &Stream) {
548  llvm::StringSet<> CaptureSet;
549  bool AnyCapturesEmitted = false;
550 
551  AnyCapturesEmitted = emitCapture(CaptureSet, "", LP.Callable.CM,
552  LP.Callable.CaptureIdentifier,
553  LP.Callable.CaptureInitializer, Stream);
554 
555  for (const BindArgument &B : LP.BindArguments) {
556  if (B.CM == CM_None || !B.IsUsed)
557  continue;
558 
559  StringRef Delimiter = AnyCapturesEmitted ? ", " : "";
560 
561  if (emitCapture(CaptureSet, Delimiter, B.CM, B.CaptureIdentifier,
562  B.SourceTokens, Stream))
563  AnyCapturesEmitted = true;
564  }
565 }
566 
567 static ArrayRef<BindArgument>
568 getForwardedArgumentList(const LambdaProperties &P) {
569  ArrayRef<BindArgument> Args = makeArrayRef(P.BindArguments);
570  if (P.Callable.Type != CT_MemberFunction)
571  return Args;
572 
573  return Args.drop_front();
574 }
575 AvoidBindCheck::AvoidBindCheck(StringRef Name, ClangTidyContext *Context)
576  : ClangTidyCheck(Name, Context),
577  PermissiveParameterList(Options.get("PermissiveParameterList", 0) != 0) {}
578 
579 void AvoidBindCheck::registerMatchers(MatchFinder *Finder) {
580  if (!getLangOpts().CPlusPlus14) // Need C++14 for generic lambdas.
581  return;
582 
583  Finder->addMatcher(
584  callExpr(
585  callee(namedDecl(
586  anyOf(hasName("::boost::bind"), hasName("::std::bind")))),
587  hasArgument(
588  0, anyOf(expr(hasType(memberPointerType())).bind("ref"),
589  expr(hasParent(materializeTemporaryExpr().bind("ref"))),
590  expr().bind("ref"))))
591  .bind("bind"),
592  this);
593 }
594 
595 void AvoidBindCheck::check(const MatchFinder::MatchResult &Result) {
596  const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>("bind");
597 
598  LambdaProperties LP = getLambdaProperties(Result);
599  auto Diag =
600  diag(MatchedDecl->getBeginLoc(),
601  formatv("prefer a lambda to {0}::bind", LP.BindNamespace).str());
602  if (!LP.IsFixitSupported)
603  return;
604 
605  const auto *Ref = Result.Nodes.getNodeAs<Expr>("ref");
606 
607  std::string Buffer;
608  llvm::raw_string_ostream Stream(Buffer);
609 
610  Stream << "[";
611  emitCaptureList(LP, Result, Stream);
612  Stream << "]";
613 
614  ArrayRef<BindArgument> FunctionCallArgs = makeArrayRef(LP.BindArguments);
615 
616  addPlaceholderArgs(LP, Stream, PermissiveParameterList);
617 
618  if (LP.Callable.Type == CT_Function) {
619  StringRef SourceTokens = LP.Callable.SourceTokens;
620  SourceTokens.consume_front("&");
621  Stream << " { return " << SourceTokens;
622  } else if (LP.Callable.Type == CT_MemberFunction) {
623  const auto *MethodDecl = dyn_cast<CXXMethodDecl>(LP.Callable.Decl);
624  const BindArgument &ObjPtr = FunctionCallArgs.front();
625 
626  Stream << " { ";
627  if (!isa<CXXThisExpr>(ignoreTemporariesAndPointers(ObjPtr.E))) {
628  Stream << ObjPtr.UsageIdentifier;
629  Stream << "->";
630  }
631 
632  Stream << MethodDecl->getName();
633  } else {
634  Stream << " { return ";
635  switch (LP.Callable.CM) {
636  case CM_ByValue:
637  case CM_ByRef:
638  if (LP.Callable.UsageIdentifier != LP.Callable.CaptureIdentifier) {
639  Stream << "(" << LP.Callable.UsageIdentifier << ")";
640  break;
641  }
642  LLVM_FALLTHROUGH;
643  case CM_InitExpression:
644  Stream << LP.Callable.UsageIdentifier;
645  break;
646  default:
647  Ref->printPretty(Stream, nullptr, Result.Context->getPrintingPolicy());
648  }
649  }
650 
651  Stream << "(";
652 
654  Stream << "); }";
655 
656  Diag << FixItHint::CreateReplacement(MatchedDecl->getSourceRange(),
657  Stream.str());
658 }
659 
660 } // namespace modernize
661 } // namespace tidy
662 } // namespace clang
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)
StringRef CaptureInitializer
CallableInfo Callable
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)
bool IsFixitSupported
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)
BindArgumentKind Kind
static bool tryCaptureAsMemberVariable(const MatchFinder::MatchResult &Result, BindArgument &B, const Expr *E)
std::string UsageIdentifier
static ArrayRef< BindArgument > getForwardedArgumentList(const LambdaProperties &P)
StringRef BindNamespace
const FunctionDecl * getCallMethodDecl(const MatchFinder::MatchResult &Result, CallableType Type, CallableMaterializationKind Materialization)
StringRef SourceTokens
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.
Definition: SourceCode.cpp:227
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
CallableType Type
static StringRef getSourceTextForExpr(const MatchFinder::MatchResult &Result, const Expr *E)
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
const Expr * E
CaptureMode CM
static bool isCallExprNamed(const Expr *E, StringRef Name)
bool IsUsed
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&#39;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)
size_t PlaceHolderIndex