clang-tools 6.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
25 std::string GetNewExprName(const CXXNewExpr *NewExpr,
26 const SourceManager &SM,
27 const LangOptions &Lang) {
28 StringRef WrittenName = Lexer::getSourceText(
29 CharSourceRange::getTokenRange(
30 NewExpr->getAllocatedTypeSourceInfo()->getTypeLoc().getSourceRange()),
31 SM, Lang);
32 if (NewExpr->isArray()) {
33 return WrittenName.str() + "[]";
34 }
35 return WrittenName.str();
36 }
37
38 } // namespace
39
40 const char MakeSmartPtrCheck::PointerType[] = "pointerType";
41 const char MakeSmartPtrCheck::ConstructorCall[] = "constructorCall";
42 const char MakeSmartPtrCheck::ResetCall[] = "resetCall";
43 const char MakeSmartPtrCheck::NewExpression[] = "newExpression";
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 void MakeSmartPtrCheck::registerPPCallbacks(CompilerInstance &Compiler) {
65 if (getLangOpts().CPlusPlus11) {
66 Inserter.reset(new utils::IncludeInserter(
67 Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
68 Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
69 }
70 }
71
72 void MakeSmartPtrCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
73 if (!getLangOpts().CPlusPlus11)
74 return;
75
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 Finder->addMatcher(
82 cxxBindTemporaryExpr(has(ignoringParenImpCasts(
83 cxxConstructExpr(
84 hasType(getSmartPointerTypeMatcher()), argumentCountIs(1),
85 hasArgument(0,
86 cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
87 equalsBoundNode(PointerType))))),
88 CanCallCtor)
89 .bind(NewExpression)),
90 unless(isInTemplateInstantiation()))
91 .bind(ConstructorCall)))),
92 this);
93
94 Finder->addMatcher(
95 cxxMemberCallExpr(
96 thisPointerType(getSmartPointerTypeMatcher()),
97 callee(cxxMethodDecl(hasName("reset"))),
98 hasArgument(0, cxxNewExpr(CanCallCtor).bind(NewExpression)),
99 unless(isInTemplateInstantiation()))
100 .bind(ResetCall),
101 this);
102 }
103
104 void MakeSmartPtrCheck::check(const MatchFinder::MatchResult &Result) {
105 // 'smart_ptr' refers to 'std::shared_ptr' or 'std::unique_ptr' or other
106 // pointer, 'make_smart_ptr' refers to 'std::make_shared' or
107 // 'std::make_unique' or other function that creates smart_ptr.
108
109 SourceManager &SM = *Result.SourceManager;
110 const auto *Construct =
111 Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructorCall);
112 const auto *Reset = Result.Nodes.getNodeAs<CXXMemberCallExpr>(ResetCall);
113 const auto *Type = Result.Nodes.getNodeAs<QualType>(PointerType);
114 const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(NewExpression);
115
116 if (New->getNumPlacementArgs() != 0)
117 return;
118
119 if (Construct)
120 checkConstruct(SM, Construct, Type, New);
121 else if (Reset)
122 checkReset(SM, Reset, New);
123 }
124
125 void MakeSmartPtrCheck::checkConstruct(SourceManager &SM,
126 const CXXConstructExpr *Construct,
127 const QualType *Type,
128 const CXXNewExpr *New) {
129 SourceLocation ConstructCallStart = Construct->getExprLoc();
130 bool InMacro = ConstructCallStart.isMacroID();
131
132 if (InMacro && IgnoreMacros) {
133 return;
134 }
135
136 bool Invalid = false;
137 StringRef ExprStr = Lexer::getSourceText(
138 CharSourceRange::getCharRange(
139 ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
140 SM, getLangOpts(), &Invalid);
141 if (Invalid)
142 return;
143
144 auto Diag = diag(ConstructCallStart, "use %0 instead")
145 << MakeSmartPtrFunctionName;
146
147 // Disable the fix in macros.
148 if (InMacro) {
149 return;
150 }
151
152 if (!replaceNew(Diag, New, SM)) {
153 return;
154 }
155
156 // Find the location of the template's left angle.
157 size_t LAngle = ExprStr.find("<");
158 SourceLocation ConstructCallEnd;
159 if (LAngle == StringRef::npos) {
160 // If the template argument is missing (because it is part of the alias)
161 // we have to add it back.
162 ConstructCallEnd = ConstructCallStart.getLocWithOffset(ExprStr.size());
163 Diag << FixItHint::CreateInsertion(
164 ConstructCallEnd,
165 "<" + GetNewExprName(New, SM, getLangOpts()) + ">");
166 } else {
167 ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
168 }
169
170 Diag << FixItHint::CreateReplacement(
171 CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
172 MakeSmartPtrFunctionName);
173
174 // If the smart_ptr is built with brace enclosed direct initialization, use
175 // parenthesis instead.
176 if (Construct->isListInitialization()) {
177 SourceRange BraceRange = Construct->getParenOrBraceRange();
178 Diag << FixItHint::CreateReplacement(
179 CharSourceRange::getCharRange(
180 BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
181 "(");
182 Diag << FixItHint::CreateReplacement(
183 CharSourceRange::getCharRange(BraceRange.getEnd(),
184 BraceRange.getEnd().getLocWithOffset(1)),
185 ")");
186 }
187
188 insertHeader(Diag, SM.getFileID(ConstructCallStart));
189 }
190
191 void MakeSmartPtrCheck::checkReset(SourceManager &SM,
192 const CXXMemberCallExpr *Reset,
193 const CXXNewExpr *New) {
194 const auto *Expr = cast<MemberExpr>(Reset->getCallee());
195 SourceLocation OperatorLoc = Expr->getOperatorLoc();
196 SourceLocation ResetCallStart = Reset->getExprLoc();
197 SourceLocation ExprStart = Expr->getLocStart();
198 SourceLocation ExprEnd =
199 Lexer::getLocForEndOfToken(Expr->getLocEnd(), 0, SM, getLangOpts());
200
201 bool InMacro = ExprStart.isMacroID();
202
203 if (InMacro && IgnoreMacros) {
204 return;
205 }
206
207 // There are some cases where we don't have operator ("." or "->") of the
208 // "reset" expression, e.g. call "reset()" method directly in the subclass of
209 // "std::unique_ptr<>". We skip these cases.
210 if (OperatorLoc.isInvalid()) {
211 return;
212 }
213
214 auto Diag = diag(ResetCallStart, "use %0 instead")
215 << MakeSmartPtrFunctionName;
216
217 // Disable the fix in macros.
218 if (InMacro) {
219 return;
220 }
221
222 if (!replaceNew(Diag, New, SM)) {
223 return;
224 }
225
226 Diag << FixItHint::CreateReplacement(
227 CharSourceRange::getCharRange(OperatorLoc, ExprEnd),
228 (llvm::Twine(" = ") + MakeSmartPtrFunctionName + "<" +
229 GetNewExprName(New, SM, getLangOpts()) + ">")
230 .str());
231
232 if (Expr->isArrow())
233 Diag << FixItHint::CreateInsertion(ExprStart, "*");
234
235 insertHeader(Diag, SM.getFileID(OperatorLoc));
236 }
237
238 bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag,
239 const CXXNewExpr *New,
240 SourceManager& SM) {
241 SourceLocation NewStart = New->getSourceRange().getBegin();
242 SourceLocation NewEnd = New->getSourceRange().getEnd();
243
244 std::string ArraySizeExpr;
245 if (const auto* ArraySize = New->getArraySize()) {
246 ArraySizeExpr = Lexer::getSourceText(CharSourceRange::getTokenRange(
247 ArraySize->getSourceRange()),
248 SM, getLangOpts())
249 .str();
250 }
251
252 switch (New->getInitializationStyle()) {
253 case CXXNewExpr::NoInit: {
254 if (ArraySizeExpr.empty()) {
255 Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
256 } else {
257 // New array expression without written initializer:
258 // smart_ptr<Foo[]>(new Foo[5]);
259 Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
260 ArraySizeExpr);
261 }
262 break;
263 }
264 case CXXNewExpr::CallInit: {
265 // FIXME: Add fixes for constructors with parameters that can be created
266 // with a C++11 braced-init-list (e.g. std::vector, std::map).
267 // Unlike ordinal cases, braced list can not be deduced in
268 // std::make_smart_ptr, we need to specify the type explicitly in the fixes:
269 // struct S { S(std::initializer_list<int>, int); };
270 // struct S2 { S2(std::vector<int>); };
271 // smart_ptr<S>(new S({1, 2, 3}, 1)); // C++98 call-style initialization
272 // smart_ptr<S>(new S({}, 1));
273 // smart_ptr<S2>(new S2({1})); // implicit conversion:
274 // // std::initializer_list => std::vector
275 // The above samples have to be replaced with:
276 // std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}), 1);
277 // std::make_smart_ptr<S>(std::initializer_list<int>({}), 1);
278 // std::make_smart_ptr<S2>(std::vector<int>({1}));
279 if (const auto *CE = New->getConstructExpr()) {
280 for (const auto *Arg : CE->arguments()) {
281 if (isa<CXXStdInitializerListExpr>(Arg)) {
282 return false;
283 }
284 // Check the implicit conversion from the std::initializer_list type to
285 // a class type.
286 if (const auto *ImplicitCE = dyn_cast<CXXConstructExpr>(Arg)) {
287 if (ImplicitCE->isStdInitListInitialization()) {
288 return false;
289 }
290 }
291 }
292 }
293 if (ArraySizeExpr.empty()) {
294 SourceRange InitRange = New->getDirectInitRange();
295 Diag << FixItHint::CreateRemoval(
296 SourceRange(NewStart, InitRange.getBegin()));
297 Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
298 }
299 else {
300 // New array expression with default/value initialization:
301 // smart_ptr<Foo[]>(new int[5]());
302 // smart_ptr<Foo[]>(new Foo[5]());
303 Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
304 ArraySizeExpr);
305 }
306 break;
307 }
308 case CXXNewExpr::ListInit: {
309 // Range of the substring that we do not want to remove.
310 SourceRange InitRange;
311 if (const auto *NewConstruct = New->getConstructExpr()) {
312 if (NewConstruct->isStdInitListInitialization()) {
313 // FIXME: Add fixes for direct initialization with the initializer-list
314 // constructor. Similar to the above CallInit case, the type has to be
315 // specified explicitly in the fixes.
316 // struct S { S(std::initializer_list<int>); };
317 // smart_ptr<S>(new S{1, 2, 3}); // C++11 direct list-initialization
318 // smart_ptr<S>(new S{}); // use initializer-list consturctor
319 // The above cases have to be replaced with:
320 // std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}));
321 // std::make_smart_ptr<S>(std::initializer_list<int>({}));
322 return false;
323 } else {
324 // Direct initialization with ordinary constructors.
325 // struct S { S(int x); S(); };
326 // smart_ptr<S>(new S{5});
327 // smart_ptr<S>(new S{}); // use default constructor
328 // The arguments in the initialization list are going to be forwarded to
329 // the constructor, so this has to be replaced with:
330 // std::make_smart_ptr<S>(5);
331 // std::make_smart_ptr<S>();
332 InitRange = SourceRange(
333 NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
334 NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
335 }
336 } else {
337 // Aggregate initialization.
338 // smart_ptr<Pair>(new Pair{first, second});
339 // Has to be replaced with:
340 // smart_ptr<Pair>(Pair{first, second});
341 InitRange = SourceRange(
342 New->getAllocatedTypeSourceInfo()->getTypeLoc().getLocStart(),
343 New->getInitializer()->getSourceRange().getEnd());
344 }
345 Diag << FixItHint::CreateRemoval(
346 CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
347 Diag << FixItHint::CreateRemoval(
348 SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));
349 break;
350 }
351 }
352 return true;
353 }
354
355 void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) {
356 if (MakeSmartPtrFunctionHeader.empty()) {
357 return;
358 }
359 if (auto IncludeFixit = Inserter->CreateIncludeInsertion(
360 FD, MakeSmartPtrFunctionHeader,
361 /*IsAngled=*/MakeSmartPtrFunctionHeader == StdMemoryHeader)) {
362 Diag << *IncludeFixit;
363 }
364 }
365
366 } // namespace modernize
367 } // namespace tidy
368 } // namespace clang
clang::ast_matchers
Definition: ClangTidyDiagnosticConsumer.h:27
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:449
Name
StringHandle Name
Definition: PreprocessorTracker.cpp:525
clang::tidy::ClangTidyCheck::getLangOpts
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:187
clang::tidy::modernize::MakeSmartPtrCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) final
Override this to register AST matchers with Finder.
Definition: MakeSmartPtrCheck.cpp:72
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:64
clang::tidy::modernize::MakeSmartPtrCheck::getSmartPointerTypeMatcher
virtual SmartPtrTypeMatcher getSmartPointerTypeMatcher() const =0
Returns matcher that match with different smart pointer types.
clang::tidy::modernize::MakeSmartPtrCheck::ConstructorCall
static const char ConstructorCall[]
Definition: MakeSmartPtrCheck.h:44
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidy.h:183
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:104
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, std::string > OptionMap
Definition: ClangTidyOptions.h:99
clang::tidy::modernize::MakeSmartPtrCheck::PointerType
static const char PointerType[]
Definition: MakeSmartPtrCheck.h:43
MakeSharedCheck.h
clang
Definition: AndroidTidyModule.cpp:28
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::utils::IncludeInserter
Produces fixes to insert specified includes to source files, if not yet present.
Definition: IncludeInserter.h:55
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:104
clang::tidy::modernize::MakeSmartPtrCheck::NewExpression
static const char NewExpression[]
Definition: MakeSmartPtrCheck.h:46
clang::tidy::modernize::MakeSmartPtrCheck::ResetCall
static const char ResetCall[]
Definition: MakeSmartPtrCheck.h:45
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:416