clang-tools 8.0.0

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