clang-tools  5.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, ClangTidyContext *Context,
46  StringRef MakeSmartPtrFunctionName)
47  : ClangTidyCheck(Name, Context),
48  IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
49  Options.get("IncludeStyle", "llvm"))),
50  MakeSmartPtrFunctionHeader(
51  Options.get("MakeSmartPtrFunctionHeader", StdMemoryHeader)),
52  MakeSmartPtrFunctionName(
53  Options.get("MakeSmartPtrFunction", MakeSmartPtrFunctionName)) {}
54 
56  Options.store(Opts, "IncludeStyle", IncludeStyle);
57  Options.store(Opts, "MakeSmartPtrFunctionHeader", MakeSmartPtrFunctionHeader);
58  Options.store(Opts, "MakeSmartPtrFunction", MakeSmartPtrFunctionName);
59 }
60 
61 void MakeSmartPtrCheck::registerPPCallbacks(CompilerInstance &Compiler) {
62  if (getLangOpts().CPlusPlus11) {
63  Inserter.reset(new utils::IncludeInserter(
64  Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
65  Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
66  }
67 }
68 
69 void MakeSmartPtrCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
70  if (!getLangOpts().CPlusPlus11)
71  return;
72 
73  // Calling make_smart_ptr from within a member function of a type with a
74  // private or protected constructor would be ill-formed.
75  auto CanCallCtor = unless(has(ignoringImpCasts(
76  cxxConstructExpr(hasDeclaration(decl(unless(isPublic())))))));
77 
78  Finder->addMatcher(
79  cxxBindTemporaryExpr(has(ignoringParenImpCasts(
80  cxxConstructExpr(
81  hasType(getSmartPointerTypeMatcher()), argumentCountIs(1),
82  hasArgument(0,
83  cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
84  equalsBoundNode(PointerType))))),
85  CanCallCtor)
86  .bind(NewExpression)))
87  .bind(ConstructorCall)))),
88  this);
89 
90  Finder->addMatcher(
91  cxxMemberCallExpr(
92  thisPointerType(getSmartPointerTypeMatcher()),
93  callee(cxxMethodDecl(hasName("reset"))),
94  hasArgument(0, cxxNewExpr(CanCallCtor).bind(NewExpression)))
95  .bind(ResetCall),
96  this);
97 }
98 
99 void MakeSmartPtrCheck::check(const MatchFinder::MatchResult &Result) {
100  // 'smart_ptr' refers to 'std::shared_ptr' or 'std::unique_ptr' or other
101  // pointer, 'make_smart_ptr' refers to 'std::make_shared' or
102  // 'std::make_unique' or other function that creates smart_ptr.
103 
104  SourceManager &SM = *Result.SourceManager;
105  const auto *Construct =
106  Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructorCall);
107  const auto *Reset = Result.Nodes.getNodeAs<CXXMemberCallExpr>(ResetCall);
108  const auto *Type = Result.Nodes.getNodeAs<QualType>(PointerType);
109  const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(NewExpression);
110 
111  if (New->getNumPlacementArgs() != 0)
112  return;
113 
114  if (Construct)
115  checkConstruct(SM, Construct, Type, New);
116  else if (Reset)
117  checkReset(SM, Reset, New);
118 }
119 
120 void MakeSmartPtrCheck::checkConstruct(SourceManager &SM,
121  const CXXConstructExpr *Construct,
122  const QualType *Type,
123  const CXXNewExpr *New) {
124  SourceLocation ConstructCallStart = Construct->getExprLoc();
125 
126  bool Invalid = false;
127  StringRef ExprStr = Lexer::getSourceText(
128  CharSourceRange::getCharRange(
129  ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
130  SM, getLangOpts(), &Invalid);
131  if (Invalid)
132  return;
133 
134  auto Diag = diag(ConstructCallStart, "use %0 instead")
135  << MakeSmartPtrFunctionName;
136 
137  // Find the location of the template's left angle.
138  size_t LAngle = ExprStr.find("<");
139  SourceLocation ConstructCallEnd;
140  if (LAngle == StringRef::npos) {
141  // If the template argument is missing (because it is part of the alias)
142  // we have to add it back.
143  ConstructCallEnd = ConstructCallStart.getLocWithOffset(ExprStr.size());
144  Diag << FixItHint::CreateInsertion(
145  ConstructCallEnd,
146  "<" + GetNewExprName(New, SM, getLangOpts()) + ">");
147  } else {
148  ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
149  }
150 
151  Diag << FixItHint::CreateReplacement(
152  CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
153  MakeSmartPtrFunctionName);
154 
155  // If the smart_ptr is built with brace enclosed direct initialization, use
156  // parenthesis instead.
157  if (Construct->isListInitialization()) {
158  SourceRange BraceRange = Construct->getParenOrBraceRange();
159  Diag << FixItHint::CreateReplacement(
160  CharSourceRange::getCharRange(
161  BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
162  "(");
163  Diag << FixItHint::CreateReplacement(
164  CharSourceRange::getCharRange(BraceRange.getEnd(),
165  BraceRange.getEnd().getLocWithOffset(1)),
166  ")");
167  }
168 
169  replaceNew(Diag, New, SM);
170  insertHeader(Diag, SM.getFileID(ConstructCallStart));
171 }
172 
173 void MakeSmartPtrCheck::checkReset(SourceManager &SM,
174  const CXXMemberCallExpr *Reset,
175  const CXXNewExpr *New) {
176  const auto *Expr = cast<MemberExpr>(Reset->getCallee());
177  SourceLocation OperatorLoc = Expr->getOperatorLoc();
178  SourceLocation ResetCallStart = Reset->getExprLoc();
179  SourceLocation ExprStart = Expr->getLocStart();
180  SourceLocation ExprEnd =
181  Lexer::getLocForEndOfToken(Expr->getLocEnd(), 0, SM, getLangOpts());
182 
183  auto Diag = diag(ResetCallStart, "use %0 instead")
184  << MakeSmartPtrFunctionName;
185 
186  Diag << FixItHint::CreateReplacement(
187  CharSourceRange::getCharRange(OperatorLoc, ExprEnd),
188  (llvm::Twine(" = ") + MakeSmartPtrFunctionName + "<" +
189  GetNewExprName(New, SM, getLangOpts()) + ">")
190  .str());
191 
192  if (Expr->isArrow())
193  Diag << FixItHint::CreateInsertion(ExprStart, "*");
194 
195  replaceNew(Diag, New, SM);
196  insertHeader(Diag, SM.getFileID(OperatorLoc));
197 }
198 
199 void MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag,
200  const CXXNewExpr *New,
201  SourceManager& SM) {
202  SourceLocation NewStart = New->getSourceRange().getBegin();
203  SourceLocation NewEnd = New->getSourceRange().getEnd();
204 
205  std::string ArraySizeExpr;
206  if (const auto* ArraySize = New->getArraySize()) {
207  ArraySizeExpr = Lexer::getSourceText(CharSourceRange::getTokenRange(
208  ArraySize->getSourceRange()),
209  SM, getLangOpts())
210  .str();
211  }
212 
213  switch (New->getInitializationStyle()) {
214  case CXXNewExpr::NoInit: {
215  if (ArraySizeExpr.empty()) {
216  Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
217  } else {
218  // New array expression without written initializer:
219  // smart_ptr<Foo[]>(new Foo[5]);
220  Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
221  ArraySizeExpr);
222  }
223  break;
224  }
225  case CXXNewExpr::CallInit: {
226  if (ArraySizeExpr.empty()) {
227  SourceRange InitRange = New->getDirectInitRange();
228  Diag << FixItHint::CreateRemoval(
229  SourceRange(NewStart, InitRange.getBegin()));
230  Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
231  }
232  else {
233  // New array expression with default/value initialization:
234  // smart_ptr<Foo[]>(new int[5]());
235  // smart_ptr<Foo[]>(new Foo[5]());
236  Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
237  ArraySizeExpr);
238  }
239  break;
240  }
241  case CXXNewExpr::ListInit: {
242  // Range of the substring that we do not want to remove.
243  SourceRange InitRange;
244  if (const auto *NewConstruct = New->getConstructExpr()) {
245  // Direct initialization with initialization list.
246  // struct S { S(int x) {} };
247  // smart_ptr<S>(new S{5});
248  // The arguments in the initialization list are going to be forwarded to
249  // the constructor, so this has to be replaced with:
250  // struct S { S(int x) {} };
251  // std::make_smart_ptr<S>(5);
252  InitRange = SourceRange(
253  NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
254  NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
255  } else {
256  // Aggregate initialization.
257  // smart_ptr<Pair>(new Pair{first, second});
258  // Has to be replaced with:
259  // smart_ptr<Pair>(Pair{first, second});
260  InitRange = SourceRange(
261  New->getAllocatedTypeSourceInfo()->getTypeLoc().getLocStart(),
262  New->getInitializer()->getSourceRange().getEnd());
263  }
264  Diag << FixItHint::CreateRemoval(
265  CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
266  Diag << FixItHint::CreateRemoval(
267  SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));
268  break;
269  }
270  }
271 }
272 
273 void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) {
274  if (MakeSmartPtrFunctionHeader.empty()) {
275  return;
276  }
277  if (auto IncludeFixit = Inserter->CreateIncludeInsertion(
278  FD, MakeSmartPtrFunctionHeader,
279  /*IsAngled=*/MakeSmartPtrFunctionHeader == StdMemoryHeader)) {
280  Diag << *IncludeFixit;
281  }
282 }
283 
284 } // namespace modernize
285 } // namespace tidy
286 } // namespace clang
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:187
StringHandle Name
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:275
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.
SourceManager & SM
void check(const ast_matchers::MatchFinder::MatchResult &Result) final
ClangTidyChecks that register ASTMatchers should do the actual work in here.
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
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.
ClangTidyContext & Context
Definition: ClangTidy.cpp:87
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's name.
Definition: ClangTidy.cpp:416