• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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,
28                            const SourceManager &SM,
29                            const LangOptions &Lang) {
30   StringRef WrittenName = Lexer::getSourceText(
31       CharSourceRange::getTokenRange(
32           NewExpr->getAllocatedTypeSourceInfo()->getTypeLoc().getSourceRange()),
33       SM, Lang);
34   if (NewExpr->isArray()) {
35     return (WrittenName + "[]").str();
36   }
37   return WrittenName.str();
38 }
39 
40 } // namespace
41 
42 const char MakeSmartPtrCheck::PointerType[] = "pointerType";
43 
MakeSmartPtrCheck(StringRef Name,ClangTidyContext * Context,StringRef MakeSmartPtrFunctionName)44 MakeSmartPtrCheck::MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context,
45                                      StringRef MakeSmartPtrFunctionName)
46     : ClangTidyCheck(Name, Context),
47       Inserter(Options.getLocalOrGlobal("IncludeStyle",
48                                         utils::IncludeSorter::IS_LLVM)),
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           ast_type_traits::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(ast_type_traits::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,
187         "<" + GetNewExprName(New, SM, getLangOpts()) + ">");
188   } else {
189     ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
190   }
191 
192   Diag << FixItHint::CreateReplacement(
193       CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
194       MakeSmartPtrFunctionName);
195 
196   // If the smart_ptr is built with brace enclosed direct initialization, use
197   // parenthesis instead.
198   if (Construct->isListInitialization()) {
199     SourceRange BraceRange = Construct->getParenOrBraceRange();
200     Diag << FixItHint::CreateReplacement(
201         CharSourceRange::getCharRange(
202             BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
203         "(");
204     Diag << FixItHint::CreateReplacement(
205         CharSourceRange::getCharRange(BraceRange.getEnd(),
206                                       BraceRange.getEnd().getLocWithOffset(1)),
207         ")");
208   }
209 
210   insertHeader(Diag, SM.getFileID(ConstructCallStart));
211 }
212 
checkReset(SourceManager & SM,ASTContext * Ctx,const CXXMemberCallExpr * Reset,const CXXNewExpr * New)213 void MakeSmartPtrCheck::checkReset(SourceManager &SM, ASTContext *Ctx,
214                                    const CXXMemberCallExpr *Reset,
215                                    const CXXNewExpr *New) {
216   const auto *Expr = cast<MemberExpr>(Reset->getCallee());
217   SourceLocation OperatorLoc = Expr->getOperatorLoc();
218   SourceLocation ResetCallStart = Reset->getExprLoc();
219   SourceLocation ExprStart = Expr->getBeginLoc();
220   SourceLocation ExprEnd =
221       Lexer::getLocForEndOfToken(Expr->getEndLoc(), 0, SM, getLangOpts());
222 
223   bool InMacro = ExprStart.isMacroID();
224 
225   if (InMacro && IgnoreMacros) {
226     return;
227   }
228 
229   // There are some cases where we don't have operator ("." or "->") of the
230   // "reset" expression, e.g. call "reset()" method directly in the subclass of
231   // "std::unique_ptr<>". We skip these cases.
232   if (OperatorLoc.isInvalid()) {
233     return;
234   }
235 
236   auto Diag = diag(ResetCallStart, "use %0 instead")
237               << MakeSmartPtrFunctionName;
238 
239   // Disable the fix in macros.
240   if (InMacro) {
241     return;
242   }
243 
244   if (!replaceNew(Diag, New, SM, Ctx)) {
245     return;
246   }
247 
248   Diag << FixItHint::CreateReplacement(
249       CharSourceRange::getCharRange(OperatorLoc, ExprEnd),
250       (llvm::Twine(" = ") + MakeSmartPtrFunctionName + "<" +
251        GetNewExprName(New, SM, getLangOpts()) + ">")
252           .str());
253 
254   if (Expr->isArrow())
255     Diag << FixItHint::CreateInsertion(ExprStart, "*");
256 
257   insertHeader(Diag, SM.getFileID(OperatorLoc));
258 }
259 
replaceNew(DiagnosticBuilder & Diag,const CXXNewExpr * New,SourceManager & SM,ASTContext * Ctx)260 bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag,
261                                    const CXXNewExpr *New, SourceManager &SM,
262                                    ASTContext *Ctx) {
263   auto SkipParensParents = [&](const Expr *E) {
264     TraversalKindScope RAII(*Ctx, ast_type_traits::TK_AsIs);
265 
266     for (const Expr *OldE = nullptr; E != OldE;) {
267       OldE = E;
268       for (const auto &Node : Ctx->getParents(*E)) {
269         if (const Expr *Parent = Node.get<ParenExpr>()) {
270           E = Parent;
271           break;
272         }
273       }
274     }
275     return E;
276   };
277 
278   SourceRange NewRange = SkipParensParents(New)->getSourceRange();
279   SourceLocation NewStart = NewRange.getBegin();
280   SourceLocation NewEnd = NewRange.getEnd();
281 
282   // Skip when the source location of the new expression is invalid.
283   if (NewStart.isInvalid() || NewEnd.isInvalid())
284     return false;
285 
286   std::string ArraySizeExpr;
287   if (const auto* ArraySize = New->getArraySize().getValueOr(nullptr)) {
288     ArraySizeExpr = Lexer::getSourceText(CharSourceRange::getTokenRange(
289                                              ArraySize->getSourceRange()),
290                                          SM, getLangOpts())
291                         .str();
292   }
293   // Returns true if the given constructor expression has any braced-init-list
294   // argument, e.g.
295   //   Foo({1, 2}, 1) => true
296   //   Foo(Bar{1, 2}) => true
297   //   Foo(1) => false
298   //   Foo{1} => false
299   auto HasListIntializedArgument = [](const CXXConstructExpr *CE) {
300     for (const auto *Arg : CE->arguments()) {
301       Arg = Arg->IgnoreImplicit();
302 
303       if (isa<CXXStdInitializerListExpr>(Arg) || isa<InitListExpr>(Arg))
304         return true;
305       // Check whether we implicitly construct a class from a
306       // std::initializer_list.
307       if (const auto *CEArg = dyn_cast<CXXConstructExpr>(Arg)) {
308         // Strip the elidable move constructor, it is present in the AST for
309         // C++11/14, e.g. Foo(Bar{1, 2}), the move constructor is around the
310         // init-list constructor.
311         if (CEArg->isElidable()) {
312           if (const auto *TempExp = CEArg->getArg(0)) {
313             if (const auto *UnwrappedCE =
314                     dyn_cast<CXXConstructExpr>(TempExp->IgnoreImplicit()))
315               CEArg = UnwrappedCE;
316           }
317         }
318         if (CEArg->isStdInitListInitialization())
319           return true;
320       }
321     }
322     return false;
323   };
324   switch (New->getInitializationStyle()) {
325   case CXXNewExpr::NoInit: {
326     if (ArraySizeExpr.empty()) {
327       Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
328     } else {
329       // New array expression without written initializer:
330       //   smart_ptr<Foo[]>(new Foo[5]);
331       Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
332                                            ArraySizeExpr);
333     }
334     break;
335   }
336   case CXXNewExpr::CallInit: {
337     // FIXME: Add fixes for constructors with parameters that can be created
338     // with a C++11 braced-init-list (e.g. std::vector, std::map).
339     // Unlike ordinal cases, braced list can not be deduced in
340     // std::make_smart_ptr, we need to specify the type explicitly in the fixes:
341     //   struct S { S(std::initializer_list<int>, int); };
342     //   struct S2 { S2(std::vector<int>); };
343     //   struct S3 { S3(S2, int); };
344     //   smart_ptr<S>(new S({1, 2, 3}, 1));  // C++98 call-style initialization
345     //   smart_ptr<S>(new S({}, 1));
346     //   smart_ptr<S2>(new S2({1})); // implicit conversion:
347     //                               //   std::initializer_list => std::vector
348     //   smart_ptr<S3>(new S3({1, 2}, 3));
349     // The above samples have to be replaced with:
350     //   std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}), 1);
351     //   std::make_smart_ptr<S>(std::initializer_list<int>({}), 1);
352     //   std::make_smart_ptr<S2>(std::vector<int>({1}));
353     //   std::make_smart_ptr<S3>(S2{1, 2}, 3);
354     if (const auto *CE = New->getConstructExpr()) {
355       if (HasListIntializedArgument(CE))
356         return false;
357     }
358     if (ArraySizeExpr.empty()) {
359       SourceRange InitRange = New->getDirectInitRange();
360       Diag << FixItHint::CreateRemoval(
361           SourceRange(NewStart, InitRange.getBegin()));
362       Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
363     }
364     else {
365       // New array expression with default/value initialization:
366       //   smart_ptr<Foo[]>(new int[5]());
367       //   smart_ptr<Foo[]>(new Foo[5]());
368       Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
369                                            ArraySizeExpr);
370     }
371     break;
372   }
373   case CXXNewExpr::ListInit: {
374     // Range of the substring that we do not want to remove.
375     SourceRange InitRange;
376     if (const auto *NewConstruct = New->getConstructExpr()) {
377       if (NewConstruct->isStdInitListInitialization() ||
378           HasListIntializedArgument(NewConstruct)) {
379         // FIXME: Add fixes for direct initialization with the initializer-list
380         // constructor. Similar to the above CallInit case, the type has to be
381         // specified explicitly in the fixes.
382         //   struct S { S(std::initializer_list<int>); };
383         //   struct S2 { S2(S, int); };
384         //   smart_ptr<S>(new S{1, 2, 3});  // C++11 direct list-initialization
385         //   smart_ptr<S>(new S{});  // use initializer-list constructor
386         //   smart_ptr<S2>()new S2{ {1,2}, 3 }; // have a list-initialized arg
387         // The above cases have to be replaced with:
388         //   std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}));
389         //   std::make_smart_ptr<S>(std::initializer_list<int>({}));
390         //   std::make_smart_ptr<S2>(S{1, 2}, 3);
391         return false;
392       } else {
393         // Direct initialization with ordinary constructors.
394         //   struct S { S(int x); S(); };
395         //   smart_ptr<S>(new S{5});
396         //   smart_ptr<S>(new S{}); // use default constructor
397         // The arguments in the initialization list are going to be forwarded to
398         // the constructor, so this has to be replaced with:
399         //   std::make_smart_ptr<S>(5);
400         //   std::make_smart_ptr<S>();
401         InitRange = SourceRange(
402             NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
403             NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
404       }
405     } else {
406       // Aggregate initialization.
407       //   smart_ptr<Pair>(new Pair{first, second});
408       // Has to be replaced with:
409       //   smart_ptr<Pair>(Pair{first, second});
410       //
411       // The fix (std::make_unique) needs to see copy/move constructor of
412       // Pair. If we found any invisible or deleted copy/move constructor, we
413       // stop generating fixes -- as the C++ rule is complicated and we are less
414       // certain about the correct fixes.
415       if (const CXXRecordDecl *RD = New->getType()->getPointeeCXXRecordDecl()) {
416         if (llvm::find_if(RD->ctors(), [](const CXXConstructorDecl *Ctor) {
417               return Ctor->isCopyOrMoveConstructor() &&
418                      (Ctor->isDeleted() || Ctor->getAccess() == AS_private);
419             }) != RD->ctor_end()) {
420           return false;
421         }
422       }
423       InitRange = SourceRange(
424           New->getAllocatedTypeSourceInfo()->getTypeLoc().getBeginLoc(),
425           New->getInitializer()->getSourceRange().getEnd());
426     }
427     Diag << FixItHint::CreateRemoval(
428         CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
429     Diag << FixItHint::CreateRemoval(
430         SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));
431     break;
432   }
433   }
434   return true;
435 }
436 
insertHeader(DiagnosticBuilder & Diag,FileID FD)437 void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) {
438   if (MakeSmartPtrFunctionHeader.empty()) {
439     return;
440   }
441   Diag << Inserter.createIncludeInsertion(FD, MakeSmartPtrFunctionHeader);
442 }
443 
444 } // namespace modernize
445 } // namespace tidy
446 } // namespace clang
447