AvoidBindCheck.cpp 23 KB

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