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