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