clang-tools  6.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 
64 void MakeSmartPtrCheck::registerPPCallbacks(CompilerInstance &Compiler) {
65  if (getLangOpts().CPlusPlus11) {
66  Inserter.reset(new utils::IncludeInserter(
67  Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
68  Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
69  }
70 }
71 
72 void MakeSmartPtrCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
73  if (!getLangOpts().CPlusPlus11)
74  return;
75 
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  Finder->addMatcher(
82  cxxBindTemporaryExpr(has(ignoringParenImpCasts(
83  cxxConstructExpr(
84  hasType(getSmartPointerTypeMatcher()), argumentCountIs(1),
85  hasArgument(0,
86  cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
87  equalsBoundNode(PointerType))))),
88  CanCallCtor)
89  .bind(NewExpression)),
90  unless(isInTemplateInstantiation()))
91  .bind(ConstructorCall)))),
92  this);
93 
94  Finder->addMatcher(
95  cxxMemberCallExpr(
96  thisPointerType(getSmartPointerTypeMatcher()),
97  callee(cxxMethodDecl(hasName("reset"))),
98  hasArgument(0, cxxNewExpr(CanCallCtor).bind(NewExpression)),
99  unless(isInTemplateInstantiation()))
100  .bind(ResetCall),
101  this);
102 }
103 
104 void MakeSmartPtrCheck::check(const MatchFinder::MatchResult &Result) {
105  // 'smart_ptr' refers to 'std::shared_ptr' or 'std::unique_ptr' or other
106  // pointer, 'make_smart_ptr' refers to 'std::make_shared' or
107  // 'std::make_unique' or other function that creates smart_ptr.
108 
109  SourceManager &SM = *Result.SourceManager;
110  const auto *Construct =
111  Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructorCall);
112  const auto *Reset = Result.Nodes.getNodeAs<CXXMemberCallExpr>(ResetCall);
113  const auto *Type = Result.Nodes.getNodeAs<QualType>(PointerType);
114  const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(NewExpression);
115 
116  if (New->getNumPlacementArgs() != 0)
117  return;
118 
119  if (Construct)
120  checkConstruct(SM, Construct, Type, New);
121  else if (Reset)
122  checkReset(SM, Reset, New);
123 }
124 
125 void MakeSmartPtrCheck::checkConstruct(SourceManager &SM,
126  const CXXConstructExpr *Construct,
127  const QualType *Type,
128  const CXXNewExpr *New) {
129  SourceLocation ConstructCallStart = Construct->getExprLoc();
130  bool InMacro = ConstructCallStart.isMacroID();
131 
132  if (InMacro && IgnoreMacros) {
133  return;
134  }
135 
136  bool Invalid = false;
137  StringRef ExprStr = Lexer::getSourceText(
138  CharSourceRange::getCharRange(
139  ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
140  SM, getLangOpts(), &Invalid);
141  if (Invalid)
142  return;
143 
144  auto Diag = diag(ConstructCallStart, "use %0 instead")
145  << MakeSmartPtrFunctionName;
146 
147  // Disable the fix in macros.
148  if (InMacro) {
149  return;
150  }
151 
152  if (!replaceNew(Diag, New, SM)) {
153  return;
154  }
155 
156  // Find the location of the template's left angle.
157  size_t LAngle = ExprStr.find("<");
158  SourceLocation ConstructCallEnd;
159  if (LAngle == StringRef::npos) {
160  // If the template argument is missing (because it is part of the alias)
161  // we have to add it back.
162  ConstructCallEnd = ConstructCallStart.getLocWithOffset(ExprStr.size());
163  Diag << FixItHint::CreateInsertion(
164  ConstructCallEnd,
165  "<" + GetNewExprName(New, SM, getLangOpts()) + ">");
166  } else {
167  ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
168  }
169 
170  Diag << FixItHint::CreateReplacement(
171  CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
172  MakeSmartPtrFunctionName);
173 
174  // If the smart_ptr is built with brace enclosed direct initialization, use
175  // parenthesis instead.
176  if (Construct->isListInitialization()) {
177  SourceRange BraceRange = Construct->getParenOrBraceRange();
178  Diag << FixItHint::CreateReplacement(
179  CharSourceRange::getCharRange(
180  BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
181  "(");
182  Diag << FixItHint::CreateReplacement(
183  CharSourceRange::getCharRange(BraceRange.getEnd(),
184  BraceRange.getEnd().getLocWithOffset(1)),
185  ")");
186  }
187 
188  insertHeader(Diag, SM.getFileID(ConstructCallStart));
189 }
190 
191 void MakeSmartPtrCheck::checkReset(SourceManager &SM,
192  const CXXMemberCallExpr *Reset,
193  const CXXNewExpr *New) {
194  const auto *Expr = cast<MemberExpr>(Reset->getCallee());
195  SourceLocation OperatorLoc = Expr->getOperatorLoc();
196  SourceLocation ResetCallStart = Reset->getExprLoc();
197  SourceLocation ExprStart = Expr->getLocStart();
198  SourceLocation ExprEnd =
199  Lexer::getLocForEndOfToken(Expr->getLocEnd(), 0, SM, getLangOpts());
200 
201  bool InMacro = ExprStart.isMacroID();
202 
203  if (InMacro && IgnoreMacros) {
204  return;
205  }
206 
207  // There are some cases where we don't have operator ("." or "->") of the
208  // "reset" expression, e.g. call "reset()" method directly in the subclass of
209  // "std::unique_ptr<>". We skip these cases.
210  if (OperatorLoc.isInvalid()) {
211  return;
212  }
213 
214  auto Diag = diag(ResetCallStart, "use %0 instead")
215  << MakeSmartPtrFunctionName;
216 
217  // Disable the fix in macros.
218  if (InMacro) {
219  return;
220  }
221 
222  if (!replaceNew(Diag, New, SM)) {
223  return;
224  }
225 
226  Diag << FixItHint::CreateReplacement(
227  CharSourceRange::getCharRange(OperatorLoc, ExprEnd),
228  (llvm::Twine(" = ") + MakeSmartPtrFunctionName + "<" +
229  GetNewExprName(New, SM, getLangOpts()) + ">")
230  .str());
231 
232  if (Expr->isArrow())
233  Diag << FixItHint::CreateInsertion(ExprStart, "*");
234 
235  insertHeader(Diag, SM.getFileID(OperatorLoc));
236 }
237 
238 bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag,
239  const CXXNewExpr *New,
240  SourceManager& SM) {
241  SourceLocation NewStart = New->getSourceRange().getBegin();
242  SourceLocation NewEnd = New->getSourceRange().getEnd();
243 
244  std::string ArraySizeExpr;
245  if (const auto* ArraySize = New->getArraySize()) {
246  ArraySizeExpr = Lexer::getSourceText(CharSourceRange::getTokenRange(
247  ArraySize->getSourceRange()),
248  SM, getLangOpts())
249  .str();
250  }
251 
252  switch (New->getInitializationStyle()) {
253  case CXXNewExpr::NoInit: {
254  if (ArraySizeExpr.empty()) {
255  Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
256  } else {
257  // New array expression without written initializer:
258  // smart_ptr<Foo[]>(new Foo[5]);
259  Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
260  ArraySizeExpr);
261  }
262  break;
263  }
264  case CXXNewExpr::CallInit: {
265  // FIXME: Add fixes for constructors with parameters that can be created
266  // with a C++11 braced-init-list (e.g. std::vector, std::map).
267  // Unlike ordinal cases, braced list can not be deduced in
268  // std::make_smart_ptr, we need to specify the type explicitly in the fixes:
269  // struct S { S(std::initializer_list<int>, int); };
270  // struct S2 { S2(std::vector<int>); };
271  // smart_ptr<S>(new S({1, 2, 3}, 1)); // C++98 call-style initialization
272  // smart_ptr<S>(new S({}, 1));
273  // smart_ptr<S2>(new S2({1})); // implicit conversion:
274  // // std::initializer_list => std::vector
275  // The above samples have to be replaced with:
276  // std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}), 1);
277  // std::make_smart_ptr<S>(std::initializer_list<int>({}), 1);
278  // std::make_smart_ptr<S2>(std::vector<int>({1}));
279  if (const auto *CE = New->getConstructExpr()) {
280  for (const auto *Arg : CE->arguments()) {
281  if (isa<CXXStdInitializerListExpr>(Arg)) {
282  return false;
283  }
284  // Check the implicit conversion from the std::initializer_list type to
285  // a class type.
286  if (const auto *ImplicitCE = dyn_cast<CXXConstructExpr>(Arg)) {
287  if (ImplicitCE->isStdInitListInitialization()) {
288  return false;
289  }
290  }
291  }
292  }
293  if (ArraySizeExpr.empty()) {
294  SourceRange InitRange = New->getDirectInitRange();
295  Diag << FixItHint::CreateRemoval(
296  SourceRange(NewStart, InitRange.getBegin()));
297  Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
298  }
299  else {
300  // New array expression with default/value initialization:
301  // smart_ptr<Foo[]>(new int[5]());
302  // smart_ptr<Foo[]>(new Foo[5]());
303  Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
304  ArraySizeExpr);
305  }
306  break;
307  }
308  case CXXNewExpr::ListInit: {
309  // Range of the substring that we do not want to remove.
310  SourceRange InitRange;
311  if (const auto *NewConstruct = New->getConstructExpr()) {
312  if (NewConstruct->isStdInitListInitialization()) {
313  // FIXME: Add fixes for direct initialization with the initializer-list
314  // constructor. Similar to the above CallInit case, the type has to be
315  // specified explicitly in the fixes.
316  // struct S { S(std::initializer_list<int>); };
317  // smart_ptr<S>(new S{1, 2, 3}); // C++11 direct list-initialization
318  // smart_ptr<S>(new S{}); // use initializer-list consturctor
319  // The above cases have to be replaced with:
320  // std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}));
321  // std::make_smart_ptr<S>(std::initializer_list<int>({}));
322  return false;
323  } else {
324  // Direct initialization with ordinary constructors.
325  // struct S { S(int x); S(); };
326  // smart_ptr<S>(new S{5});
327  // smart_ptr<S>(new S{}); // use default constructor
328  // The arguments in the initialization list are going to be forwarded to
329  // the constructor, so this has to be replaced with:
330  // std::make_smart_ptr<S>(5);
331  // std::make_smart_ptr<S>();
332  InitRange = SourceRange(
333  NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
334  NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
335  }
336  } else {
337  // Aggregate initialization.
338  // smart_ptr<Pair>(new Pair{first, second});
339  // Has to be replaced with:
340  // smart_ptr<Pair>(Pair{first, second});
341  InitRange = SourceRange(
342  New->getAllocatedTypeSourceInfo()->getTypeLoc().getLocStart(),
343  New->getInitializer()->getSourceRange().getEnd());
344  }
345  Diag << FixItHint::CreateRemoval(
346  CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
347  Diag << FixItHint::CreateRemoval(
348  SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));
349  break;
350  }
351  }
352  return true;
353 }
354 
355 void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) {
356  if (MakeSmartPtrFunctionHeader.empty()) {
357  return;
358  }
359  if (auto IncludeFixit = Inserter->CreateIncludeInsertion(
360  FD, MakeSmartPtrFunctionHeader,
361  /*IsAngled=*/MakeSmartPtrFunctionHeader == StdMemoryHeader)) {
362  Diag << *IncludeFixit;
363  }
364 }
365 
366 } // namespace modernize
367 } // namespace tidy
368 } // 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:449
StringHandle Name
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:187
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
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:416