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