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