clang-tools  7.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 
25 std::string GetNewExprName(const CXXNewExpr *NewExpr,
26  const SourceManager &SM,
27  const LangOptions &Lang) {
28  StringRef WrittenName = Lexer::getSourceText(
29  CharSourceRange::getTokenRange(
30  NewExpr->getAllocatedTypeSourceInfo()->getTypeLoc().getSourceRange()),
31  SM, Lang);
32  if (NewExpr->isArray()) {
33  return WrittenName.str() + "[]";
34  }
35  return WrittenName.str();
36 }
37 
38 } // namespace
39 
40 const char MakeSmartPtrCheck::PointerType[] = "pointerType";
41 const char MakeSmartPtrCheck::ConstructorCall[] = "constructorCall";
42 const char MakeSmartPtrCheck::ResetCall[] = "resetCall";
43 const char MakeSmartPtrCheck::NewExpression[] = "newExpression";
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.reset(new 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 
124  if (Construct)
125  checkConstruct(SM, Construct, Type, New);
126  else if (Reset)
127  checkReset(SM, Reset, New);
128 }
129 
130 void MakeSmartPtrCheck::checkConstruct(SourceManager &SM,
131  const CXXConstructExpr *Construct,
132  const QualType *Type,
133  const CXXNewExpr *New) {
134  SourceLocation ConstructCallStart = Construct->getExprLoc();
135  bool InMacro = ConstructCallStart.isMacroID();
136 
137  if (InMacro && IgnoreMacros) {
138  return;
139  }
140 
141  bool Invalid = false;
142  StringRef ExprStr = Lexer::getSourceText(
143  CharSourceRange::getCharRange(
144  ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
145  SM, getLangOpts(), &Invalid);
146  if (Invalid)
147  return;
148 
149  auto Diag = diag(ConstructCallStart, "use %0 instead")
150  << MakeSmartPtrFunctionName;
151 
152  // Disable the fix in macros.
153  if (InMacro) {
154  return;
155  }
156 
157  if (!replaceNew(Diag, New, SM)) {
158  return;
159  }
160 
161  // Find the location of the template's left angle.
162  size_t LAngle = ExprStr.find("<");
163  SourceLocation ConstructCallEnd;
164  if (LAngle == StringRef::npos) {
165  // If the template argument is missing (because it is part of the alias)
166  // we have to add it back.
167  ConstructCallEnd = ConstructCallStart.getLocWithOffset(ExprStr.size());
168  Diag << FixItHint::CreateInsertion(
169  ConstructCallEnd,
170  "<" + GetNewExprName(New, SM, getLangOpts()) + ">");
171  } else {
172  ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
173  }
174 
175  Diag << FixItHint::CreateReplacement(
176  CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
177  MakeSmartPtrFunctionName);
178 
179  // If the smart_ptr is built with brace enclosed direct initialization, use
180  // parenthesis instead.
181  if (Construct->isListInitialization()) {
182  SourceRange BraceRange = Construct->getParenOrBraceRange();
183  Diag << FixItHint::CreateReplacement(
184  CharSourceRange::getCharRange(
185  BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
186  "(");
187  Diag << FixItHint::CreateReplacement(
188  CharSourceRange::getCharRange(BraceRange.getEnd(),
189  BraceRange.getEnd().getLocWithOffset(1)),
190  ")");
191  }
192 
193  insertHeader(Diag, SM.getFileID(ConstructCallStart));
194 }
195 
196 void MakeSmartPtrCheck::checkReset(SourceManager &SM,
197  const CXXMemberCallExpr *Reset,
198  const CXXNewExpr *New) {
199  const auto *Expr = cast<MemberExpr>(Reset->getCallee());
200  SourceLocation OperatorLoc = Expr->getOperatorLoc();
201  SourceLocation ResetCallStart = Reset->getExprLoc();
202  SourceLocation ExprStart = Expr->getLocStart();
203  SourceLocation ExprEnd =
204  Lexer::getLocForEndOfToken(Expr->getLocEnd(), 0, SM, getLangOpts());
205 
206  bool InMacro = ExprStart.isMacroID();
207 
208  if (InMacro && IgnoreMacros) {
209  return;
210  }
211 
212  // There are some cases where we don't have operator ("." or "->") of the
213  // "reset" expression, e.g. call "reset()" method directly in the subclass of
214  // "std::unique_ptr<>". We skip these cases.
215  if (OperatorLoc.isInvalid()) {
216  return;
217  }
218 
219  auto Diag = diag(ResetCallStart, "use %0 instead")
220  << MakeSmartPtrFunctionName;
221 
222  // Disable the fix in macros.
223  if (InMacro) {
224  return;
225  }
226 
227  if (!replaceNew(Diag, New, SM)) {
228  return;
229  }
230 
231  Diag << FixItHint::CreateReplacement(
232  CharSourceRange::getCharRange(OperatorLoc, ExprEnd),
233  (llvm::Twine(" = ") + MakeSmartPtrFunctionName + "<" +
234  GetNewExprName(New, SM, getLangOpts()) + ">")
235  .str());
236 
237  if (Expr->isArrow())
238  Diag << FixItHint::CreateInsertion(ExprStart, "*");
239 
240  insertHeader(Diag, SM.getFileID(OperatorLoc));
241 }
242 
243 bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag,
244  const CXXNewExpr *New,
245  SourceManager& SM) {
246  SourceLocation NewStart = New->getSourceRange().getBegin();
247  SourceLocation NewEnd = New->getSourceRange().getEnd();
248 
249  // Skip when the source location of the new expression is invalid.
250  if (NewStart.isInvalid() || NewEnd.isInvalid())
251  return false;
252 
253  std::string ArraySizeExpr;
254  if (const auto* ArraySize = New->getArraySize()) {
255  ArraySizeExpr = Lexer::getSourceText(CharSourceRange::getTokenRange(
256  ArraySize->getSourceRange()),
257  SM, getLangOpts())
258  .str();
259  }
260 
261  switch (New->getInitializationStyle()) {
262  case CXXNewExpr::NoInit: {
263  if (ArraySizeExpr.empty()) {
264  Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
265  } else {
266  // New array expression without written initializer:
267  // smart_ptr<Foo[]>(new Foo[5]);
268  Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
269  ArraySizeExpr);
270  }
271  break;
272  }
273  case CXXNewExpr::CallInit: {
274  // FIXME: Add fixes for constructors with parameters that can be created
275  // with a C++11 braced-init-list (e.g. std::vector, std::map).
276  // Unlike ordinal cases, braced list can not be deduced in
277  // std::make_smart_ptr, we need to specify the type explicitly in the fixes:
278  // struct S { S(std::initializer_list<int>, int); };
279  // struct S2 { S2(std::vector<int>); };
280  // smart_ptr<S>(new S({1, 2, 3}, 1)); // C++98 call-style initialization
281  // smart_ptr<S>(new S({}, 1));
282  // smart_ptr<S2>(new S2({1})); // implicit conversion:
283  // // std::initializer_list => std::vector
284  // The above samples have to be replaced with:
285  // std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}), 1);
286  // std::make_smart_ptr<S>(std::initializer_list<int>({}), 1);
287  // std::make_smart_ptr<S2>(std::vector<int>({1}));
288  if (const auto *CE = New->getConstructExpr()) {
289  for (const auto *Arg : CE->arguments()) {
290  if (isa<CXXStdInitializerListExpr>(Arg)) {
291  return false;
292  }
293  // Check whether we construct a class from a std::initializer_list.
294  // If so, we won't generate the fixes.
295  auto IsStdInitListInitConstructExpr = [](const Expr* E) {
296  assert(E);
297  if (const auto *ImplicitCE = dyn_cast<CXXConstructExpr>(E)) {
298  if (ImplicitCE->isStdInitListInitialization())
299  return true;
300  }
301  return false;
302  };
303  if (IsStdInitListInitConstructExpr(Arg->IgnoreImplicit()))
304  return false;
305  }
306  }
307  if (ArraySizeExpr.empty()) {
308  SourceRange InitRange = New->getDirectInitRange();
309  Diag << FixItHint::CreateRemoval(
310  SourceRange(NewStart, InitRange.getBegin()));
311  Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
312  }
313  else {
314  // New array expression with default/value initialization:
315  // smart_ptr<Foo[]>(new int[5]());
316  // smart_ptr<Foo[]>(new Foo[5]());
317  Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
318  ArraySizeExpr);
319  }
320  break;
321  }
322  case CXXNewExpr::ListInit: {
323  // Range of the substring that we do not want to remove.
324  SourceRange InitRange;
325  if (const auto *NewConstruct = New->getConstructExpr()) {
326  if (NewConstruct->isStdInitListInitialization()) {
327  // FIXME: Add fixes for direct initialization with the initializer-list
328  // constructor. Similar to the above CallInit case, the type has to be
329  // specified explicitly in the fixes.
330  // struct S { S(std::initializer_list<int>); };
331  // smart_ptr<S>(new S{1, 2, 3}); // C++11 direct list-initialization
332  // smart_ptr<S>(new S{}); // use initializer-list consturctor
333  // The above cases have to be replaced with:
334  // std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}));
335  // std::make_smart_ptr<S>(std::initializer_list<int>({}));
336  return false;
337  } else {
338  // Direct initialization with ordinary constructors.
339  // struct S { S(int x); S(); };
340  // smart_ptr<S>(new S{5});
341  // smart_ptr<S>(new S{}); // use default constructor
342  // The arguments in the initialization list are going to be forwarded to
343  // the constructor, so this has to be replaced with:
344  // std::make_smart_ptr<S>(5);
345  // std::make_smart_ptr<S>();
346  InitRange = SourceRange(
347  NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
348  NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
349  }
350  } else {
351  // Aggregate initialization.
352  // smart_ptr<Pair>(new Pair{first, second});
353  // Has to be replaced with:
354  // smart_ptr<Pair>(Pair{first, second});
355  InitRange = SourceRange(
356  New->getAllocatedTypeSourceInfo()->getTypeLoc().getLocStart(),
357  New->getInitializer()->getSourceRange().getEnd());
358  }
359  Diag << FixItHint::CreateRemoval(
360  CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
361  Diag << FixItHint::CreateRemoval(
362  SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));
363  break;
364  }
365  }
366  return true;
367 }
368 
369 void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) {
370  if (MakeSmartPtrFunctionHeader.empty()) {
371  return;
372  }
373  if (auto IncludeFixit = Inserter->CreateIncludeInsertion(
374  FD, MakeSmartPtrFunctionHeader,
375  /*IsAngled=*/MakeSmartPtrFunctionHeader == StdMemoryHeader)) {
376  Diag << *IncludeFixit;
377  }
378 }
379 
380 } // namespace modernize
381 } // namespace tidy
382 } // namespace clang
llvm::StringRef Name
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:460
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.
void check(const ast_matchers::MatchFinder::MatchResult &Result) final
ClangTidyChecks that register ASTMatchers should do the actual work in here.
std::map< std::string, std::string > OptionMap
static bool isPublic(const clang::AccessSpecifier AS, const clang::Linkage Link)
Definition: Serialize.cpp:174
===– 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...
Produces fixes to insert specified includes to source files, if not yet present.
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:427