1 //===--- MakeSmartPtrCheck.cpp - clang-tidy--------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
15 using namespace clang::ast_matchers
;
17 namespace clang::tidy::modernize
{
21 constexpr char ConstructorCall
[] = "constructorCall";
22 constexpr char ResetCall
[] = "resetCall";
23 constexpr char NewExpression
[] = "newExpression";
25 std::string
getNewExprName(const CXXNewExpr
*NewExpr
, const SourceManager
&SM
,
26 const LangOptions
&Lang
) {
27 StringRef WrittenName
= Lexer::getSourceText(
28 CharSourceRange::getTokenRange(
29 NewExpr
->getAllocatedTypeSourceInfo()->getTypeLoc().getSourceRange()),
31 if (NewExpr
->isArray()) {
32 return (WrittenName
+ "[]").str();
34 return WrittenName
.str();
39 const char MakeSmartPtrCheck::PointerType
[] = "pointerType";
41 MakeSmartPtrCheck::MakeSmartPtrCheck(StringRef Name
, ClangTidyContext
*Context
,
42 StringRef MakeSmartPtrFunctionName
)
43 : ClangTidyCheck(Name
, Context
),
44 Inserter(Options
.getLocalOrGlobal("IncludeStyle",
45 utils::IncludeSorter::IS_LLVM
),
46 areDiagsSelfContained()),
47 MakeSmartPtrFunctionHeader(
48 Options
.get("MakeSmartPtrFunctionHeader", "<memory>")),
49 MakeSmartPtrFunctionName(
50 Options
.get("MakeSmartPtrFunction", MakeSmartPtrFunctionName
)),
51 IgnoreMacros(Options
.getLocalOrGlobal("IgnoreMacros", true)),
52 IgnoreDefaultInitialization(
53 Options
.get("IgnoreDefaultInitialization", true)) {}
55 void MakeSmartPtrCheck::storeOptions(ClangTidyOptions::OptionMap
&Opts
) {
56 Options
.store(Opts
, "IncludeStyle", Inserter
.getStyle());
57 Options
.store(Opts
, "MakeSmartPtrFunctionHeader", MakeSmartPtrFunctionHeader
);
58 Options
.store(Opts
, "MakeSmartPtrFunction", MakeSmartPtrFunctionName
);
59 Options
.store(Opts
, "IgnoreMacros", IgnoreMacros
);
60 Options
.store(Opts
, "IgnoreDefaultInitialization",
61 IgnoreDefaultInitialization
);
64 bool MakeSmartPtrCheck::isLanguageVersionSupported(
65 const LangOptions
&LangOpts
) const {
66 return LangOpts
.CPlusPlus11
;
69 void MakeSmartPtrCheck::registerPPCallbacks(const SourceManager
&SM
,
71 Preprocessor
*ModuleExpanderPP
) {
72 Inserter
.registerPreprocessor(PP
);
75 void MakeSmartPtrCheck::registerMatchers(ast_matchers::MatchFinder
*Finder
) {
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())))))));
81 auto IsPlacement
= hasAnyPlacementArg(anything());
86 cxxBindTemporaryExpr(has(ignoringParenImpCasts(
88 hasType(getSmartPointerTypeMatcher()), argumentCountIs(1),
90 0, cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
91 equalsBoundNode(PointerType
))))),
92 CanCallCtor
, unless(IsPlacement
))
93 .bind(NewExpression
)),
94 unless(isInTemplateInstantiation()))
95 .bind(ConstructorCall
))))),
102 unless(isInTemplateInstantiation()),
103 hasArgument(0, cxxNewExpr(CanCallCtor
, unless(IsPlacement
))
104 .bind(NewExpression
)),
105 callee(cxxMethodDecl(hasName("reset"))),
106 anyOf(thisPointerType(getSmartPointerTypeMatcher()),
107 on(ignoringImplicit(anyOf(
108 hasType(getSmartPointerTypeMatcher()),
109 hasType(pointsTo(getSmartPointerTypeMatcher())))))))
114 void MakeSmartPtrCheck::check(const MatchFinder::MatchResult
&Result
) {
115 // 'smart_ptr' refers to 'std::shared_ptr' or 'std::unique_ptr' or other
116 // pointer, 'make_smart_ptr' refers to 'std::make_shared' or
117 // 'std::make_unique' or other function that creates smart_ptr.
119 SourceManager
&SM
= *Result
.SourceManager
;
120 const auto *Construct
=
121 Result
.Nodes
.getNodeAs
<CXXConstructExpr
>(ConstructorCall
);
122 const auto *Reset
= Result
.Nodes
.getNodeAs
<CXXMemberCallExpr
>(ResetCall
);
123 const auto *Type
= Result
.Nodes
.getNodeAs
<QualType
>(PointerType
);
124 const auto *New
= Result
.Nodes
.getNodeAs
<CXXNewExpr
>(NewExpression
);
126 // Skip when this is a new-expression with `auto`, e.g. new auto(1)
127 if (New
->getType()->getPointeeType()->getContainedAutoType())
130 // Be conservative for cases where we construct and default initialize.
133 // P.reset(new int) // check fix: P = std::make_unique<int>()
134 // P.reset(new int[5]) // check fix: P = std::make_unique<int []>(5)
136 // The fix of the check has side effect, it introduces value initialization
137 // which maybe unexpected and cause performance regression.
138 bool Initializes
= New
->hasInitializer() ||
139 !utils::type_traits::isTriviallyDefaultConstructible(
140 New
->getAllocatedType(), *Result
.Context
);
141 if (!Initializes
&& IgnoreDefaultInitialization
)
144 checkConstruct(SM
, Result
.Context
, Construct
, Type
, New
);
146 checkReset(SM
, Result
.Context
, Reset
, New
);
149 void MakeSmartPtrCheck::checkConstruct(SourceManager
&SM
, ASTContext
*Ctx
,
150 const CXXConstructExpr
*Construct
,
151 const QualType
*Type
,
152 const CXXNewExpr
*New
) {
153 SourceLocation ConstructCallStart
= Construct
->getExprLoc();
154 bool InMacro
= ConstructCallStart
.isMacroID();
156 if (InMacro
&& IgnoreMacros
) {
160 bool Invalid
= false;
161 StringRef ExprStr
= Lexer::getSourceText(
162 CharSourceRange::getCharRange(
163 ConstructCallStart
, Construct
->getParenOrBraceRange().getBegin()),
164 SM
, getLangOpts(), &Invalid
);
168 auto Diag
= diag(ConstructCallStart
, "use %0 instead")
169 << MakeSmartPtrFunctionName
;
171 // Disable the fix in macros.
176 if (!replaceNew(Diag
, New
, SM
, Ctx
)) {
180 // Find the location of the template's left angle.
181 size_t LAngle
= ExprStr
.find('<');
182 SourceLocation ConstructCallEnd
;
183 if (LAngle
== StringRef::npos
) {
184 // If the template argument is missing (because it is part of the alias)
185 // we have to add it back.
186 ConstructCallEnd
= ConstructCallStart
.getLocWithOffset(ExprStr
.size());
187 Diag
<< FixItHint::CreateInsertion(
188 ConstructCallEnd
, "<" + getNewExprName(New
, SM
, getLangOpts()) + ">");
190 ConstructCallEnd
= ConstructCallStart
.getLocWithOffset(LAngle
);
193 Diag
<< FixItHint::CreateReplacement(
194 CharSourceRange::getCharRange(ConstructCallStart
, ConstructCallEnd
),
195 MakeSmartPtrFunctionName
);
197 // If the smart_ptr is built with brace enclosed direct initialization, use
198 // parenthesis instead.
199 if (Construct
->isListInitialization()) {
200 SourceRange BraceRange
= Construct
->getParenOrBraceRange();
201 Diag
<< FixItHint::CreateReplacement(
202 CharSourceRange::getCharRange(
203 BraceRange
.getBegin(), BraceRange
.getBegin().getLocWithOffset(1)),
205 Diag
<< FixItHint::CreateReplacement(
206 CharSourceRange::getCharRange(BraceRange
.getEnd(),
207 BraceRange
.getEnd().getLocWithOffset(1)),
211 insertHeader(Diag
, SM
.getFileID(ConstructCallStart
));
214 void MakeSmartPtrCheck::checkReset(SourceManager
&SM
, ASTContext
*Ctx
,
215 const CXXMemberCallExpr
*Reset
,
216 const CXXNewExpr
*New
) {
217 const auto *Expr
= cast
<MemberExpr
>(Reset
->getCallee());
218 SourceLocation OperatorLoc
= Expr
->getOperatorLoc();
219 SourceLocation ResetCallStart
= Reset
->getExprLoc();
220 SourceLocation ExprStart
= Expr
->getBeginLoc();
221 SourceLocation ExprEnd
=
222 Lexer::getLocForEndOfToken(Expr
->getEndLoc(), 0, SM
, getLangOpts());
224 bool InMacro
= ExprStart
.isMacroID();
226 if (InMacro
&& IgnoreMacros
) {
230 // There are some cases where we don't have operator ("." or "->") of the
231 // "reset" expression, e.g. call "reset()" method directly in the subclass of
232 // "std::unique_ptr<>". We skip these cases.
233 if (OperatorLoc
.isInvalid()) {
237 auto Diag
= diag(ResetCallStart
, "use %0 instead")
238 << MakeSmartPtrFunctionName
;
240 // Disable the fix in macros.
245 if (!replaceNew(Diag
, New
, SM
, Ctx
)) {
249 Diag
<< FixItHint::CreateReplacement(
250 CharSourceRange::getCharRange(OperatorLoc
, ExprEnd
),
251 (llvm::Twine(" = ") + MakeSmartPtrFunctionName
+ "<" +
252 getNewExprName(New
, SM
, getLangOpts()) + ">")
256 Diag
<< FixItHint::CreateInsertion(ExprStart
, "*");
258 insertHeader(Diag
, SM
.getFileID(OperatorLoc
));
261 bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder
&Diag
,
262 const CXXNewExpr
*New
, SourceManager
&SM
,
264 auto SkipParensParents
= [&](const Expr
*E
) {
265 TraversalKindScope
RAII(*Ctx
, TK_AsIs
);
267 for (const Expr
*OldE
= nullptr; E
!= OldE
;) {
269 for (const auto &Node
: Ctx
->getParents(*E
)) {
270 if (const Expr
*Parent
= Node
.get
<ParenExpr
>()) {
279 SourceRange NewRange
= SkipParensParents(New
)->getSourceRange();
280 SourceLocation NewStart
= NewRange
.getBegin();
281 SourceLocation NewEnd
= NewRange
.getEnd();
283 // Skip when the source location of the new expression is invalid.
284 if (NewStart
.isInvalid() || NewEnd
.isInvalid())
287 std::string ArraySizeExpr
;
288 if (const auto *ArraySize
= New
->getArraySize().value_or(nullptr)) {
289 ArraySizeExpr
= Lexer::getSourceText(CharSourceRange::getTokenRange(
290 ArraySize
->getSourceRange()),
294 // Returns true if the given constructor expression has any braced-init-list
296 // Foo({1, 2}, 1) => true
297 // Foo(Bar{1, 2}) => true
300 auto HasListIntializedArgument
= [](const CXXConstructExpr
*CE
) {
301 for (const auto *Arg
: CE
->arguments()) {
302 Arg
= Arg
->IgnoreImplicit();
304 if (isa
<CXXStdInitializerListExpr
>(Arg
) || isa
<InitListExpr
>(Arg
))
306 // Check whether we implicitly construct a class from a
307 // std::initializer_list.
308 if (const auto *CEArg
= dyn_cast
<CXXConstructExpr
>(Arg
)) {
309 // Strip the elidable move constructor, it is present in the AST for
310 // C++11/14, e.g. Foo(Bar{1, 2}), the move constructor is around the
311 // init-list constructor.
312 if (CEArg
->isElidable()) {
313 if (const auto *TempExp
= CEArg
->getArg(0)) {
314 if (const auto *UnwrappedCE
=
315 dyn_cast
<CXXConstructExpr
>(TempExp
->IgnoreImplicit()))
319 if (CEArg
->isStdInitListInitialization())
325 switch (New
->getInitializationStyle()) {
326 case CXXNewInitializationStyle::None
: {
327 if (ArraySizeExpr
.empty()) {
328 Diag
<< FixItHint::CreateRemoval(SourceRange(NewStart
, NewEnd
));
330 // New array expression without written initializer:
331 // smart_ptr<Foo[]>(new Foo[5]);
332 Diag
<< FixItHint::CreateReplacement(SourceRange(NewStart
, NewEnd
),
337 case CXXNewInitializationStyle::Parens
: {
338 // FIXME: Add fixes for constructors with parameters that can be created
339 // with a C++11 braced-init-list (e.g. std::vector, std::map).
340 // Unlike ordinal cases, braced list can not be deduced in
341 // std::make_smart_ptr, we need to specify the type explicitly in the fixes:
342 // struct S { S(std::initializer_list<int>, int); };
343 // struct S2 { S2(std::vector<int>); };
344 // struct S3 { S3(S2, int); };
345 // smart_ptr<S>(new S({1, 2, 3}, 1)); // C++98 call-style initialization
346 // smart_ptr<S>(new S({}, 1));
347 // smart_ptr<S2>(new S2({1})); // implicit conversion:
348 // // std::initializer_list => std::vector
349 // smart_ptr<S3>(new S3({1, 2}, 3));
350 // The above samples have to be replaced with:
351 // std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}), 1);
352 // std::make_smart_ptr<S>(std::initializer_list<int>({}), 1);
353 // std::make_smart_ptr<S2>(std::vector<int>({1}));
354 // std::make_smart_ptr<S3>(S2{1, 2}, 3);
355 if (const auto *CE
= New
->getConstructExpr()) {
356 if (HasListIntializedArgument(CE
))
359 if (ArraySizeExpr
.empty()) {
360 SourceRange InitRange
= New
->getDirectInitRange();
361 Diag
<< FixItHint::CreateRemoval(
362 SourceRange(NewStart
, InitRange
.getBegin()));
363 Diag
<< FixItHint::CreateRemoval(SourceRange(InitRange
.getEnd(), NewEnd
));
366 // New array expression with default/value initialization:
367 // smart_ptr<Foo[]>(new int[5]());
368 // smart_ptr<Foo[]>(new Foo[5]());
369 Diag
<< FixItHint::CreateReplacement(SourceRange(NewStart
, NewEnd
),
374 case CXXNewInitializationStyle::Braces
: {
375 // Range of the substring that we do not want to remove.
376 SourceRange InitRange
;
377 if (const auto *NewConstruct
= New
->getConstructExpr()) {
378 if (NewConstruct
->isStdInitListInitialization() ||
379 HasListIntializedArgument(NewConstruct
)) {
380 // FIXME: Add fixes for direct initialization with the initializer-list
381 // constructor. Similar to the above CallInit case, the type has to be
382 // specified explicitly in the fixes.
383 // struct S { S(std::initializer_list<int>); };
384 // struct S2 { S2(S, int); };
385 // smart_ptr<S>(new S{1, 2, 3}); // C++11 direct list-initialization
386 // smart_ptr<S>(new S{}); // use initializer-list constructor
387 // smart_ptr<S2>()new S2{ {1,2}, 3 }; // have a list-initialized arg
388 // The above cases have to be replaced with:
389 // std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}));
390 // std::make_smart_ptr<S>(std::initializer_list<int>({}));
391 // std::make_smart_ptr<S2>(S{1, 2}, 3);
394 // Direct initialization with ordinary constructors.
395 // struct S { S(int x); S(); };
396 // smart_ptr<S>(new S{5});
397 // smart_ptr<S>(new S{}); // use default constructor
398 // The arguments in the initialization list are going to be forwarded to
399 // the constructor, so this has to be replaced with:
400 // std::make_smart_ptr<S>(5);
401 // std::make_smart_ptr<S>();
402 InitRange
= SourceRange(
403 NewConstruct
->getParenOrBraceRange().getBegin().getLocWithOffset(1),
404 NewConstruct
->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
406 // Aggregate initialization.
407 // smart_ptr<Pair>(new Pair{first, second});
408 // Has to be replaced with:
409 // smart_ptr<Pair>(Pair{first, second});
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::any_of(RD
->ctors(), [](const CXXConstructorDecl
*Ctor
) {
417 return Ctor
->isCopyOrMoveConstructor() &&
418 (Ctor
->isDeleted() || Ctor
->getAccess() == AS_private
);
423 InitRange
= SourceRange(
424 New
->getAllocatedTypeSourceInfo()->getTypeLoc().getBeginLoc(),
425 New
->getInitializer()->getSourceRange().getEnd());
427 Diag
<< FixItHint::CreateRemoval(
428 CharSourceRange::getCharRange(NewStart
, InitRange
.getBegin()));
429 Diag
<< FixItHint::CreateRemoval(
430 SourceRange(InitRange
.getEnd().getLocWithOffset(1), NewEnd
));
437 void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder
&Diag
, FileID FD
) {
438 if (MakeSmartPtrFunctionHeader
.empty()) {
441 Diag
<< Inserter
.createIncludeInsertion(FD
, MakeSmartPtrFunctionHeader
);
444 } // namespace clang::tidy::modernize