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 
57 void MakeSmartPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
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   // Skip when the source location of the new expression is invalid.
245   if (NewStart.isInvalid() || NewEnd.isInvalid())
246     return false;
247 
248   std::string ArraySizeExpr;
249   if (const auto* ArraySize = New->getArraySize()) {
250     ArraySizeExpr = Lexer::getSourceText(CharSourceRange::getTokenRange(
251                                              ArraySize->getSourceRange()),
252                                          SM, getLangOpts())
253                         .str();
254   }
255 
256   switch (New->getInitializationStyle()) {
257   case CXXNewExpr::NoInit: {
258     if (ArraySizeExpr.empty()) {
259       Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
260     } else {
261       // New array expression without written initializer:
262       //   smart_ptr<Foo[]>(new Foo[5]);
263       Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
264                                            ArraySizeExpr);
265     }
266     break;
267   }
268   case CXXNewExpr::CallInit: {
269     // FIXME: Add fixes for constructors with parameters that can be created
270     // with a C++11 braced-init-list (e.g. std::vector, std::map).
271     // Unlike ordinal cases, braced list can not be deduced in
272     // std::make_smart_ptr, we need to specify the type explicitly in the fixes:
273     //   struct S { S(std::initializer_list<int>, int); };
274     //   struct S2 { S2(std::vector<int>); };
275     //   smart_ptr<S>(new S({1, 2, 3}, 1));  // C++98 call-style initialization
276     //   smart_ptr<S>(new S({}, 1));
277     //   smart_ptr<S2>(new S2({1})); // implicit conversion:
278     //                               //   std::initializer_list => std::vector
279     // The above samples have to be replaced with:
280     //   std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}), 1);
281     //   std::make_smart_ptr<S>(std::initializer_list<int>({}), 1);
282     //   std::make_smart_ptr<S2>(std::vector<int>({1}));
283     if (const auto *CE = New->getConstructExpr()) {
284       for (const auto *Arg : CE->arguments()) {
285         if (isa<CXXStdInitializerListExpr>(Arg)) {
286           return false;
287         }
288         // Check whether we construct a class from a std::initializer_list.
289         // If so, we won't generate the fixes.
290         auto IsStdInitListInitConstructExpr = [](const Expr* E) {
291           assert(E);
292           if (const auto *ImplicitCE = dyn_cast<CXXConstructExpr>(E)) {
293             if (ImplicitCE->isStdInitListInitialization())
294               return true;
295           }
296           return false;
297         };
298         // Check the implicit conversion from the std::initializer_list type to
299         // a class type.
300         if (IsStdInitListInitConstructExpr(Arg))
301           return false;
302         // The Arg can be a CXXBindTemporaryExpr, checking its underlying
303         // construct expr.
304         if (const auto * CTE = dyn_cast<CXXBindTemporaryExpr>(Arg)) {
305           if (IsStdInitListInitConstructExpr(CTE->getSubExpr()))
306             return false;
307         }
308       }
309     }
310     if (ArraySizeExpr.empty()) {
311       SourceRange InitRange = New->getDirectInitRange();
312       Diag << FixItHint::CreateRemoval(
313           SourceRange(NewStart, InitRange.getBegin()));
314       Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
315     }
316     else {
317       // New array expression with default/value initialization:
318       //   smart_ptr<Foo[]>(new int[5]());
319       //   smart_ptr<Foo[]>(new Foo[5]());
320       Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
321                                            ArraySizeExpr);
322     }
323     break;
324   }
325   case CXXNewExpr::ListInit: {
326     // Range of the substring that we do not want to remove.
327     SourceRange InitRange;
328     if (const auto *NewConstruct = New->getConstructExpr()) {
329       if (NewConstruct->isStdInitListInitialization()) {
330         // FIXME: Add fixes for direct initialization with the initializer-list
331         // constructor. Similar to the above CallInit case, the type has to be
332         // specified explicitly in the fixes.
333         //   struct S { S(std::initializer_list<int>); };
334         //   smart_ptr<S>(new S{1, 2, 3});  // C++11 direct list-initialization
335         //   smart_ptr<S>(new S{});  // use initializer-list consturctor
336         // The above cases have to be replaced with:
337         //   std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}));
338         //   std::make_smart_ptr<S>(std::initializer_list<int>({}));
339         return false;
340       } else {
341         // Direct initialization with ordinary constructors.
342         //   struct S { S(int x); S(); };
343         //   smart_ptr<S>(new S{5});
344         //   smart_ptr<S>(new S{}); // use default constructor
345         // The arguments in the initialization list are going to be forwarded to
346         // the constructor, so this has to be replaced with:
347         //   std::make_smart_ptr<S>(5);
348         //   std::make_smart_ptr<S>();
349         InitRange = SourceRange(
350             NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
351             NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
352       }
353     } else {
354       // Aggregate initialization.
355       //   smart_ptr<Pair>(new Pair{first, second});
356       // Has to be replaced with:
357       //   smart_ptr<Pair>(Pair{first, second});
358       InitRange = SourceRange(
359           New->getAllocatedTypeSourceInfo()->getTypeLoc().getLocStart(),
360           New->getInitializer()->getSourceRange().getEnd());
361     }
362     Diag << FixItHint::CreateRemoval(
363         CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
364     Diag << FixItHint::CreateRemoval(
365         SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));
366     break;
367   }
368   }
369   return true;
370 }
371 
372 void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) {
373   if (MakeSmartPtrFunctionHeader.empty()) {
374     return;
375   }
376   if (auto IncludeFixit = Inserter->CreateIncludeInsertion(
377           FD, MakeSmartPtrFunctionHeader,
378           /*IsAngled=*/MakeSmartPtrFunctionHeader == StdMemoryHeader)) {
379     Diag << *IncludeFixit;
380   }
381 }
382 
383 } // namespace modernize
384 } // namespace tidy
385 } // namespace clang
386