clang-tools 19.0.0git
MakeSmartPtrCheck.cpp
Go to the documentation of this file.
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
15using namespace clang::ast_matchers;
16
17namespace clang::tidy::modernize {
18
19namespace {
20
21constexpr char ConstructorCall[] = "constructorCall";
22constexpr char ResetCall[] = "resetCall";
23constexpr char NewExpression[] = "newExpression";
24
25std::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()),
30 SM, Lang);
31 if (NewExpr->isArray()) {
32 return (WrittenName + "[]").str();
33 }
34 return WrittenName.str();
35}
36
37} // namespace
38
39const char MakeSmartPtrCheck::PointerType[] = "pointerType";
40
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)) {}
54
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);
62}
63
65 const LangOptions &LangOpts) const {
66 return LangOpts.CPlusPlus11;
67}
68
69void MakeSmartPtrCheck::registerPPCallbacks(const SourceManager &SM,
70 Preprocessor *PP,
71 Preprocessor *ModuleExpanderPP) {
72 Inserter.registerPreprocessor(PP);
73}
74
75void 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())))))));
80
81 auto IsPlacement = hasAnyPlacementArg(anything());
82
83 Finder->addMatcher(
84 traverse(
85 TK_AsIs,
86 cxxBindTemporaryExpr(has(ignoringParenImpCasts(
87 cxxConstructExpr(
88 hasType(getSmartPointerTypeMatcher()), argumentCountIs(1),
89 hasArgument(
90 0, cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
91 equalsBoundNode(PointerType))))),
92 CanCallCtor, unless(IsPlacement))
93 .bind(NewExpression)),
94 unless(isInTemplateInstantiation()))
95 .bind(ConstructorCall))))),
96 this);
97
98 Finder->addMatcher(
99 traverse(
100 TK_AsIs,
101 cxxMemberCallExpr(
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(
109 hasType(pointsTo(getSmartPointerTypeMatcher())))))))
110 .bind(ResetCall)),
111 this);
112}
113
114void 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.
118
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);
125
126 // Skip when this is a new-expression with `auto`, e.g. new auto(1)
127 if (New->getType()->getPointeeType()->getContainedAutoType())
128 return;
129
130 // Be conservative for cases where we construct and default initialize.
131 //
132 // For example,
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)
135 //
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() ||
140 New->getAllocatedType(), *Result.Context);
141 if (!Initializes && IgnoreDefaultInitialization)
142 return;
143 if (Construct)
144 checkConstruct(SM, Result.Context, Construct, Type, New);
145 else if (Reset)
146 checkReset(SM, Result.Context, Reset, New);
147}
148
149void 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();
155
156 if (InMacro && IgnoreMacros) {
157 return;
158 }
159
160 bool Invalid = false;
161 StringRef ExprStr = Lexer::getSourceText(
162 CharSourceRange::getCharRange(
163 ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
164 SM, getLangOpts(), &Invalid);
165 if (Invalid)
166 return;
167
168 auto Diag = diag(ConstructCallStart, "use %0 instead")
169 << MakeSmartPtrFunctionName;
170
171 // Disable the fix in macros.
172 if (InMacro) {
173 return;
174 }
175
176 if (!replaceNew(Diag, New, SM, Ctx)) {
177 return;
178 }
179
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()) + ">");
189 } else {
190 ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
191 }
192
193 Diag << FixItHint::CreateReplacement(
194 CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
195 MakeSmartPtrFunctionName);
196
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)),
204 "(");
205 Diag << FixItHint::CreateReplacement(
206 CharSourceRange::getCharRange(BraceRange.getEnd(),
207 BraceRange.getEnd().getLocWithOffset(1)),
208 ")");
209 }
210
211 insertHeader(Diag, SM.getFileID(ConstructCallStart));
212}
213
214void 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());
223
224 bool InMacro = ExprStart.isMacroID();
225
226 if (InMacro && IgnoreMacros) {
227 return;
228 }
229
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()) {
234 return;
235 }
236
237 auto Diag = diag(ResetCallStart, "use %0 instead")
238 << MakeSmartPtrFunctionName;
239
240 // Disable the fix in macros.
241 if (InMacro) {
242 return;
243 }
244
245 if (!replaceNew(Diag, New, SM, Ctx)) {
246 return;
247 }
248
249 Diag << FixItHint::CreateReplacement(
250 CharSourceRange::getCharRange(OperatorLoc, ExprEnd),
251 (llvm::Twine(" = ") + MakeSmartPtrFunctionName + "<" +
252 getNewExprName(New, SM, getLangOpts()) + ">")
253 .str());
254
255 if (Expr->isArrow())
256 Diag << FixItHint::CreateInsertion(ExprStart, "*");
257
258 insertHeader(Diag, SM.getFileID(OperatorLoc));
259}
260
261bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag,
262 const CXXNewExpr *New, SourceManager &SM,
263 ASTContext *Ctx) {
264 auto SkipParensParents = [&](const Expr *E) {
265 TraversalKindScope RAII(*Ctx, TK_AsIs);
266
267 for (const Expr *OldE = nullptr; E != OldE;) {
268 OldE = E;
269 for (const auto &Node : Ctx->getParents(*E)) {
270 if (const Expr *Parent = Node.get<ParenExpr>()) {
271 E = Parent;
272 break;
273 }
274 }
275 }
276 return E;
277 };
278
279 SourceRange NewRange = SkipParensParents(New)->getSourceRange();
280 SourceLocation NewStart = NewRange.getBegin();
281 SourceLocation NewEnd = NewRange.getEnd();
282
283 // Skip when the source location of the new expression is invalid.
284 if (NewStart.isInvalid() || NewEnd.isInvalid())
285 return false;
286
287 std::string ArraySizeExpr;
288 if (const auto *ArraySize = New->getArraySize().value_or(nullptr)) {
289 ArraySizeExpr = Lexer::getSourceText(CharSourceRange::getTokenRange(
290 ArraySize->getSourceRange()),
291 SM, getLangOpts())
292 .str();
293 }
294 // Returns true if the given constructor expression has any braced-init-list
295 // argument, e.g.
296 // Foo({1, 2}, 1) => true
297 // Foo(Bar{1, 2}) => true
298 // Foo(1) => false
299 // Foo{1} => false
300 auto HasListIntializedArgument = [](const CXXConstructExpr *CE) {
301 for (const auto *Arg : CE->arguments()) {
302 Arg = Arg->IgnoreImplicit();
303
304 if (isa<CXXStdInitializerListExpr>(Arg) || isa<InitListExpr>(Arg))
305 return true;
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()))
316 CEArg = UnwrappedCE;
317 }
318 }
319 if (CEArg->isStdInitListInitialization())
320 return true;
321 }
322 }
323 return false;
324 };
325 switch (New->getInitializationStyle()) {
326 case CXXNewInitializationStyle::None: {
327 if (ArraySizeExpr.empty()) {
328 Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
329 } else {
330 // New array expression without written initializer:
331 // smart_ptr<Foo[]>(new Foo[5]);
332 Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
333 ArraySizeExpr);
334 }
335 break;
336 }
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))
357 return false;
358 }
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));
364 }
365 else {
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),
370 ArraySizeExpr);
371 }
372 break;
373 }
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);
392 return false;
393 }
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));
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::any_of(RD->ctors(), [](const CXXConstructorDecl *Ctor) {
417 return Ctor->isCopyOrMoveConstructor() &&
418 (Ctor->isDeleted() || Ctor->getAccess() == AS_private);
419 })) {
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
437void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) {
438 if (MakeSmartPtrFunctionHeader.empty()) {
439 return;
440 }
441 Diag << Inserter.createIncludeInsertion(FD, MakeSmartPtrFunctionHeader);
442}
443
444} // namespace clang::tidy::modernize
const Expr * E
CaptureExpr CE
llvm::SmallString< 256U > Name
const Node * Parent
NodeType Type
::clang::DynTypedNode Node
std::string Lang
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
const LangOptions & getLangOpts() const
Returns the language options from the context.
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
ClangTidyChecks that register ASTMatchers should do the actual work in here.
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
Override this to register PPCallbacks in the preprocessor.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
void registerMatchers(ast_matchers::MatchFinder *Finder) final
Override this to register AST matchers with Finder.
void registerPreprocessor(Preprocessor *PP)
Registers this with the Preprocessor PP, must be called before this class is used.
std::optional< FixItHint > createIncludeInsertion(FileID FileID, llvm::StringRef Header)
Creates a Header inclusion directive fixit in the File FileID.
IncludeSorter::IncludeStyle getStyle() const
bool isTriviallyDefaultConstructible(QualType Type, const ASTContext &Context)
Returns true if Type is trivially default constructible.
Definition: TypeTraits.cpp:90
llvm::StringMap< ClangTidyValue > OptionMap