[clang][modules] Don't prevent translation of FW_Private includes when explicitly...
[llvm-project.git] / clang-tools-extra / clang-tidy / modernize / MakeSmartPtrCheck.cpp
blob2f9f47d3f6c3e85a985c4466cacc2b8ac51ba7c7
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 //===----------------------------------------------------------------------===//
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"
15 using namespace clang::ast_matchers;
17 namespace clang::tidy::modernize {
19 namespace {
21 constexpr char ConstructorCall[] = "constructorCall";
22 constexpr char ResetCall[] = "resetCall";
23 constexpr char NewExpression[] = "newExpression";
25 std::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();
34 return WrittenName.str();
37 } // namespace
39 const char MakeSmartPtrCheck::PointerType[] = "pointerType";
41 MakeSmartPtrCheck::MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context,
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)) {}
55 void MakeSmartPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
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);
64 bool MakeSmartPtrCheck::isLanguageVersionSupported(
65 const LangOptions &LangOpts) const {
66 return LangOpts.CPlusPlus11;
69 void MakeSmartPtrCheck::registerPPCallbacks(const SourceManager &SM,
70 Preprocessor *PP,
71 Preprocessor *ModuleExpanderPP) {
72 Inserter.registerPreprocessor(PP);
75 void 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())))))));
81 auto IsPlacement = hasAnyPlacementArg(anything());
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);
98 Finder->addMatcher(
99 traverse(TK_AsIs,
100 cxxMemberCallExpr(
101 thisPointerType(getSmartPointerTypeMatcher()),
102 callee(cxxMethodDecl(hasName("reset"))),
103 hasArgument(0, cxxNewExpr(CanCallCtor, unless(IsPlacement))
104 .bind(NewExpression)),
105 unless(isInTemplateInstantiation()))
106 .bind(ResetCall)),
107 this);
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.
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);
122 // Skip when this is a new-expression with `auto`, e.g. new auto(1)
123 if (New->getType()->getPointeeType()->getContainedAutoType())
124 return;
126 // Be conservative for cases where we construct and default initialize.
128 // For example,
129 // P.reset(new int) // check fix: P = std::make_unique<int>()
130 // P.reset(new int[5]) // check fix: P = std::make_unique<int []>(5)
132 // The fix of the check has side effect, it introduces value initialization
133 // which maybe unexpected and cause performance regression.
134 bool Initializes = New->hasInitializer() ||
135 !utils::type_traits::isTriviallyDefaultConstructible(
136 New->getAllocatedType(), *Result.Context);
137 if (!Initializes && IgnoreDefaultInitialization)
138 return;
139 if (Construct)
140 checkConstruct(SM, Result.Context, Construct, Type, New);
141 else if (Reset)
142 checkReset(SM, Result.Context, Reset, New);
145 void MakeSmartPtrCheck::checkConstruct(SourceManager &SM, ASTContext *Ctx,
146 const CXXConstructExpr *Construct,
147 const QualType *Type,
148 const CXXNewExpr *New) {
149 SourceLocation ConstructCallStart = Construct->getExprLoc();
150 bool InMacro = ConstructCallStart.isMacroID();
152 if (InMacro && IgnoreMacros) {
153 return;
156 bool Invalid = false;
157 StringRef ExprStr = Lexer::getSourceText(
158 CharSourceRange::getCharRange(
159 ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
160 SM, getLangOpts(), &Invalid);
161 if (Invalid)
162 return;
164 auto Diag = diag(ConstructCallStart, "use %0 instead")
165 << MakeSmartPtrFunctionName;
167 // Disable the fix in macros.
168 if (InMacro) {
169 return;
172 if (!replaceNew(Diag, New, SM, Ctx)) {
173 return;
176 // Find the location of the template's left angle.
177 size_t LAngle = ExprStr.find('<');
178 SourceLocation ConstructCallEnd;
179 if (LAngle == StringRef::npos) {
180 // If the template argument is missing (because it is part of the alias)
181 // we have to add it back.
182 ConstructCallEnd = ConstructCallStart.getLocWithOffset(ExprStr.size());
183 Diag << FixItHint::CreateInsertion(
184 ConstructCallEnd, "<" + getNewExprName(New, SM, getLangOpts()) + ">");
185 } else {
186 ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
189 Diag << FixItHint::CreateReplacement(
190 CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
191 MakeSmartPtrFunctionName);
193 // If the smart_ptr is built with brace enclosed direct initialization, use
194 // parenthesis instead.
195 if (Construct->isListInitialization()) {
196 SourceRange BraceRange = Construct->getParenOrBraceRange();
197 Diag << FixItHint::CreateReplacement(
198 CharSourceRange::getCharRange(
199 BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
200 "(");
201 Diag << FixItHint::CreateReplacement(
202 CharSourceRange::getCharRange(BraceRange.getEnd(),
203 BraceRange.getEnd().getLocWithOffset(1)),
204 ")");
207 insertHeader(Diag, SM.getFileID(ConstructCallStart));
210 void MakeSmartPtrCheck::checkReset(SourceManager &SM, ASTContext *Ctx,
211 const CXXMemberCallExpr *Reset,
212 const CXXNewExpr *New) {
213 const auto *Expr = cast<MemberExpr>(Reset->getCallee());
214 SourceLocation OperatorLoc = Expr->getOperatorLoc();
215 SourceLocation ResetCallStart = Reset->getExprLoc();
216 SourceLocation ExprStart = Expr->getBeginLoc();
217 SourceLocation ExprEnd =
218 Lexer::getLocForEndOfToken(Expr->getEndLoc(), 0, SM, getLangOpts());
220 bool InMacro = ExprStart.isMacroID();
222 if (InMacro && IgnoreMacros) {
223 return;
226 // There are some cases where we don't have operator ("." or "->") of the
227 // "reset" expression, e.g. call "reset()" method directly in the subclass of
228 // "std::unique_ptr<>". We skip these cases.
229 if (OperatorLoc.isInvalid()) {
230 return;
233 auto Diag = diag(ResetCallStart, "use %0 instead")
234 << MakeSmartPtrFunctionName;
236 // Disable the fix in macros.
237 if (InMacro) {
238 return;
241 if (!replaceNew(Diag, New, SM, Ctx)) {
242 return;
245 Diag << FixItHint::CreateReplacement(
246 CharSourceRange::getCharRange(OperatorLoc, ExprEnd),
247 (llvm::Twine(" = ") + MakeSmartPtrFunctionName + "<" +
248 getNewExprName(New, SM, getLangOpts()) + ">")
249 .str());
251 if (Expr->isArrow())
252 Diag << FixItHint::CreateInsertion(ExprStart, "*");
254 insertHeader(Diag, SM.getFileID(OperatorLoc));
257 bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag,
258 const CXXNewExpr *New, SourceManager &SM,
259 ASTContext *Ctx) {
260 auto SkipParensParents = [&](const Expr *E) {
261 TraversalKindScope RAII(*Ctx, TK_AsIs);
263 for (const Expr *OldE = nullptr; E != OldE;) {
264 OldE = E;
265 for (const auto &Node : Ctx->getParents(*E)) {
266 if (const Expr *Parent = Node.get<ParenExpr>()) {
267 E = Parent;
268 break;
272 return E;
275 SourceRange NewRange = SkipParensParents(New)->getSourceRange();
276 SourceLocation NewStart = NewRange.getBegin();
277 SourceLocation NewEnd = NewRange.getEnd();
279 // Skip when the source location of the new expression is invalid.
280 if (NewStart.isInvalid() || NewEnd.isInvalid())
281 return false;
283 std::string ArraySizeExpr;
284 if (const auto *ArraySize = New->getArraySize().value_or(nullptr)) {
285 ArraySizeExpr = Lexer::getSourceText(CharSourceRange::getTokenRange(
286 ArraySize->getSourceRange()),
287 SM, getLangOpts())
288 .str();
290 // Returns true if the given constructor expression has any braced-init-list
291 // argument, e.g.
292 // Foo({1, 2}, 1) => true
293 // Foo(Bar{1, 2}) => true
294 // Foo(1) => false
295 // Foo{1} => false
296 auto HasListIntializedArgument = [](const CXXConstructExpr *CE) {
297 for (const auto *Arg : CE->arguments()) {
298 Arg = Arg->IgnoreImplicit();
300 if (isa<CXXStdInitializerListExpr>(Arg) || isa<InitListExpr>(Arg))
301 return true;
302 // Check whether we implicitly construct a class from a
303 // std::initializer_list.
304 if (const auto *CEArg = dyn_cast<CXXConstructExpr>(Arg)) {
305 // Strip the elidable move constructor, it is present in the AST for
306 // C++11/14, e.g. Foo(Bar{1, 2}), the move constructor is around the
307 // init-list constructor.
308 if (CEArg->isElidable()) {
309 if (const auto *TempExp = CEArg->getArg(0)) {
310 if (const auto *UnwrappedCE =
311 dyn_cast<CXXConstructExpr>(TempExp->IgnoreImplicit()))
312 CEArg = UnwrappedCE;
315 if (CEArg->isStdInitListInitialization())
316 return true;
319 return false;
321 switch (New->getInitializationStyle()) {
322 case CXXNewExpr::NoInit: {
323 if (ArraySizeExpr.empty()) {
324 Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
325 } else {
326 // New array expression without written initializer:
327 // smart_ptr<Foo[]>(new Foo[5]);
328 Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
329 ArraySizeExpr);
331 break;
333 case CXXNewExpr::CallInit: {
334 // FIXME: Add fixes for constructors with parameters that can be created
335 // with a C++11 braced-init-list (e.g. std::vector, std::map).
336 // Unlike ordinal cases, braced list can not be deduced in
337 // std::make_smart_ptr, we need to specify the type explicitly in the fixes:
338 // struct S { S(std::initializer_list<int>, int); };
339 // struct S2 { S2(std::vector<int>); };
340 // struct S3 { S3(S2, int); };
341 // smart_ptr<S>(new S({1, 2, 3}, 1)); // C++98 call-style initialization
342 // smart_ptr<S>(new S({}, 1));
343 // smart_ptr<S2>(new S2({1})); // implicit conversion:
344 // // std::initializer_list => std::vector
345 // smart_ptr<S3>(new S3({1, 2}, 3));
346 // The above samples have to be replaced with:
347 // std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}), 1);
348 // std::make_smart_ptr<S>(std::initializer_list<int>({}), 1);
349 // std::make_smart_ptr<S2>(std::vector<int>({1}));
350 // std::make_smart_ptr<S3>(S2{1, 2}, 3);
351 if (const auto *CE = New->getConstructExpr()) {
352 if (HasListIntializedArgument(CE))
353 return false;
355 if (ArraySizeExpr.empty()) {
356 SourceRange InitRange = New->getDirectInitRange();
357 Diag << FixItHint::CreateRemoval(
358 SourceRange(NewStart, InitRange.getBegin()));
359 Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
361 else {
362 // New array expression with default/value initialization:
363 // smart_ptr<Foo[]>(new int[5]());
364 // smart_ptr<Foo[]>(new Foo[5]());
365 Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
366 ArraySizeExpr);
368 break;
370 case CXXNewExpr::ListInit: {
371 // Range of the substring that we do not want to remove.
372 SourceRange InitRange;
373 if (const auto *NewConstruct = New->getConstructExpr()) {
374 if (NewConstruct->isStdInitListInitialization() ||
375 HasListIntializedArgument(NewConstruct)) {
376 // FIXME: Add fixes for direct initialization with the initializer-list
377 // constructor. Similar to the above CallInit case, the type has to be
378 // specified explicitly in the fixes.
379 // struct S { S(std::initializer_list<int>); };
380 // struct S2 { S2(S, int); };
381 // smart_ptr<S>(new S{1, 2, 3}); // C++11 direct list-initialization
382 // smart_ptr<S>(new S{}); // use initializer-list constructor
383 // smart_ptr<S2>()new S2{ {1,2}, 3 }; // have a list-initialized arg
384 // The above cases have to be replaced with:
385 // std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}));
386 // std::make_smart_ptr<S>(std::initializer_list<int>({}));
387 // std::make_smart_ptr<S2>(S{1, 2}, 3);
388 return false;
390 // Direct initialization with ordinary constructors.
391 // struct S { S(int x); S(); };
392 // smart_ptr<S>(new S{5});
393 // smart_ptr<S>(new S{}); // use default constructor
394 // The arguments in the initialization list are going to be forwarded to
395 // the constructor, so this has to be replaced with:
396 // std::make_smart_ptr<S>(5);
397 // std::make_smart_ptr<S>();
398 InitRange = SourceRange(
399 NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
400 NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
401 } else {
402 // Aggregate initialization.
403 // smart_ptr<Pair>(new Pair{first, second});
404 // Has to be replaced with:
405 // smart_ptr<Pair>(Pair{first, second});
407 // The fix (std::make_unique) needs to see copy/move constructor of
408 // Pair. If we found any invisible or deleted copy/move constructor, we
409 // stop generating fixes -- as the C++ rule is complicated and we are less
410 // certain about the correct fixes.
411 if (const CXXRecordDecl *RD = New->getType()->getPointeeCXXRecordDecl()) {
412 if (llvm::any_of(RD->ctors(), [](const CXXConstructorDecl *Ctor) {
413 return Ctor->isCopyOrMoveConstructor() &&
414 (Ctor->isDeleted() || Ctor->getAccess() == AS_private);
415 })) {
416 return false;
419 InitRange = SourceRange(
420 New->getAllocatedTypeSourceInfo()->getTypeLoc().getBeginLoc(),
421 New->getInitializer()->getSourceRange().getEnd());
423 Diag << FixItHint::CreateRemoval(
424 CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
425 Diag << FixItHint::CreateRemoval(
426 SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));
427 break;
430 return true;
433 void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) {
434 if (MakeSmartPtrFunctionHeader.empty()) {
435 return;
437 Diag << Inserter.createIncludeInsertion(FD, MakeSmartPtrFunctionHeader);
440 } // namespace clang::tidy::modernize