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