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