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