clang-tools 10.0.0

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 "MakeSharedCheck.h"
10 #include "clang/Frontend/CompilerInstance.h"
11 #include "clang/Lex/Lexer.h"
12 #include "clang/Lex/Preprocessor.h"
13
14 using namespace clang::ast_matchers;
15
16 namespace clang {
17 namespace tidy {
18 namespace modernize {
19
20 namespace {
21
22 constexpr char StdMemoryHeader[] = "memory";
23 constexpr char ConstructorCall[] = "constructorCall";
24 constexpr char ResetCall[] = "resetCall";
25 constexpr char NewExpression[] = "newExpression";
26
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
44 MakeSmartPtrCheck::MakeSmartPtrCheck(StringRef Name,
45 ClangTidyContext* Context,
46 StringRef MakeSmartPtrFunctionName)
47 : ClangTidyCheck(Name, Context),
48 IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
49 Options.getLocalOrGlobal("IncludeStyle", "llvm"))),
50 MakeSmartPtrFunctionHeader(
51 Options.get("MakeSmartPtrFunctionHeader", StdMemoryHeader)),
52 MakeSmartPtrFunctionName(
53 Options.get("MakeSmartPtrFunction", MakeSmartPtrFunctionName)),
54 IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)) {}
55
56 void MakeSmartPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
57 Options.store(Opts, "IncludeStyle", IncludeStyle);
58 Options.store(Opts, "MakeSmartPtrFunctionHeader", MakeSmartPtrFunctionHeader);
59 Options.store(Opts, "MakeSmartPtrFunction", MakeSmartPtrFunctionName);
60 Options.store(Opts, "IgnoreMacros", IgnoreMacros);
61 }
62
63 bool MakeSmartPtrCheck::isLanguageVersionSupported(
64 const LangOptions &LangOpts) const {
65 return LangOpts.CPlusPlus11;
66 }
67
68 void MakeSmartPtrCheck::registerPPCallbacks(const SourceManager &SM,
69 Preprocessor *PP,
70 Preprocessor *ModuleExpanderPP) {
71 if (isLanguageVersionSupported(getLangOpts())) {
72 Inserter = std::make_unique<utils::IncludeInserter>(SM, getLangOpts(),
73 IncludeStyle);
74 PP->addPPCallbacks(Inserter->CreatePPCallbacks());
75 }
76 }
77
78 void MakeSmartPtrCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
79 if (!isLanguageVersionSupported(getLangOpts()))
80 return;
81
82 // Calling make_smart_ptr from within a member function of a type with a
83 // private or protected constructor would be ill-formed.
84 auto CanCallCtor = unless(has(ignoringImpCasts(
85 cxxConstructExpr(hasDeclaration(decl(unless(isPublic())))))));
86
87 Finder->addMatcher(
88 cxxBindTemporaryExpr(has(ignoringParenImpCasts(
89 cxxConstructExpr(
90 hasType(getSmartPointerTypeMatcher()), argumentCountIs(1),
91 hasArgument(0,
92 cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
93 equalsBoundNode(PointerType))))),
94 CanCallCtor)
95 .bind(NewExpression)),
96 unless(isInTemplateInstantiation()))
97 .bind(ConstructorCall)))),
98 this);
99
100 Finder->addMatcher(
101 cxxMemberCallExpr(
102 thisPointerType(getSmartPointerTypeMatcher()),
103 callee(cxxMethodDecl(hasName("reset"))),
104 hasArgument(0, cxxNewExpr(CanCallCtor).bind(NewExpression)),
105 unless(isInTemplateInstantiation()))
106 .bind(ResetCall),
107 this);
108 }
109
110 void MakeSmartPtrCheck::check(const MatchFinder::MatchResult &Result) {
111 // 'smart_ptr' refers to 'std::shared_ptr' or 'std::unique_ptr' or other
112 // pointer, 'make_smart_ptr' refers to 'std::make_shared' or
113 // 'std::make_unique' or other function that creates smart_ptr.
114
115 SourceManager &SM = *Result.SourceManager;
116 const auto *Construct =
117 Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructorCall);
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 if (New->getNumPlacementArgs() != 0)
123 return;
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 an array without any
129 // initalization.
130 // For example,
131 // P.reset(new int[5]) // check fix: P = std::make_unique<int []>(5)
132 //
133 // The fix of the check has side effect, it introduces default initialization
134 // which maybe unexpected and cause performance regression.
135 if (New->isArray() && !New->hasInitializer())
136 return;
137 if (Construct)
138 checkConstruct(SM, Result.Context, Construct, Type, New);
139 else if (Reset)
140 checkReset(SM, Result.Context, Reset, New);
141 }
142
143 void MakeSmartPtrCheck::checkConstruct(SourceManager &SM, ASTContext *Ctx,
144 const CXXConstructExpr *Construct,
145 const QualType *Type,
146 const CXXNewExpr *New) {
147 SourceLocation ConstructCallStart = Construct->getExprLoc();
148 bool InMacro = ConstructCallStart.isMacroID();
149
150 if (InMacro && IgnoreMacros) {
151 return;
152 }
153
154 bool Invalid = false;
155 StringRef ExprStr = Lexer::getSourceText(
156 CharSourceRange::getCharRange(
157 ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
158 SM, getLangOpts(), &Invalid);
159 if (Invalid)
160 return;
161
162 auto Diag = diag(ConstructCallStart, "use %0 instead")
163 << MakeSmartPtrFunctionName;
164
165 // Disable the fix in macros.
166 if (InMacro) {
167 return;
168 }
169
170 if (!replaceNew(Diag, New, SM, Ctx)) {
171 return;
172 }
173
174 // Find the location of the template's left angle.
175 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,
183 "<" + GetNewExprName(New, SM, getLangOpts()) + ">");
184 } else {
185 ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
186 }
187
188 Diag << FixItHint::CreateReplacement(
189 CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
190 MakeSmartPtrFunctionName);
191
192 // If the smart_ptr is built with brace enclosed direct initialization, use
193 // parenthesis instead.
194 if (Construct->isListInitialization()) {
195 SourceRange BraceRange = Construct->getParenOrBraceRange();
196 Diag << FixItHint::CreateReplacement(
197 CharSourceRange::getCharRange(
198 BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
199 "(");
200 Diag << FixItHint::CreateReplacement(
201 CharSourceRange::getCharRange(BraceRange.getEnd(),
202 BraceRange.getEnd().getLocWithOffset(1)),
203 ")");
204 }
205
206 insertHeader(Diag, SM.getFileID(ConstructCallStart));
207 }
208
209 void MakeSmartPtrCheck::checkReset(SourceManager &SM, ASTContext *Ctx,
210 const CXXMemberCallExpr *Reset,
211 const CXXNewExpr *New) {
212 const auto *Expr = cast<MemberExpr>(Reset->getCallee());
213 SourceLocation OperatorLoc = Expr->getOperatorLoc();
214 SourceLocation ResetCallStart = Reset->getExprLoc();
215 SourceLocation ExprStart = Expr->getBeginLoc();
216 SourceLocation ExprEnd =
217 Lexer::getLocForEndOfToken(Expr->getEndLoc(), 0, SM, getLangOpts());
218
219 bool InMacro = ExprStart.isMacroID();
220
221 if (InMacro && IgnoreMacros) {
222 return;
223 }
224
225 // There are some cases where we don't have operator ("." or "->") of the
226 // "reset" expression, e.g. call "reset()" method directly in the subclass of
227 // "std::unique_ptr<>". We skip these cases.
228 if (OperatorLoc.isInvalid()) {
229 return;
230 }
231
232 auto Diag = diag(ResetCallStart, "use %0 instead")
233 << MakeSmartPtrFunctionName;
234
235 // Disable the fix in macros.
236 if (InMacro) {
237 return;
238 }
239
240 if (!replaceNew(Diag, New, SM, Ctx)) {
241 return;
242 }
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
256 bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag,
257 const CXXNewExpr *New, SourceManager &SM,
258 ASTContext *Ctx) {
259 auto SkipParensParents = [&](const Expr *E) {
260 for (const Expr *OldE = nullptr; E != OldE;) {
261 OldE = E;
262 for (const auto &Node : Ctx->getParents(*E)) {
263 if (const Expr *Parent = Node.get<ParenExpr>()) {
264 E = Parent;
265 break;
266 }
267 }
268 }
269 return E;
270 };
271
272 SourceRange NewRange = SkipParensParents(New)->getSourceRange();
273 SourceLocation NewStart = NewRange.getBegin();
274 SourceLocation NewEnd = NewRange.getEnd();
275
276 // Skip when the source location of the new expression is invalid.
277 if (NewStart.isInvalid() || NewEnd.isInvalid())
278 return false;
279
280 std::string ArraySizeExpr;
281 if (const auto* ArraySize = New->getArraySize().getValueOr(nullptr)) {
282 ArraySizeExpr = Lexer::getSourceText(CharSourceRange::getTokenRange(
283 ArraySize->getSourceRange()),
284 SM, getLangOpts())
285 .str();
286 }
287 // Returns true if the given constructor expression has any braced-init-list
288 // argument, e.g.
289 // Foo({1, 2}, 1) => true
290 // Foo(Bar{1, 2}) => true
291 // Foo(1) => false
292 // Foo{1} => false
293 auto HasListIntializedArgument = [](const CXXConstructExpr *CE) {
294 for (const auto *Arg : CE->arguments()) {
295 Arg = Arg->IgnoreImplicit();
296
297 if (isa<CXXStdInitializerListExpr>(Arg) || isa<InitListExpr>(Arg))
298 return true;
299 // Check whether we implicitly construct a class from a
300 // std::initializer_list.
301 if (const auto *CEArg = dyn_cast<CXXConstructExpr>(Arg)) {
302 // Strip the elidable move constructor, it is present in the AST for
303 // C++11/14, e.g. Foo(Bar{1, 2}), the move constructor is around the
304 // init-list constructor.
305 if (CEArg->isElidable()) {
306 if (const auto *TempExp = CEArg->getArg(0)) {
307 if (const auto *UnwrappedCE =
308 dyn_cast<CXXConstructExpr>(TempExp->IgnoreImplicit()))
309 CEArg = UnwrappedCE;
310 }
311 }
312 if (CEArg->isStdInitListInitialization())
313 return true;
314 }
315 }
316 return false;
317 };
318 switch (New->getInitializationStyle()) {
319 case CXXNewExpr::NoInit: {
320 if (ArraySizeExpr.empty()) {
321 Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
322 } else {
323 // New array expression without written initializer:
324 // smart_ptr<Foo[]>(new Foo[5]);
325 Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
326 ArraySizeExpr);
327 }
328 break;
329 }
330 case CXXNewExpr::CallInit: {
331 // FIXME: Add fixes for constructors with parameters that can be created
332 // with a C++11 braced-init-list (e.g. std::vector, std::map).
333 // Unlike ordinal cases, braced list can not be deduced in
334 // std::make_smart_ptr, we need to specify the type explicitly in the fixes:
335 // struct S { S(std::initializer_list<int>, int); };
336 // struct S2 { S2(std::vector<int>); };
337 // struct S3 { S3(S2, int); };
338 // smart_ptr<S>(new S({1, 2, 3}, 1)); // C++98 call-style initialization
339 // smart_ptr<S>(new S({}, 1));
340 // smart_ptr<S2>(new S2({1})); // implicit conversion:
341 // // std::initializer_list => std::vector
342 // smart_ptr<S3>(new S3({1, 2}, 3));
343 // The above samples have to be replaced with:
344 // std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}), 1);
345 // std::make_smart_ptr<S>(std::initializer_list<int>({}), 1);
346 // std::make_smart_ptr<S2>(std::vector<int>({1}));
347 // std::make_smart_ptr<S3>(S2{1, 2}, 3);
348 if (const auto *CE = New->getConstructExpr()) {
349 if (HasListIntializedArgument(CE))
350 return false;
351 }
352 if (ArraySizeExpr.empty()) {
353 SourceRange InitRange = New->getDirectInitRange();
354 Diag << FixItHint::CreateRemoval(
355 SourceRange(NewStart, InitRange.getBegin()));
356 Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
357 }
358 else {
359 // New array expression with default/value initialization:
360 // smart_ptr<Foo[]>(new int[5]());
361 // smart_ptr<Foo[]>(new Foo[5]());
362 Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
363 ArraySizeExpr);
364 }
365 break;
366 }
367 case CXXNewExpr::ListInit: {
368 // Range of the substring that we do not want to remove.
369 SourceRange InitRange;
370 if (const auto *NewConstruct = New->getConstructExpr()) {
371 if (NewConstruct->isStdInitListInitialization() ||
372 HasListIntializedArgument(NewConstruct)) {
373 // FIXME: Add fixes for direct initialization with the initializer-list
374 // constructor. Similar to the above CallInit case, the type has to be
375 // specified explicitly in the fixes.
376 // struct S { S(std::initializer_list<int>); };
377 // struct S2 { S2(S, int); };
378 // smart_ptr<S>(new S{1, 2, 3}); // C++11 direct list-initialization
379 // smart_ptr<S>(new S{}); // use initializer-list constructor
380 // smart_ptr<S2>()new S2{ {1,2}, 3 }; // have a list-initialized arg
381 // The above cases have to be replaced with:
382 // std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}));
383 // std::make_smart_ptr<S>(std::initializer_list<int>({}));
384 // std::make_smart_ptr<S2>(S{1, 2}, 3);
385 return false;
386 } else {
387 // Direct initialization with ordinary constructors.
388 // struct S { S(int x); S(); };
389 // smart_ptr<S>(new S{5});
390 // smart_ptr<S>(new S{}); // use default constructor
391 // The arguments in the initialization list are going to be forwarded to
392 // the constructor, so this has to be replaced with:
393 // std::make_smart_ptr<S>(5);
394 // std::make_smart_ptr<S>();
395 InitRange = SourceRange(
396 NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
397 NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
398 }
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::find_if(RD->ctors(), [](const CXXConstructorDecl *Ctor) {
411 return Ctor->isCopyOrMoveConstructor() &&
412 (Ctor->isDeleted() || Ctor->getAccess() == AS_private);
413 }) != RD->ctor_end()) {
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
431 void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) {
432 if (MakeSmartPtrFunctionHeader.empty()) {
433 return;
434 }
435 if (auto IncludeFixit = Inserter->CreateIncludeInsertion(
436 FD, MakeSmartPtrFunctionHeader,
437 /*IsAngled=*/MakeSmartPtrFunctionHeader == StdMemoryHeader)) {
438 Diag << *IncludeFixit;
439 }
440 }
441
442 } // namespace modernize
443 } // namespace tidy
444 } // namespace clang
clang::ast_matchers
Definition: AbseilMatcher.h:14
Parent
const Node * Parent
Definition: ExtractFunction.cpp:148
clang::tidy::modernize::MakeSmartPtrCheck::isLanguageVersionSupported
virtual bool isLanguageVersionSupported(const LangOptions &LangOpts) const
Returns whether the C++ version is compatible with current check.
Definition: MakeSmartPtrCheck.cpp:63
clang::tidy::modernize::MakeSmartPtrCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) final
Override this to register AST matchers with Finder.
Definition: MakeSmartPtrCheck.cpp:78
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:47
clang::tidy::ClangTidyCheck::getLangOpts
const LangOptions & getLangOpts() const
Returns the language options from the context.
Definition: ClangTidyCheck.h:188
clang::tidy::modernize::MakeSmartPtrCheck::getSmartPointerTypeMatcher
virtual SmartPtrTypeMatcher getSmartPointerTypeMatcher() const =0
Returns matcher that match with different smart pointer types.
clang::tidy::modernize::MakeSmartPtrCheck::registerPPCallbacks
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
Override this to register PPCallbacks in the preprocessor.
Definition: MakeSmartPtrCheck.cpp:68
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:184
Ctx
Context Ctx
Definition: TUScheduler.cpp:237
clang::tidy::ClangTidyCheck::OptionsView::store
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.
Definition: ClangTidyCheck.cpp:58
clang::tidy::modernize::MakeSmartPtrCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) final
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: MakeSmartPtrCheck.cpp:110
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, std::string > OptionMap
Definition: ClangTidyOptions.h:97
clang::tidy::modernize::MakeSmartPtrCheck::PointerType
static const char PointerType[]
Definition: MakeSmartPtrCheck.h:46
MakeSharedCheck.h
clang::clangd::getTokenRange
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
Definition: SourceCode.cpp:227
clang::doc::serialize::isPublic
static bool isPublic(const clang::AccessSpecifier AS, const clang::Linkage Link)
Definition: Serialize.cpp:223
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::modernize::MakeSmartPtrCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Definition: MakeSmartPtrCheck.cpp:56
clang::tidy::bugprone::PP
static Preprocessor * PP
Definition: BadSignalToKillThreadCheck.cpp:28
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:77
E
const Expr * E
Definition: AvoidBindCheck.cpp:82
Type
NodeType Type
Definition: HTMLGenerator.cpp:73
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidyCheck.cpp:21