UseEmplaceCheck.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. //===--- UseEmplaceCheck.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 "UseEmplaceCheck.h"
  9. #include "../utils/OptionsUtils.h"
  10. using namespace clang::ast_matchers;
  11. namespace clang::tidy::modernize {
  12. namespace {
  13. // Identical to hasAnyName, except it does not take template specifiers into
  14. // account. This is used to match the functions names as in
  15. // DefaultEmplacyFunctions below without caring about the template types of the
  16. // containers.
  17. AST_MATCHER_P(NamedDecl, hasAnyNameIgnoringTemplates, std::vector<StringRef>,
  18. Names) {
  19. const std::string FullName = "::" + Node.getQualifiedNameAsString();
  20. // This loop removes template specifiers by only keeping characters not within
  21. // template brackets. We keep a depth count to handle nested templates. For
  22. // example, it'll transform a::b<c<d>>::e<f> to simply a::b::e.
  23. std::string FullNameTrimmed;
  24. int Depth = 0;
  25. for (const auto &Character : FullName) {
  26. if (Character == '<') {
  27. ++Depth;
  28. } else if (Character == '>') {
  29. --Depth;
  30. } else if (Depth == 0) {
  31. FullNameTrimmed.append(1, Character);
  32. }
  33. }
  34. // This loop is taken from HasNameMatcher::matchesNodeFullSlow in
  35. // clang/lib/ASTMatchers/ASTMatchersInternal.cpp and checks whether
  36. // FullNameTrimmed matches any of the given Names.
  37. const StringRef FullNameTrimmedRef = FullNameTrimmed;
  38. for (const StringRef Pattern : Names) {
  39. if (Pattern.startswith("::")) {
  40. if (FullNameTrimmed == Pattern)
  41. return true;
  42. } else if (FullNameTrimmedRef.endswith(Pattern) &&
  43. FullNameTrimmedRef.drop_back(Pattern.size()).endswith("::")) {
  44. return true;
  45. }
  46. }
  47. return false;
  48. }
  49. // Checks if the given matcher is the last argument of the given CallExpr.
  50. AST_MATCHER_P(CallExpr, hasLastArgument,
  51. clang::ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
  52. if (Node.getNumArgs() == 0)
  53. return false;
  54. return InnerMatcher.matches(*Node.getArg(Node.getNumArgs() - 1), Finder,
  55. Builder);
  56. }
  57. // Checks if the given member call has the same number of arguments as the
  58. // function had parameters defined (this is useful to check if there is only one
  59. // variadic argument).
  60. AST_MATCHER(CXXMemberCallExpr, hasSameNumArgsAsDeclNumParams) {
  61. if (Node.getMethodDecl()->isFunctionTemplateSpecialization())
  62. return Node.getNumArgs() == Node.getMethodDecl()
  63. ->getPrimaryTemplate()
  64. ->getTemplatedDecl()
  65. ->getNumParams();
  66. return Node.getNumArgs() == Node.getMethodDecl()->getNumParams();
  67. }
  68. AST_MATCHER(DeclRefExpr, hasExplicitTemplateArgs) {
  69. return Node.hasExplicitTemplateArgs();
  70. }
  71. const auto DefaultContainersWithPushBack =
  72. "::std::vector; ::std::list; ::std::deque";
  73. const auto DefaultContainersWithPush =
  74. "::std::stack; ::std::queue; ::std::priority_queue";
  75. const auto DefaultContainersWithPushFront =
  76. "::std::forward_list; ::std::list; ::std::deque";
  77. const auto DefaultSmartPointers =
  78. "::std::shared_ptr; ::std::unique_ptr; ::std::auto_ptr; ::std::weak_ptr";
  79. const auto DefaultTupleTypes = "::std::pair; ::std::tuple";
  80. const auto DefaultTupleMakeFunctions = "::std::make_pair; ::std::make_tuple";
  81. const auto DefaultEmplacyFunctions =
  82. "vector::emplace_back; vector::emplace;"
  83. "deque::emplace; deque::emplace_front; deque::emplace_back;"
  84. "forward_list::emplace_after; forward_list::emplace_front;"
  85. "list::emplace; list::emplace_back; list::emplace_front;"
  86. "set::emplace; set::emplace_hint;"
  87. "map::emplace; map::emplace_hint;"
  88. "multiset::emplace; multiset::emplace_hint;"
  89. "multimap::emplace; multimap::emplace_hint;"
  90. "unordered_set::emplace; unordered_set::emplace_hint;"
  91. "unordered_map::emplace; unordered_map::emplace_hint;"
  92. "unordered_multiset::emplace; unordered_multiset::emplace_hint;"
  93. "unordered_multimap::emplace; unordered_multimap::emplace_hint;"
  94. "stack::emplace; queue::emplace; priority_queue::emplace";
  95. } // namespace
  96. UseEmplaceCheck::UseEmplaceCheck(StringRef Name, ClangTidyContext *Context)
  97. : ClangTidyCheck(Name, Context), IgnoreImplicitConstructors(Options.get(
  98. "IgnoreImplicitConstructors", false)),
  99. ContainersWithPushBack(utils::options::parseStringList(Options.get(
  100. "ContainersWithPushBack", DefaultContainersWithPushBack))),
  101. ContainersWithPush(utils::options::parseStringList(
  102. Options.get("ContainersWithPush", DefaultContainersWithPush))),
  103. ContainersWithPushFront(utils::options::parseStringList(Options.get(
  104. "ContainersWithPushFront", DefaultContainersWithPushFront))),
  105. SmartPointers(utils::options::parseStringList(
  106. Options.get("SmartPointers", DefaultSmartPointers))),
  107. TupleTypes(utils::options::parseStringList(
  108. Options.get("TupleTypes", DefaultTupleTypes))),
  109. TupleMakeFunctions(utils::options::parseStringList(
  110. Options.get("TupleMakeFunctions", DefaultTupleMakeFunctions))),
  111. EmplacyFunctions(utils::options::parseStringList(
  112. Options.get("EmplacyFunctions", DefaultEmplacyFunctions))) {}
  113. void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
  114. // FIXME: Bunch of functionality that could be easily added:
  115. // + add handling of `insert` for stl associative container, but be careful
  116. // because this requires special treatment (it could cause performance
  117. // regression)
  118. // + match for emplace calls that should be replaced with insertion
  119. auto CallPushBack = cxxMemberCallExpr(
  120. hasDeclaration(functionDecl(hasName("push_back"))),
  121. on(hasType(hasCanonicalType(
  122. hasDeclaration(cxxRecordDecl(hasAnyName(ContainersWithPushBack)))))));
  123. auto CallPush =
  124. cxxMemberCallExpr(hasDeclaration(functionDecl(hasName("push"))),
  125. on(hasType(hasCanonicalType(hasDeclaration(
  126. cxxRecordDecl(hasAnyName(ContainersWithPush)))))));
  127. auto CallPushFront = cxxMemberCallExpr(
  128. hasDeclaration(functionDecl(hasName("push_front"))),
  129. on(hasType(hasCanonicalType(hasDeclaration(
  130. cxxRecordDecl(hasAnyName(ContainersWithPushFront)))))));
  131. auto CallEmplacy = cxxMemberCallExpr(
  132. hasDeclaration(
  133. functionDecl(hasAnyNameIgnoringTemplates(EmplacyFunctions))),
  134. on(hasType(hasCanonicalType(hasDeclaration(has(typedefNameDecl(
  135. hasName("value_type"), hasType(type(hasUnqualifiedDesugaredType(
  136. recordType().bind("value_type")))))))))));
  137. // We can't replace push_backs of smart pointer because
  138. // if emplacement fails (f.e. bad_alloc in vector) we will have leak of
  139. // passed pointer because smart pointer won't be constructed
  140. // (and destructed) as in push_back case.
  141. auto IsCtorOfSmartPtr =
  142. hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(SmartPointers))));
  143. // Bitfields binds only to consts and emplace_back take it by universal ref.
  144. auto BitFieldAsArgument = hasAnyArgument(
  145. ignoringImplicit(memberExpr(hasDeclaration(fieldDecl(isBitField())))));
  146. // Initializer list can't be passed to universal reference.
  147. auto InitializerListAsArgument = hasAnyArgument(
  148. ignoringImplicit(cxxConstructExpr(isListInitialization())));
  149. // We could have leak of resource.
  150. auto NewExprAsArgument = hasAnyArgument(ignoringImplicit(cxxNewExpr()));
  151. // We would call another constructor.
  152. auto ConstructingDerived =
  153. hasParent(implicitCastExpr(hasCastKind(CastKind::CK_DerivedToBase)));
  154. // emplace_back can't access private or protected constructors.
  155. auto IsPrivateOrProtectedCtor =
  156. hasDeclaration(cxxConstructorDecl(anyOf(isPrivate(), isProtected())));
  157. auto HasInitList = anyOf(has(ignoringImplicit(initListExpr())),
  158. has(cxxStdInitializerListExpr()));
  159. // FIXME: Discard 0/NULL (as nullptr), static inline const data members,
  160. // overloaded functions and template names.
  161. auto SoughtConstructExpr =
  162. cxxConstructExpr(
  163. unless(anyOf(IsCtorOfSmartPtr, HasInitList, BitFieldAsArgument,
  164. InitializerListAsArgument, NewExprAsArgument,
  165. ConstructingDerived, IsPrivateOrProtectedCtor)))
  166. .bind("ctor");
  167. auto HasConstructExpr = has(ignoringImplicit(SoughtConstructExpr));
  168. auto MakeTuple = ignoringImplicit(
  169. callExpr(callee(expr(ignoringImplicit(declRefExpr(
  170. unless(hasExplicitTemplateArgs()),
  171. to(functionDecl(hasAnyName(TupleMakeFunctions))))))))
  172. .bind("make"));
  173. // make_something can return type convertible to container's element type.
  174. // Allow the conversion only on containers of pairs.
  175. auto MakeTupleCtor = ignoringImplicit(cxxConstructExpr(
  176. has(materializeTemporaryExpr(MakeTuple)),
  177. hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(TupleTypes))))));
  178. auto SoughtParam = materializeTemporaryExpr(
  179. anyOf(has(MakeTuple), has(MakeTupleCtor), HasConstructExpr,
  180. has(cxxFunctionalCastExpr(HasConstructExpr))));
  181. auto HasConstructExprWithValueTypeType =
  182. has(ignoringImplicit(cxxConstructExpr(
  183. SoughtConstructExpr, hasType(type(hasUnqualifiedDesugaredType(
  184. type(equalsBoundNode("value_type"))))))));
  185. auto HasConstructExprWithValueTypeTypeAsLastArgument =
  186. hasLastArgument(materializeTemporaryExpr(anyOf(
  187. HasConstructExprWithValueTypeType,
  188. has(cxxFunctionalCastExpr(HasConstructExprWithValueTypeType)))));
  189. Finder->addMatcher(
  190. traverse(TK_AsIs, cxxMemberCallExpr(CallPushBack, has(SoughtParam),
  191. unless(isInTemplateInstantiation()))
  192. .bind("push_back_call")),
  193. this);
  194. Finder->addMatcher(
  195. traverse(TK_AsIs, cxxMemberCallExpr(CallPush, has(SoughtParam),
  196. unless(isInTemplateInstantiation()))
  197. .bind("push_call")),
  198. this);
  199. Finder->addMatcher(
  200. traverse(TK_AsIs, cxxMemberCallExpr(CallPushFront, has(SoughtParam),
  201. unless(isInTemplateInstantiation()))
  202. .bind("push_front_call")),
  203. this);
  204. Finder->addMatcher(
  205. traverse(TK_AsIs,
  206. cxxMemberCallExpr(
  207. CallEmplacy, HasConstructExprWithValueTypeTypeAsLastArgument,
  208. hasSameNumArgsAsDeclNumParams(),
  209. unless(isInTemplateInstantiation()))
  210. .bind("emplacy_call")),
  211. this);
  212. Finder->addMatcher(
  213. traverse(
  214. TK_AsIs,
  215. cxxMemberCallExpr(
  216. CallEmplacy,
  217. on(hasType(cxxRecordDecl(has(typedefNameDecl(
  218. hasName("value_type"),
  219. hasType(type(
  220. hasUnqualifiedDesugaredType(recordType(hasDeclaration(
  221. cxxRecordDecl(hasAnyName(SmallVector<StringRef, 2>(
  222. TupleTypes.begin(), TupleTypes.end()))))))))))))),
  223. has(MakeTuple), hasSameNumArgsAsDeclNumParams(),
  224. unless(isInTemplateInstantiation()))
  225. .bind("emplacy_call")),
  226. this);
  227. }
  228. void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) {
  229. const auto *PushBackCall =
  230. Result.Nodes.getNodeAs<CXXMemberCallExpr>("push_back_call");
  231. const auto *PushCall = Result.Nodes.getNodeAs<CXXMemberCallExpr>("push_call");
  232. const auto *PushFrontCall =
  233. Result.Nodes.getNodeAs<CXXMemberCallExpr>("push_front_call");
  234. const auto *EmplacyCall =
  235. Result.Nodes.getNodeAs<CXXMemberCallExpr>("emplacy_call");
  236. const auto *CtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctor");
  237. const auto *MakeCall = Result.Nodes.getNodeAs<CallExpr>("make");
  238. const CXXMemberCallExpr *Call = [&]() {
  239. if (PushBackCall) {
  240. return PushBackCall;
  241. }
  242. if (PushCall) {
  243. return PushCall;
  244. }
  245. if (PushFrontCall) {
  246. return PushFrontCall;
  247. }
  248. return EmplacyCall;
  249. }();
  250. assert(Call && "No call matched");
  251. assert((CtorCall || MakeCall) && "No push_back parameter matched");
  252. if (IgnoreImplicitConstructors && CtorCall && CtorCall->getNumArgs() >= 1 &&
  253. CtorCall->getArg(0)->getSourceRange() == CtorCall->getSourceRange())
  254. return;
  255. const auto FunctionNameSourceRange = CharSourceRange::getCharRange(
  256. Call->getExprLoc(), Call->getArg(0)->getExprLoc());
  257. auto Diag =
  258. EmplacyCall
  259. ? diag(CtorCall ? CtorCall->getBeginLoc() : MakeCall->getBeginLoc(),
  260. "unnecessary temporary object created while calling %0")
  261. : diag(Call->getExprLoc(), "use emplace%select{|_back|_front}0 "
  262. "instead of push%select{|_back|_front}0");
  263. if (EmplacyCall)
  264. Diag << Call->getMethodDecl()->getName();
  265. else if (PushCall)
  266. Diag << 0;
  267. else if (PushBackCall)
  268. Diag << 1;
  269. else
  270. Diag << 2;
  271. if (FunctionNameSourceRange.getBegin().isMacroID())
  272. return;
  273. if (PushBackCall) {
  274. const char *EmplacePrefix = MakeCall ? "emplace_back" : "emplace_back(";
  275. Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
  276. EmplacePrefix);
  277. } else if (PushCall) {
  278. const char *EmplacePrefix = MakeCall ? "emplace" : "emplace(";
  279. Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
  280. EmplacePrefix);
  281. } else if (PushFrontCall) {
  282. const char *EmplacePrefix = MakeCall ? "emplace_front" : "emplace_front(";
  283. Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
  284. EmplacePrefix);
  285. }
  286. const SourceRange CallParensRange =
  287. MakeCall ? SourceRange(MakeCall->getCallee()->getEndLoc(),
  288. MakeCall->getRParenLoc())
  289. : CtorCall->getParenOrBraceRange();
  290. // Finish if there is no explicit constructor call.
  291. if (CallParensRange.getBegin().isInvalid())
  292. return;
  293. const SourceLocation ExprBegin =
  294. CtorCall ? CtorCall->getExprLoc() : MakeCall->getExprLoc();
  295. // Range for constructor name and opening brace.
  296. const auto ParamCallSourceRange =
  297. CharSourceRange::getTokenRange(ExprBegin, CallParensRange.getBegin());
  298. Diag << FixItHint::CreateRemoval(ParamCallSourceRange)
  299. << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
  300. CallParensRange.getEnd(), CallParensRange.getEnd()));
  301. if (MakeCall && EmplacyCall) {
  302. // Remove extra left parenthesis
  303. Diag << FixItHint::CreateRemoval(
  304. CharSourceRange::getCharRange(MakeCall->getCallee()->getEndLoc(),
  305. MakeCall->getArg(0)->getBeginLoc()));
  306. }
  307. }
  308. void UseEmplaceCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
  309. Options.store(Opts, "IgnoreImplicitConstructors", IgnoreImplicitConstructors);
  310. Options.store(Opts, "ContainersWithPushBack",
  311. utils::options::serializeStringList(ContainersWithPushBack));
  312. Options.store(Opts, "ContainersWithPush",
  313. utils::options::serializeStringList(ContainersWithPush));
  314. Options.store(Opts, "ContainersWithPushFront",
  315. utils::options::serializeStringList(ContainersWithPushFront));
  316. Options.store(Opts, "SmartPointers",
  317. utils::options::serializeStringList(SmartPointers));
  318. Options.store(Opts, "TupleTypes",
  319. utils::options::serializeStringList(TupleTypes));
  320. Options.store(Opts, "TupleMakeFunctions",
  321. utils::options::serializeStringList(TupleMakeFunctions));
  322. Options.store(Opts, "EmplacyFunctions",
  323. utils::options::serializeStringList(EmplacyFunctions));
  324. }
  325. } // namespace clang::tidy::modernize