MakeSmartPtrCheck.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. //===--- MakeSmartPtrCheck.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 "../utils/TypeTraits.h"
  9. #include "MakeSharedCheck.h"
  10. #include "clang/Frontend/CompilerInstance.h"
  11. #include "clang/Lex/Lexer.h"
  12. #include "clang/Lex/Preprocessor.h"
  13. using namespace clang::ast_matchers;
  14. namespace clang::tidy::modernize {
  15. namespace {
  16. constexpr char ConstructorCall[] = "constructorCall";
  17. constexpr char ResetCall[] = "resetCall";
  18. constexpr char NewExpression[] = "newExpression";
  19. std::string getNewExprName(const CXXNewExpr *NewExpr, const SourceManager &SM,
  20. const LangOptions &Lang) {
  21. StringRef WrittenName = Lexer::getSourceText(
  22. CharSourceRange::getTokenRange(
  23. NewExpr->getAllocatedTypeSourceInfo()->getTypeLoc().getSourceRange()),
  24. SM, Lang);
  25. if (NewExpr->isArray()) {
  26. return (WrittenName + "[]").str();
  27. }
  28. return WrittenName.str();
  29. }
  30. } // namespace
  31. const char MakeSmartPtrCheck::PointerType[] = "pointerType";
  32. MakeSmartPtrCheck::MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context,
  33. StringRef MakeSmartPtrFunctionName)
  34. : ClangTidyCheck(Name, Context),
  35. Inserter(Options.getLocalOrGlobal("IncludeStyle",
  36. utils::IncludeSorter::IS_LLVM),
  37. areDiagsSelfContained()),
  38. MakeSmartPtrFunctionHeader(
  39. Options.get("MakeSmartPtrFunctionHeader", "<memory>")),
  40. MakeSmartPtrFunctionName(
  41. Options.get("MakeSmartPtrFunction", MakeSmartPtrFunctionName)),
  42. IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)),
  43. IgnoreDefaultInitialization(
  44. Options.get("IgnoreDefaultInitialization", true)) {}
  45. void MakeSmartPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
  46. Options.store(Opts, "IncludeStyle", Inserter.getStyle());
  47. Options.store(Opts, "MakeSmartPtrFunctionHeader", MakeSmartPtrFunctionHeader);
  48. Options.store(Opts, "MakeSmartPtrFunction", MakeSmartPtrFunctionName);
  49. Options.store(Opts, "IgnoreMacros", IgnoreMacros);
  50. Options.store(Opts, "IgnoreDefaultInitialization",
  51. IgnoreDefaultInitialization);
  52. }
  53. bool MakeSmartPtrCheck::isLanguageVersionSupported(
  54. const LangOptions &LangOpts) const {
  55. return LangOpts.CPlusPlus11;
  56. }
  57. void MakeSmartPtrCheck::registerPPCallbacks(const SourceManager &SM,
  58. Preprocessor *PP,
  59. Preprocessor *ModuleExpanderPP) {
  60. Inserter.registerPreprocessor(PP);
  61. }
  62. void MakeSmartPtrCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
  63. // Calling make_smart_ptr from within a member function of a type with a
  64. // private or protected constructor would be ill-formed.
  65. auto CanCallCtor = unless(has(ignoringImpCasts(
  66. cxxConstructExpr(hasDeclaration(decl(unless(isPublic())))))));
  67. auto IsPlacement = hasAnyPlacementArg(anything());
  68. Finder->addMatcher(
  69. traverse(
  70. TK_AsIs,
  71. cxxBindTemporaryExpr(has(ignoringParenImpCasts(
  72. cxxConstructExpr(
  73. hasType(getSmartPointerTypeMatcher()), argumentCountIs(1),
  74. hasArgument(
  75. 0, cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
  76. equalsBoundNode(PointerType))))),
  77. CanCallCtor, unless(IsPlacement))
  78. .bind(NewExpression)),
  79. unless(isInTemplateInstantiation()))
  80. .bind(ConstructorCall))))),
  81. this);
  82. Finder->addMatcher(
  83. traverse(TK_AsIs,
  84. cxxMemberCallExpr(
  85. thisPointerType(getSmartPointerTypeMatcher()),
  86. callee(cxxMethodDecl(hasName("reset"))),
  87. hasArgument(0, cxxNewExpr(CanCallCtor, unless(IsPlacement))
  88. .bind(NewExpression)),
  89. unless(isInTemplateInstantiation()))
  90. .bind(ResetCall)),
  91. this);
  92. }
  93. void MakeSmartPtrCheck::check(const MatchFinder::MatchResult &Result) {
  94. // 'smart_ptr' refers to 'std::shared_ptr' or 'std::unique_ptr' or other
  95. // pointer, 'make_smart_ptr' refers to 'std::make_shared' or
  96. // 'std::make_unique' or other function that creates smart_ptr.
  97. SourceManager &SM = *Result.SourceManager;
  98. const auto *Construct =
  99. Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructorCall);
  100. const auto *Reset = Result.Nodes.getNodeAs<CXXMemberCallExpr>(ResetCall);
  101. const auto *Type = Result.Nodes.getNodeAs<QualType>(PointerType);
  102. const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(NewExpression);
  103. // Skip when this is a new-expression with `auto`, e.g. new auto(1)
  104. if (New->getType()->getPointeeType()->getContainedAutoType())
  105. return;
  106. // Be conservative for cases where we construct and default initialize.
  107. //
  108. // For example,
  109. // P.reset(new int) // check fix: P = std::make_unique<int>()
  110. // P.reset(new int[5]) // check fix: P = std::make_unique<int []>(5)
  111. //
  112. // The fix of the check has side effect, it introduces value initialization
  113. // which maybe unexpected and cause performance regression.
  114. bool Initializes = New->hasInitializer() ||
  115. !utils::type_traits::isTriviallyDefaultConstructible(
  116. New->getAllocatedType(), *Result.Context);
  117. if (!Initializes && IgnoreDefaultInitialization)
  118. return;
  119. if (Construct)
  120. checkConstruct(SM, Result.Context, Construct, Type, New);
  121. else if (Reset)
  122. checkReset(SM, Result.Context, Reset, New);
  123. }
  124. void MakeSmartPtrCheck::checkConstruct(SourceManager &SM, ASTContext *Ctx,
  125. const CXXConstructExpr *Construct,
  126. const QualType *Type,
  127. const CXXNewExpr *New) {
  128. SourceLocation ConstructCallStart = Construct->getExprLoc();
  129. bool InMacro = ConstructCallStart.isMacroID();
  130. if (InMacro && IgnoreMacros) {
  131. return;
  132. }
  133. bool Invalid = false;
  134. StringRef ExprStr = Lexer::getSourceText(
  135. CharSourceRange::getCharRange(
  136. ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
  137. SM, getLangOpts(), &Invalid);
  138. if (Invalid)
  139. return;
  140. auto Diag = diag(ConstructCallStart, "use %0 instead")
  141. << MakeSmartPtrFunctionName;
  142. // Disable the fix in macros.
  143. if (InMacro) {
  144. return;
  145. }
  146. if (!replaceNew(Diag, New, SM, Ctx)) {
  147. return;
  148. }
  149. // Find the location of the template's left angle.
  150. size_t LAngle = ExprStr.find('<');
  151. SourceLocation ConstructCallEnd;
  152. if (LAngle == StringRef::npos) {
  153. // If the template argument is missing (because it is part of the alias)
  154. // we have to add it back.
  155. ConstructCallEnd = ConstructCallStart.getLocWithOffset(ExprStr.size());
  156. Diag << FixItHint::CreateInsertion(
  157. ConstructCallEnd, "<" + getNewExprName(New, SM, getLangOpts()) + ">");
  158. } else {
  159. ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
  160. }
  161. Diag << FixItHint::CreateReplacement(
  162. CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
  163. MakeSmartPtrFunctionName);
  164. // If the smart_ptr is built with brace enclosed direct initialization, use
  165. // parenthesis instead.
  166. if (Construct->isListInitialization()) {
  167. SourceRange BraceRange = Construct->getParenOrBraceRange();
  168. Diag << FixItHint::CreateReplacement(
  169. CharSourceRange::getCharRange(
  170. BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
  171. "(");
  172. Diag << FixItHint::CreateReplacement(
  173. CharSourceRange::getCharRange(BraceRange.getEnd(),
  174. BraceRange.getEnd().getLocWithOffset(1)),
  175. ")");
  176. }
  177. insertHeader(Diag, SM.getFileID(ConstructCallStart));
  178. }
  179. void MakeSmartPtrCheck::checkReset(SourceManager &SM, ASTContext *Ctx,
  180. const CXXMemberCallExpr *Reset,
  181. const CXXNewExpr *New) {
  182. const auto *Expr = cast<MemberExpr>(Reset->getCallee());
  183. SourceLocation OperatorLoc = Expr->getOperatorLoc();
  184. SourceLocation ResetCallStart = Reset->getExprLoc();
  185. SourceLocation ExprStart = Expr->getBeginLoc();
  186. SourceLocation ExprEnd =
  187. Lexer::getLocForEndOfToken(Expr->getEndLoc(), 0, SM, getLangOpts());
  188. bool InMacro = ExprStart.isMacroID();
  189. if (InMacro && IgnoreMacros) {
  190. return;
  191. }
  192. // There are some cases where we don't have operator ("." or "->") of the
  193. // "reset" expression, e.g. call "reset()" method directly in the subclass of
  194. // "std::unique_ptr<>". We skip these cases.
  195. if (OperatorLoc.isInvalid()) {
  196. return;
  197. }
  198. auto Diag = diag(ResetCallStart, "use %0 instead")
  199. << MakeSmartPtrFunctionName;
  200. // Disable the fix in macros.
  201. if (InMacro) {
  202. return;
  203. }
  204. if (!replaceNew(Diag, New, SM, Ctx)) {
  205. return;
  206. }
  207. Diag << FixItHint::CreateReplacement(
  208. CharSourceRange::getCharRange(OperatorLoc, ExprEnd),
  209. (llvm::Twine(" = ") + MakeSmartPtrFunctionName + "<" +
  210. getNewExprName(New, SM, getLangOpts()) + ">")
  211. .str());
  212. if (Expr->isArrow())
  213. Diag << FixItHint::CreateInsertion(ExprStart, "*");
  214. insertHeader(Diag, SM.getFileID(OperatorLoc));
  215. }
  216. bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag,
  217. const CXXNewExpr *New, SourceManager &SM,
  218. ASTContext *Ctx) {
  219. auto SkipParensParents = [&](const Expr *E) {
  220. TraversalKindScope RAII(*Ctx, TK_AsIs);
  221. for (const Expr *OldE = nullptr; E != OldE;) {
  222. OldE = E;
  223. for (const auto &Node : Ctx->getParents(*E)) {
  224. if (const Expr *Parent = Node.get<ParenExpr>()) {
  225. E = Parent;
  226. break;
  227. }
  228. }
  229. }
  230. return E;
  231. };
  232. SourceRange NewRange = SkipParensParents(New)->getSourceRange();
  233. SourceLocation NewStart = NewRange.getBegin();
  234. SourceLocation NewEnd = NewRange.getEnd();
  235. // Skip when the source location of the new expression is invalid.
  236. if (NewStart.isInvalid() || NewEnd.isInvalid())
  237. return false;
  238. std::string ArraySizeExpr;
  239. if (const auto *ArraySize = New->getArraySize().value_or(nullptr)) {
  240. ArraySizeExpr = Lexer::getSourceText(CharSourceRange::getTokenRange(
  241. ArraySize->getSourceRange()),
  242. SM, getLangOpts())
  243. .str();
  244. }
  245. // Returns true if the given constructor expression has any braced-init-list
  246. // argument, e.g.
  247. // Foo({1, 2}, 1) => true
  248. // Foo(Bar{1, 2}) => true
  249. // Foo(1) => false
  250. // Foo{1} => false
  251. auto HasListIntializedArgument = [](const CXXConstructExpr *CE) {
  252. for (const auto *Arg : CE->arguments()) {
  253. Arg = Arg->IgnoreImplicit();
  254. if (isa<CXXStdInitializerListExpr>(Arg) || isa<InitListExpr>(Arg))
  255. return true;
  256. // Check whether we implicitly construct a class from a
  257. // std::initializer_list.
  258. if (const auto *CEArg = dyn_cast<CXXConstructExpr>(Arg)) {
  259. // Strip the elidable move constructor, it is present in the AST for
  260. // C++11/14, e.g. Foo(Bar{1, 2}), the move constructor is around the
  261. // init-list constructor.
  262. if (CEArg->isElidable()) {
  263. if (const auto *TempExp = CEArg->getArg(0)) {
  264. if (const auto *UnwrappedCE =
  265. dyn_cast<CXXConstructExpr>(TempExp->IgnoreImplicit()))
  266. CEArg = UnwrappedCE;
  267. }
  268. }
  269. if (CEArg->isStdInitListInitialization())
  270. return true;
  271. }
  272. }
  273. return false;
  274. };
  275. switch (New->getInitializationStyle()) {
  276. case CXXNewExpr::NoInit: {
  277. if (ArraySizeExpr.empty()) {
  278. Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
  279. } else {
  280. // New array expression without written initializer:
  281. // smart_ptr<Foo[]>(new Foo[5]);
  282. Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
  283. ArraySizeExpr);
  284. }
  285. break;
  286. }
  287. case CXXNewExpr::CallInit: {
  288. // FIXME: Add fixes for constructors with parameters that can be created
  289. // with a C++11 braced-init-list (e.g. std::vector, std::map).
  290. // Unlike ordinal cases, braced list can not be deduced in
  291. // std::make_smart_ptr, we need to specify the type explicitly in the fixes:
  292. // struct S { S(std::initializer_list<int>, int); };
  293. // struct S2 { S2(std::vector<int>); };
  294. // struct S3 { S3(S2, int); };
  295. // smart_ptr<S>(new S({1, 2, 3}, 1)); // C++98 call-style initialization
  296. // smart_ptr<S>(new S({}, 1));
  297. // smart_ptr<S2>(new S2({1})); // implicit conversion:
  298. // // std::initializer_list => std::vector
  299. // smart_ptr<S3>(new S3({1, 2}, 3));
  300. // The above samples have to be replaced with:
  301. // std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}), 1);
  302. // std::make_smart_ptr<S>(std::initializer_list<int>({}), 1);
  303. // std::make_smart_ptr<S2>(std::vector<int>({1}));
  304. // std::make_smart_ptr<S3>(S2{1, 2}, 3);
  305. if (const auto *CE = New->getConstructExpr()) {
  306. if (HasListIntializedArgument(CE))
  307. return false;
  308. }
  309. if (ArraySizeExpr.empty()) {
  310. SourceRange InitRange = New->getDirectInitRange();
  311. Diag << FixItHint::CreateRemoval(
  312. SourceRange(NewStart, InitRange.getBegin()));
  313. Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
  314. }
  315. else {
  316. // New array expression with default/value initialization:
  317. // smart_ptr<Foo[]>(new int[5]());
  318. // smart_ptr<Foo[]>(new Foo[5]());
  319. Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
  320. ArraySizeExpr);
  321. }
  322. break;
  323. }
  324. case CXXNewExpr::ListInit: {
  325. // Range of the substring that we do not want to remove.
  326. SourceRange InitRange;
  327. if (const auto *NewConstruct = New->getConstructExpr()) {
  328. if (NewConstruct->isStdInitListInitialization() ||
  329. HasListIntializedArgument(NewConstruct)) {
  330. // FIXME: Add fixes for direct initialization with the initializer-list
  331. // constructor. Similar to the above CallInit case, the type has to be
  332. // specified explicitly in the fixes.
  333. // struct S { S(std::initializer_list<int>); };
  334. // struct S2 { S2(S, int); };
  335. // smart_ptr<S>(new S{1, 2, 3}); // C++11 direct list-initialization
  336. // smart_ptr<S>(new S{}); // use initializer-list constructor
  337. // smart_ptr<S2>()new S2{ {1,2}, 3 }; // have a list-initialized arg
  338. // The above cases have to be replaced with:
  339. // std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}));
  340. // std::make_smart_ptr<S>(std::initializer_list<int>({}));
  341. // std::make_smart_ptr<S2>(S{1, 2}, 3);
  342. return false;
  343. }
  344. // Direct initialization with ordinary constructors.
  345. // struct S { S(int x); S(); };
  346. // smart_ptr<S>(new S{5});
  347. // smart_ptr<S>(new S{}); // use default constructor
  348. // The arguments in the initialization list are going to be forwarded to
  349. // the constructor, so this has to be replaced with:
  350. // std::make_smart_ptr<S>(5);
  351. // std::make_smart_ptr<S>();
  352. InitRange = SourceRange(
  353. NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
  354. NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
  355. } else {
  356. // Aggregate initialization.
  357. // smart_ptr<Pair>(new Pair{first, second});
  358. // Has to be replaced with:
  359. // smart_ptr<Pair>(Pair{first, second});
  360. //
  361. // The fix (std::make_unique) needs to see copy/move constructor of
  362. // Pair. If we found any invisible or deleted copy/move constructor, we
  363. // stop generating fixes -- as the C++ rule is complicated and we are less
  364. // certain about the correct fixes.
  365. if (const CXXRecordDecl *RD = New->getType()->getPointeeCXXRecordDecl()) {
  366. if (llvm::any_of(RD->ctors(), [](const CXXConstructorDecl *Ctor) {
  367. return Ctor->isCopyOrMoveConstructor() &&
  368. (Ctor->isDeleted() || Ctor->getAccess() == AS_private);
  369. })) {
  370. return false;
  371. }
  372. }
  373. InitRange = SourceRange(
  374. New->getAllocatedTypeSourceInfo()->getTypeLoc().getBeginLoc(),
  375. New->getInitializer()->getSourceRange().getEnd());
  376. }
  377. Diag << FixItHint::CreateRemoval(
  378. CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
  379. Diag << FixItHint::CreateRemoval(
  380. SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));
  381. break;
  382. }
  383. }
  384. return true;
  385. }
  386. void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) {
  387. if (MakeSmartPtrFunctionHeader.empty()) {
  388. return;
  389. }
  390. Diag << Inserter.createIncludeInsertion(FD, MakeSmartPtrFunctionHeader);
  391. }
  392. } // namespace clang::tidy::modernize