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