clang-tools  4.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/Lex/Lexer.h"
12 
13 using namespace clang::ast_matchers;
14 
15 namespace clang {
16 namespace tidy {
17 namespace modernize {
18 
19 const char MakeSmartPtrCheck::PointerType[] = "pointerType";
20 const char MakeSmartPtrCheck::ConstructorCall[] = "constructorCall";
21 const char MakeSmartPtrCheck::ResetCall[] = "resetCall";
22 const char MakeSmartPtrCheck::NewExpression[] = "newExpression";
23 
24 MakeSmartPtrCheck::MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context,
25  std::string makeSmartPtrFunctionName)
26  : ClangTidyCheck(Name, Context),
27  makeSmartPtrFunctionName(std::move(makeSmartPtrFunctionName)) {}
28 
29 void MakeSmartPtrCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
30  if (!getLangOpts().CPlusPlus11)
31  return;
32 
33  // Calling make_smart_ptr from within a member function of a type with a
34  // private or protected constructor would be ill-formed.
35  auto CanCallCtor = unless(has(ignoringImpCasts(
36  cxxConstructExpr(hasDeclaration(decl(unless(isPublic())))))));
37 
38  Finder->addMatcher(
39  cxxBindTemporaryExpr(has(ignoringParenImpCasts(
40  cxxConstructExpr(
41  hasType(getSmartPointerTypeMatcher()), argumentCountIs(1),
42  hasArgument(0,
43  cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
44  equalsBoundNode(PointerType))))),
45  CanCallCtor)
46  .bind(NewExpression)))
47  .bind(ConstructorCall)))),
48  this);
49 
50  Finder->addMatcher(
51  cxxMemberCallExpr(
52  thisPointerType(getSmartPointerTypeMatcher()),
53  callee(cxxMethodDecl(hasName("reset"))),
54  hasArgument(0, cxxNewExpr(CanCallCtor).bind(NewExpression)))
55  .bind(ResetCall),
56  this);
57 }
58 
59 void MakeSmartPtrCheck::check(const MatchFinder::MatchResult &Result) {
60  // 'smart_ptr' refers to 'std::shared_ptr' or 'std::unique_ptr' or other
61  // pointer, 'make_smart_ptr' refers to 'std::make_shared' or
62  // 'std::make_unique' or other function that creates smart_ptr.
63 
64  SourceManager &SM = *Result.SourceManager;
65  const auto *Construct =
66  Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructorCall);
67  const auto *Reset = Result.Nodes.getNodeAs<CXXMemberCallExpr>(ResetCall);
68  const auto *Type = Result.Nodes.getNodeAs<QualType>(PointerType);
69  const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(NewExpression);
70 
71  if (New->getNumPlacementArgs() != 0)
72  return;
73 
74  if (Construct)
75  checkConstruct(SM, Construct, Type, New);
76  else if (Reset)
77  checkReset(SM, Reset, New);
78 }
79 
80 void MakeSmartPtrCheck::checkConstruct(SourceManager &SM,
81  const CXXConstructExpr *Construct,
82  const QualType *Type,
83  const CXXNewExpr *New) {
84  SourceLocation ConstructCallStart = Construct->getExprLoc();
85 
86  bool Invalid = false;
87  StringRef ExprStr = Lexer::getSourceText(
88  CharSourceRange::getCharRange(
89  ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
90  SM, LangOptions(), &Invalid);
91  if (Invalid)
92  return;
93 
94  auto Diag = diag(ConstructCallStart, "use %0 instead")
95  << makeSmartPtrFunctionName;
96 
97  // Find the location of the template's left angle.
98  size_t LAngle = ExprStr.find("<");
99  SourceLocation ConstructCallEnd;
100  if (LAngle == StringRef::npos) {
101  // If the template argument is missing (because it is part of the alias)
102  // we have to add it back.
103  ConstructCallEnd = ConstructCallStart.getLocWithOffset(ExprStr.size());
104  Diag << FixItHint::CreateInsertion(
105  ConstructCallEnd, "<" + Type->getAsString(getLangOpts()) + ">");
106  } else {
107  ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
108  }
109 
110  Diag << FixItHint::CreateReplacement(
111  CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
112  makeSmartPtrFunctionName);
113 
114  // If the smart_ptr is built with brace enclosed direct initialization, use
115  // parenthesis instead.
116  if (Construct->isListInitialization()) {
117  SourceRange BraceRange = Construct->getParenOrBraceRange();
118  Diag << FixItHint::CreateReplacement(
119  CharSourceRange::getCharRange(
120  BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
121  "(");
122  Diag << FixItHint::CreateReplacement(
123  CharSourceRange::getCharRange(BraceRange.getEnd(),
124  BraceRange.getEnd().getLocWithOffset(1)),
125  ")");
126  }
127 
128  replaceNew(Diag, New);
129 }
130 
131 void MakeSmartPtrCheck::checkReset(SourceManager &SM,
132  const CXXMemberCallExpr *Reset,
133  const CXXNewExpr *New) {
134  const auto *Expr = cast<MemberExpr>(Reset->getCallee());
135  SourceLocation OperatorLoc = Expr->getOperatorLoc();
136  SourceLocation ResetCallStart = Reset->getExprLoc();
137  SourceLocation ExprStart = Expr->getLocStart();
138  SourceLocation ExprEnd =
139  Lexer::getLocForEndOfToken(Expr->getLocEnd(), 0, SM, getLangOpts());
140 
141  auto Diag = diag(ResetCallStart, "use %0 instead")
142  << makeSmartPtrFunctionName;
143 
144  Diag << FixItHint::CreateReplacement(
145  CharSourceRange::getCharRange(OperatorLoc, ExprEnd),
146  (llvm::Twine(" = ") + makeSmartPtrFunctionName + "<" +
147  New->getAllocatedType().getAsString(getLangOpts()) + ">")
148  .str());
149 
150  if (Expr->isArrow())
151  Diag << FixItHint::CreateInsertion(ExprStart, "*");
152 
153  replaceNew(Diag, New);
154 }
155 
156 void MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag,
157  const CXXNewExpr *New) {
158  SourceLocation NewStart = New->getSourceRange().getBegin();
159  SourceLocation NewEnd = New->getSourceRange().getEnd();
160  switch (New->getInitializationStyle()) {
161  case CXXNewExpr::NoInit: {
162  Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
163  break;
164  }
165  case CXXNewExpr::CallInit: {
166  SourceRange InitRange = New->getDirectInitRange();
167  Diag << FixItHint::CreateRemoval(
168  SourceRange(NewStart, InitRange.getBegin()));
169  Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
170  break;
171  }
172  case CXXNewExpr::ListInit: {
173  // Range of the substring that we do not want to remove.
174  SourceRange InitRange;
175  if (const auto *NewConstruct = New->getConstructExpr()) {
176  // Direct initialization with initialization list.
177  // struct S { S(int x) {} };
178  // smart_ptr<S>(new S{5});
179  // The arguments in the initialization list are going to be forwarded to
180  // the constructor, so this has to be replaced with:
181  // struct S { S(int x) {} };
182  // std::make_smart_ptr<S>(5);
183  InitRange = SourceRange(
184  NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
185  NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
186  } else {
187  // Aggregate initialization.
188  // smart_ptr<Pair>(new Pair{first, second});
189  // Has to be replaced with:
190  // smart_ptr<Pair>(Pair{first, second});
191  InitRange = SourceRange(
192  New->getAllocatedTypeSourceInfo()->getTypeLoc().getLocStart(),
193  New->getInitializer()->getSourceRange().getEnd());
194  }
195  Diag << FixItHint::CreateRemoval(
196  CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
197  Diag << FixItHint::CreateRemoval(
198  SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));
199  break;
200  }
201  }
202 }
203 
204 } // namespace modernize
205 } // namespace tidy
206 } // namespace clang
const std::string Name
Definition: USRFinder.cpp:164
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:187
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:262
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
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.
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:403
const NamedDecl * Result
Definition: USRFinder.cpp:162