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 // Skip when the source location of the new expression is invalid. 245 if (NewStart.isInvalid() || NewEnd.isInvalid()) 246 return false; 247 248 std::string ArraySizeExpr; 249 if (const auto* ArraySize = New->getArraySize()) { 250 ArraySizeExpr = Lexer::getSourceText(CharSourceRange::getTokenRange( 251 ArraySize->getSourceRange()), 252 SM, getLangOpts()) 253 .str(); 254 } 255 256 switch (New->getInitializationStyle()) { 257 case CXXNewExpr::NoInit: { 258 if (ArraySizeExpr.empty()) { 259 Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd)); 260 } else { 261 // New array expression without written initializer: 262 // smart_ptr<Foo[]>(new Foo[5]); 263 Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd), 264 ArraySizeExpr); 265 } 266 break; 267 } 268 case CXXNewExpr::CallInit: { 269 // FIXME: Add fixes for constructors with parameters that can be created 270 // with a C++11 braced-init-list (e.g. std::vector, std::map). 271 // Unlike ordinal cases, braced list can not be deduced in 272 // std::make_smart_ptr, we need to specify the type explicitly in the fixes: 273 // struct S { S(std::initializer_list<int>, int); }; 274 // struct S2 { S2(std::vector<int>); }; 275 // smart_ptr<S>(new S({1, 2, 3}, 1)); // C++98 call-style initialization 276 // smart_ptr<S>(new S({}, 1)); 277 // smart_ptr<S2>(new S2({1})); // implicit conversion: 278 // // std::initializer_list => std::vector 279 // The above samples have to be replaced with: 280 // std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}), 1); 281 // std::make_smart_ptr<S>(std::initializer_list<int>({}), 1); 282 // std::make_smart_ptr<S2>(std::vector<int>({1})); 283 if (const auto *CE = New->getConstructExpr()) { 284 for (const auto *Arg : CE->arguments()) { 285 if (isa<CXXStdInitializerListExpr>(Arg)) { 286 return false; 287 } 288 // Check whether we construct a class from a std::initializer_list. 289 // If so, we won't generate the fixes. 290 auto IsStdInitListInitConstructExpr = [](const Expr* E) { 291 assert(E); 292 if (const auto *ImplicitCE = dyn_cast<CXXConstructExpr>(E)) { 293 if (ImplicitCE->isStdInitListInitialization()) 294 return true; 295 } 296 return false; 297 }; 298 // Check the implicit conversion from the std::initializer_list type to 299 // a class type. 300 if (IsStdInitListInitConstructExpr(Arg)) 301 return false; 302 // The Arg can be a CXXBindTemporaryExpr, checking its underlying 303 // construct expr. 304 if (const auto * CTE = dyn_cast<CXXBindTemporaryExpr>(Arg)) { 305 if (IsStdInitListInitConstructExpr(CTE->getSubExpr())) 306 return false; 307 } 308 } 309 } 310 if (ArraySizeExpr.empty()) { 311 SourceRange InitRange = New->getDirectInitRange(); 312 Diag << FixItHint::CreateRemoval( 313 SourceRange(NewStart, InitRange.getBegin())); 314 Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd)); 315 } 316 else { 317 // New array expression with default/value initialization: 318 // smart_ptr<Foo[]>(new int[5]()); 319 // smart_ptr<Foo[]>(new Foo[5]()); 320 Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd), 321 ArraySizeExpr); 322 } 323 break; 324 } 325 case CXXNewExpr::ListInit: { 326 // Range of the substring that we do not want to remove. 327 SourceRange InitRange; 328 if (const auto *NewConstruct = New->getConstructExpr()) { 329 if (NewConstruct->isStdInitListInitialization()) { 330 // FIXME: Add fixes for direct initialization with the initializer-list 331 // constructor. Similar to the above CallInit case, the type has to be 332 // specified explicitly in the fixes. 333 // struct S { S(std::initializer_list<int>); }; 334 // smart_ptr<S>(new S{1, 2, 3}); // C++11 direct list-initialization 335 // smart_ptr<S>(new S{}); // use initializer-list consturctor 336 // The above cases have to be replaced with: 337 // std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3})); 338 // std::make_smart_ptr<S>(std::initializer_list<int>({})); 339 return false; 340 } else { 341 // Direct initialization with ordinary constructors. 342 // struct S { S(int x); S(); }; 343 // smart_ptr<S>(new S{5}); 344 // smart_ptr<S>(new S{}); // use default constructor 345 // The arguments in the initialization list are going to be forwarded to 346 // the constructor, so this has to be replaced with: 347 // std::make_smart_ptr<S>(5); 348 // std::make_smart_ptr<S>(); 349 InitRange = SourceRange( 350 NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1), 351 NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1)); 352 } 353 } else { 354 // Aggregate initialization. 355 // smart_ptr<Pair>(new Pair{first, second}); 356 // Has to be replaced with: 357 // smart_ptr<Pair>(Pair{first, second}); 358 InitRange = SourceRange( 359 New->getAllocatedTypeSourceInfo()->getTypeLoc().getLocStart(), 360 New->getInitializer()->getSourceRange().getEnd()); 361 } 362 Diag << FixItHint::CreateRemoval( 363 CharSourceRange::getCharRange(NewStart, InitRange.getBegin())); 364 Diag << FixItHint::CreateRemoval( 365 SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd)); 366 break; 367 } 368 } 369 return true; 370 } 371 372 void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) { 373 if (MakeSmartPtrFunctionHeader.empty()) { 374 return; 375 } 376 if (auto IncludeFixit = Inserter->CreateIncludeInsertion( 377 FD, MakeSmartPtrFunctionHeader, 378 /*IsAngled=*/MakeSmartPtrFunctionHeader == StdMemoryHeader)) { 379 Diag << *IncludeFixit; 380 } 381 } 382 383 } // namespace modernize 384 } // namespace tidy 385 } // namespace clang 386