1 //===--- MakeSmartPtrCheck.cpp - clang-tidy--------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "../utils/TypeTraits.h"
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 ConstructorCall[] = "constructorCall";
24 constexpr char ResetCall[] = "resetCall";
25 constexpr char NewExpression[] = "newExpression";
26 
getNewExprName(const CXXNewExpr * NewExpr,const SourceManager & SM,const LangOptions & Lang)27 std::string getNewExprName(const CXXNewExpr *NewExpr, const SourceManager &SM,
28                            const LangOptions &Lang) {
29   StringRef WrittenName = Lexer::getSourceText(
30       CharSourceRange::getTokenRange(
31           NewExpr->getAllocatedTypeSourceInfo()->getTypeLoc().getSourceRange()),
32       SM, Lang);
33   if (NewExpr->isArray()) {
34     return (WrittenName + "[]").str();
35   }
36   return WrittenName.str();
37 }
38 
39 } // namespace
40 
41 const char MakeSmartPtrCheck::PointerType[] = "pointerType";
42 
MakeSmartPtrCheck(StringRef Name,ClangTidyContext * Context,StringRef MakeSmartPtrFunctionName)43 MakeSmartPtrCheck::MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context,
44                                      StringRef MakeSmartPtrFunctionName)
45     : ClangTidyCheck(Name, Context),
46       Inserter(Options.getLocalOrGlobal("IncludeStyle",
47                                         utils::IncludeSorter::IS_LLVM),
48                areDiagsSelfContained()),
49       MakeSmartPtrFunctionHeader(
50           Options.get("MakeSmartPtrFunctionHeader", "<memory>")),
51       MakeSmartPtrFunctionName(
52           Options.get("MakeSmartPtrFunction", MakeSmartPtrFunctionName)),
53       IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)),
54       IgnoreDefaultInitialization(
55           Options.get("IgnoreDefaultInitialization", true)) {}
56 
storeOptions(ClangTidyOptions::OptionMap & Opts)57 void MakeSmartPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
58   Options.store(Opts, "IncludeStyle", Inserter.getStyle());
59   Options.store(Opts, "MakeSmartPtrFunctionHeader", MakeSmartPtrFunctionHeader);
60   Options.store(Opts, "MakeSmartPtrFunction", MakeSmartPtrFunctionName);
61   Options.store(Opts, "IgnoreMacros", IgnoreMacros);
62   Options.store(Opts, "IgnoreDefaultInitialization",
63                 IgnoreDefaultInitialization);
64 }
65 
isLanguageVersionSupported(const LangOptions & LangOpts) const66 bool MakeSmartPtrCheck::isLanguageVersionSupported(
67     const LangOptions &LangOpts) const {
68   return LangOpts.CPlusPlus11;
69 }
70 
registerPPCallbacks(const SourceManager & SM,Preprocessor * PP,Preprocessor * ModuleExpanderPP)71 void MakeSmartPtrCheck::registerPPCallbacks(const SourceManager &SM,
72                                             Preprocessor *PP,
73                                             Preprocessor *ModuleExpanderPP) {
74   Inserter.registerPreprocessor(PP);
75 }
76 
registerMatchers(ast_matchers::MatchFinder * Finder)77 void MakeSmartPtrCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
78   // Calling make_smart_ptr from within a member function of a type with a
79   // private or protected constructor would be ill-formed.
80   auto CanCallCtor = unless(has(ignoringImpCasts(
81       cxxConstructExpr(hasDeclaration(decl(unless(isPublic())))))));
82 
83   auto IsPlacement = hasAnyPlacementArg(anything());
84 
85   Finder->addMatcher(
86       traverse(
87           TK_AsIs,
88           cxxBindTemporaryExpr(has(ignoringParenImpCasts(
89               cxxConstructExpr(
90                   hasType(getSmartPointerTypeMatcher()), argumentCountIs(1),
91                   hasArgument(
92                       0, cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
93                                         equalsBoundNode(PointerType))))),
94                                     CanCallCtor, unless(IsPlacement))
95                              .bind(NewExpression)),
96                   unless(isInTemplateInstantiation()))
97                   .bind(ConstructorCall))))),
98       this);
99 
100   Finder->addMatcher(
101       traverse(TK_AsIs,
102                cxxMemberCallExpr(
103                    thisPointerType(getSmartPointerTypeMatcher()),
104                    callee(cxxMethodDecl(hasName("reset"))),
105                    hasArgument(0, cxxNewExpr(CanCallCtor, unless(IsPlacement))
106                                       .bind(NewExpression)),
107                    unless(isInTemplateInstantiation()))
108                    .bind(ResetCall)),
109       this);
110 }
111 
check(const MatchFinder::MatchResult & Result)112 void MakeSmartPtrCheck::check(const MatchFinder::MatchResult &Result) {
113   // 'smart_ptr' refers to 'std::shared_ptr' or 'std::unique_ptr' or other
114   // pointer, 'make_smart_ptr' refers to 'std::make_shared' or
115   // 'std::make_unique' or other function that creates smart_ptr.
116 
117   SourceManager &SM = *Result.SourceManager;
118   const auto *Construct =
119       Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructorCall);
120   const auto *Reset = Result.Nodes.getNodeAs<CXXMemberCallExpr>(ResetCall);
121   const auto *Type = Result.Nodes.getNodeAs<QualType>(PointerType);
122   const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(NewExpression);
123 
124   // Skip when this is a new-expression with `auto`, e.g. new auto(1)
125   if (New->getType()->getPointeeType()->getContainedAutoType())
126     return;
127 
128   // Be conservative for cases where we construct and default initialize.
129   //
130   // For example,
131   //    P.reset(new int)    // check fix: P = std::make_unique<int>()
132   //    P.reset(new int[5]) // check fix: P = std::make_unique<int []>(5)
133   //
134   // The fix of the check has side effect, it introduces value initialization
135   // which maybe unexpected and cause performance regression.
136   bool Initializes = New->hasInitializer() ||
137                      !utils::type_traits::isTriviallyDefaultConstructible(
138                          New->getAllocatedType(), *Result.Context);
139   if (!Initializes && IgnoreDefaultInitialization)
140     return;
141   if (Construct)
142     checkConstruct(SM, Result.Context, Construct, Type, New);
143   else if (Reset)
144     checkReset(SM, Result.Context, Reset, New);
145 }
146 
checkConstruct(SourceManager & SM,ASTContext * Ctx,const CXXConstructExpr * Construct,const QualType * Type,const CXXNewExpr * New)147 void MakeSmartPtrCheck::checkConstruct(SourceManager &SM, ASTContext *Ctx,
148                                        const CXXConstructExpr *Construct,
149                                        const QualType *Type,
150                                        const CXXNewExpr *New) {
151   SourceLocation ConstructCallStart = Construct->getExprLoc();
152   bool InMacro = ConstructCallStart.isMacroID();
153 
154   if (InMacro && IgnoreMacros) {
155     return;
156   }
157 
158   bool Invalid = false;
159   StringRef ExprStr = Lexer::getSourceText(
160       CharSourceRange::getCharRange(
161           ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
162       SM, getLangOpts(), &Invalid);
163   if (Invalid)
164     return;
165 
166   auto Diag = diag(ConstructCallStart, "use %0 instead")
167               << MakeSmartPtrFunctionName;
168 
169   // Disable the fix in macros.
170   if (InMacro) {
171     return;
172   }
173 
174   if (!replaceNew(Diag, New, SM, Ctx)) {
175     return;
176   }
177 
178   // Find the location of the template's left angle.
179   size_t LAngle = ExprStr.find('<');
180   SourceLocation ConstructCallEnd;
181   if (LAngle == StringRef::npos) {
182     // If the template argument is missing (because it is part of the alias)
183     // we have to add it back.
184     ConstructCallEnd = ConstructCallStart.getLocWithOffset(ExprStr.size());
185     Diag << FixItHint::CreateInsertion(
186         ConstructCallEnd, "<" + getNewExprName(New, SM, getLangOpts()) + ">");
187   } else {
188     ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
189   }
190 
191   Diag << FixItHint::CreateReplacement(
192       CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
193       MakeSmartPtrFunctionName);
194 
195   // If the smart_ptr is built with brace enclosed direct initialization, use
196   // parenthesis instead.
197   if (Construct->isListInitialization()) {
198     SourceRange BraceRange = Construct->getParenOrBraceRange();
199     Diag << FixItHint::CreateReplacement(
200         CharSourceRange::getCharRange(
201             BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
202         "(");
203     Diag << FixItHint::CreateReplacement(
204         CharSourceRange::getCharRange(BraceRange.getEnd(),
205                                       BraceRange.getEnd().getLocWithOffset(1)),
206         ")");
207   }
208 
209   insertHeader(Diag, SM.getFileID(ConstructCallStart));
210 }
211 
checkReset(SourceManager & SM,ASTContext * Ctx,const CXXMemberCallExpr * Reset,const CXXNewExpr * New)212 void MakeSmartPtrCheck::checkReset(SourceManager &SM, ASTContext *Ctx,
213                                    const CXXMemberCallExpr *Reset,
214                                    const CXXNewExpr *New) {
215   const auto *Expr = cast<MemberExpr>(Reset->getCallee());
216   SourceLocation OperatorLoc = Expr->getOperatorLoc();
217   SourceLocation ResetCallStart = Reset->getExprLoc();
218   SourceLocation ExprStart = Expr->getBeginLoc();
219   SourceLocation ExprEnd =
220       Lexer::getLocForEndOfToken(Expr->getEndLoc(), 0, SM, getLangOpts());
221 
222   bool InMacro = ExprStart.isMacroID();
223 
224   if (InMacro && IgnoreMacros) {
225     return;
226   }
227 
228   // There are some cases where we don't have operator ("." or "->") of the
229   // "reset" expression, e.g. call "reset()" method directly in the subclass of
230   // "std::unique_ptr<>". We skip these cases.
231   if (OperatorLoc.isInvalid()) {
232     return;
233   }
234 
235   auto Diag = diag(ResetCallStart, "use %0 instead")
236               << MakeSmartPtrFunctionName;
237 
238   // Disable the fix in macros.
239   if (InMacro) {
240     return;
241   }
242 
243   if (!replaceNew(Diag, New, SM, Ctx)) {
244     return;
245   }
246 
247   Diag << FixItHint::CreateReplacement(
248       CharSourceRange::getCharRange(OperatorLoc, ExprEnd),
249       (llvm::Twine(" = ") + MakeSmartPtrFunctionName + "<" +
250        getNewExprName(New, SM, getLangOpts()) + ">")
251           .str());
252 
253   if (Expr->isArrow())
254     Diag << FixItHint::CreateInsertion(ExprStart, "*");
255 
256   insertHeader(Diag, SM.getFileID(OperatorLoc));
257 }
258 
replaceNew(DiagnosticBuilder & Diag,const CXXNewExpr * New,SourceManager & SM,ASTContext * Ctx)259 bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag,
260                                    const CXXNewExpr *New, SourceManager &SM,
261                                    ASTContext *Ctx) {
262   auto SkipParensParents = [&](const Expr *E) {
263     TraversalKindScope RAII(*Ctx, TK_AsIs);
264 
265     for (const Expr *OldE = nullptr; E != OldE;) {
266       OldE = E;
267       for (const auto &Node : Ctx->getParents(*E)) {
268         if (const Expr *Parent = Node.get<ParenExpr>()) {
269           E = Parent;
270           break;
271         }
272       }
273     }
274     return E;
275   };
276 
277   SourceRange NewRange = SkipParensParents(New)->getSourceRange();
278   SourceLocation NewStart = NewRange.getBegin();
279   SourceLocation NewEnd = NewRange.getEnd();
280 
281   // Skip when the source location of the new expression is invalid.
282   if (NewStart.isInvalid() || NewEnd.isInvalid())
283     return false;
284 
285   std::string ArraySizeExpr;
286   if (const auto *ArraySize = New->getArraySize().value_or(nullptr)) {
287     ArraySizeExpr = Lexer::getSourceText(CharSourceRange::getTokenRange(
288                                              ArraySize->getSourceRange()),
289                                          SM, getLangOpts())
290                         .str();
291   }
292   // Returns true if the given constructor expression has any braced-init-list
293   // argument, e.g.
294   //   Foo({1, 2}, 1) => true
295   //   Foo(Bar{1, 2}) => true
296   //   Foo(1) => false
297   //   Foo{1} => false
298   auto HasListIntializedArgument = [](const CXXConstructExpr *CE) {
299     for (const auto *Arg : CE->arguments()) {
300       Arg = Arg->IgnoreImplicit();
301 
302       if (isa<CXXStdInitializerListExpr>(Arg) || isa<InitListExpr>(Arg))
303         return true;
304       // Check whether we implicitly construct a class from a
305       // std::initializer_list.
306       if (const auto *CEArg = dyn_cast<CXXConstructExpr>(Arg)) {
307         // Strip the elidable move constructor, it is present in the AST for
308         // C++11/14, e.g. Foo(Bar{1, 2}), the move constructor is around the
309         // init-list constructor.
310         if (CEArg->isElidable()) {
311           if (const auto *TempExp = CEArg->getArg(0)) {
312             if (const auto *UnwrappedCE =
313                     dyn_cast<CXXConstructExpr>(TempExp->IgnoreImplicit()))
314               CEArg = UnwrappedCE;
315           }
316         }
317         if (CEArg->isStdInitListInitialization())
318           return true;
319       }
320     }
321     return false;
322   };
323   switch (New->getInitializationStyle()) {
324   case CXXNewExpr::NoInit: {
325     if (ArraySizeExpr.empty()) {
326       Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
327     } else {
328       // New array expression without written initializer:
329       //   smart_ptr<Foo[]>(new Foo[5]);
330       Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
331                                            ArraySizeExpr);
332     }
333     break;
334   }
335   case CXXNewExpr::CallInit: {
336     // FIXME: Add fixes for constructors with parameters that can be created
337     // with a C++11 braced-init-list (e.g. std::vector, std::map).
338     // Unlike ordinal cases, braced list can not be deduced in
339     // std::make_smart_ptr, we need to specify the type explicitly in the fixes:
340     //   struct S { S(std::initializer_list<int>, int); };
341     //   struct S2 { S2(std::vector<int>); };
342     //   struct S3 { S3(S2, int); };
343     //   smart_ptr<S>(new S({1, 2, 3}, 1));  // C++98 call-style initialization
344     //   smart_ptr<S>(new S({}, 1));
345     //   smart_ptr<S2>(new S2({1})); // implicit conversion:
346     //                               //   std::initializer_list => std::vector
347     //   smart_ptr<S3>(new S3({1, 2}, 3));
348     // The above samples have to be replaced with:
349     //   std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}), 1);
350     //   std::make_smart_ptr<S>(std::initializer_list<int>({}), 1);
351     //   std::make_smart_ptr<S2>(std::vector<int>({1}));
352     //   std::make_smart_ptr<S3>(S2{1, 2}, 3);
353     if (const auto *CE = New->getConstructExpr()) {
354       if (HasListIntializedArgument(CE))
355         return false;
356     }
357     if (ArraySizeExpr.empty()) {
358       SourceRange InitRange = New->getDirectInitRange();
359       Diag << FixItHint::CreateRemoval(
360           SourceRange(NewStart, InitRange.getBegin()));
361       Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
362     }
363     else {
364       // New array expression with default/value initialization:
365       //   smart_ptr<Foo[]>(new int[5]());
366       //   smart_ptr<Foo[]>(new Foo[5]());
367       Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
368                                            ArraySizeExpr);
369     }
370     break;
371   }
372   case CXXNewExpr::ListInit: {
373     // Range of the substring that we do not want to remove.
374     SourceRange InitRange;
375     if (const auto *NewConstruct = New->getConstructExpr()) {
376       if (NewConstruct->isStdInitListInitialization() ||
377           HasListIntializedArgument(NewConstruct)) {
378         // FIXME: Add fixes for direct initialization with the initializer-list
379         // constructor. Similar to the above CallInit case, the type has to be
380         // specified explicitly in the fixes.
381         //   struct S { S(std::initializer_list<int>); };
382         //   struct S2 { S2(S, int); };
383         //   smart_ptr<S>(new S{1, 2, 3});  // C++11 direct list-initialization
384         //   smart_ptr<S>(new S{});  // use initializer-list constructor
385         //   smart_ptr<S2>()new S2{ {1,2}, 3 }; // have a list-initialized arg
386         // The above cases have to be replaced with:
387         //   std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}));
388         //   std::make_smart_ptr<S>(std::initializer_list<int>({}));
389         //   std::make_smart_ptr<S2>(S{1, 2}, 3);
390         return false;
391       }
392       // Direct initialization with ordinary constructors.
393       //   struct S { S(int x); S(); };
394       //   smart_ptr<S>(new S{5});
395       //   smart_ptr<S>(new S{}); // use default constructor
396       // The arguments in the initialization list are going to be forwarded to
397       // the constructor, so this has to be replaced with:
398       //   std::make_smart_ptr<S>(5);
399       //   std::make_smart_ptr<S>();
400       InitRange = SourceRange(
401           NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
402           NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
403     } else {
404       // Aggregate initialization.
405       //   smart_ptr<Pair>(new Pair{first, second});
406       // Has to be replaced with:
407       //   smart_ptr<Pair>(Pair{first, second});
408       //
409       // The fix (std::make_unique) needs to see copy/move constructor of
410       // Pair. If we found any invisible or deleted copy/move constructor, we
411       // stop generating fixes -- as the C++ rule is complicated and we are less
412       // certain about the correct fixes.
413       if (const CXXRecordDecl *RD = New->getType()->getPointeeCXXRecordDecl()) {
414         if (llvm::find_if(RD->ctors(), [](const CXXConstructorDecl *Ctor) {
415               return Ctor->isCopyOrMoveConstructor() &&
416                      (Ctor->isDeleted() || Ctor->getAccess() == AS_private);
417             }) != RD->ctor_end()) {
418           return false;
419         }
420       }
421       InitRange = SourceRange(
422           New->getAllocatedTypeSourceInfo()->getTypeLoc().getBeginLoc(),
423           New->getInitializer()->getSourceRange().getEnd());
424     }
425     Diag << FixItHint::CreateRemoval(
426         CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
427     Diag << FixItHint::CreateRemoval(
428         SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));
429     break;
430   }
431   }
432   return true;
433 }
434 
insertHeader(DiagnosticBuilder & Diag,FileID FD)435 void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) {
436   if (MakeSmartPtrFunctionHeader.empty()) {
437     return;
438   }
439   Diag << Inserter.createIncludeInsertion(FD, MakeSmartPtrFunctionHeader);
440 }
441 
442 } // namespace modernize
443 } // namespace tidy
444 } // namespace clang
445