1 | //===--- ReplaceAutoPtrCheck.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 "ReplaceAutoPtrCheck.h" |
10 | #include "clang/AST/ASTContext.h" |
11 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
12 | #include "clang/Frontend/CompilerInstance.h" |
13 | #include "clang/Lex/Lexer.h" |
14 | #include "clang/Lex/Preprocessor.h" |
15 | |
16 | using namespace clang; |
17 | using namespace clang::ast_matchers; |
18 | |
19 | namespace clang::tidy::modernize { |
20 | |
21 | namespace { |
22 | static const char AutoPtrTokenId[] = "AutoPrTokenId" ; |
23 | static const char AutoPtrOwnershipTransferId[] = "AutoPtrOwnershipTransferId" ; |
24 | |
25 | /// Matches expressions that are lvalues. |
26 | /// |
27 | /// In the following example, a[0] matches expr(isLValue()): |
28 | /// \code |
29 | /// std::string a[2]; |
30 | /// std::string b; |
31 | /// b = a[0]; |
32 | /// b = "this string won't match"; |
33 | /// \endcode |
34 | AST_MATCHER(Expr, isLValue) { return Node.getValueKind() == VK_LValue; } |
35 | |
36 | } // namespace |
37 | |
38 | ReplaceAutoPtrCheck::ReplaceAutoPtrCheck(StringRef Name, |
39 | ClangTidyContext *Context) |
40 | : ClangTidyCheck(Name, Context), |
41 | Inserter(Options.getLocalOrGlobal(LocalName: "IncludeStyle" , |
42 | Default: utils::IncludeSorter::IS_LLVM), |
43 | areDiagsSelfContained()) {} |
44 | |
45 | void ReplaceAutoPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { |
46 | Options.store(Options&: Opts, LocalName: "IncludeStyle" , Value: Inserter.getStyle()); |
47 | } |
48 | |
49 | void ReplaceAutoPtrCheck::registerMatchers(MatchFinder *Finder) { |
50 | auto AutoPtrDecl = recordDecl(hasName(Name: "auto_ptr" ), isInStdNamespace()); |
51 | auto AutoPtrType = qualType(hasDeclaration(InnerMatcher: AutoPtrDecl)); |
52 | |
53 | // std::auto_ptr<int> a; |
54 | // ^~~~~~~~~~~~~ |
55 | // |
56 | // typedef std::auto_ptr<int> int_ptr_t; |
57 | // ^~~~~~~~~~~~~ |
58 | // |
59 | // std::auto_ptr<int> fn(std::auto_ptr<int>); |
60 | // ^~~~~~~~~~~~~ ^~~~~~~~~~~~~ |
61 | Finder->addMatcher(NodeMatch: typeLoc(loc(InnerMatcher: qualType(AutoPtrType, |
62 | // Skip elaboratedType() as the named |
63 | // type will match soon thereafter. |
64 | unless(elaboratedType())))) |
65 | .bind(ID: AutoPtrTokenId), |
66 | Action: this); |
67 | |
68 | // using std::auto_ptr; |
69 | // ^~~~~~~~~~~~~~~~~~~ |
70 | Finder->addMatcher(NodeMatch: usingDecl(hasAnyUsingShadowDecl(InnerMatcher: hasTargetDecl(InnerMatcher: namedDecl( |
71 | hasName(Name: "auto_ptr" ), isInStdNamespace())))) |
72 | .bind(ID: AutoPtrTokenId), |
73 | Action: this); |
74 | |
75 | // Find ownership transfers via copy construction and assignment. |
76 | // AutoPtrOwnershipTransferId is bound to the part that has to be wrapped |
77 | // into std::move(). |
78 | // std::auto_ptr<int> i, j; |
79 | // i = j; |
80 | // ~~~~^ |
81 | auto MovableArgumentMatcher = |
82 | expr(isLValue(), hasType(InnerMatcher: AutoPtrType)).bind(ID: AutoPtrOwnershipTransferId); |
83 | |
84 | Finder->addMatcher( |
85 | NodeMatch: cxxOperatorCallExpr(hasOverloadedOperatorName(Name: "=" ), |
86 | callee(InnerMatcher: cxxMethodDecl(ofClass(InnerMatcher: AutoPtrDecl))), |
87 | hasArgument(N: 1, InnerMatcher: MovableArgumentMatcher)), |
88 | Action: this); |
89 | Finder->addMatcher( |
90 | NodeMatch: traverse(TK: TK_AsIs, |
91 | InnerMatcher: cxxConstructExpr(hasType(InnerMatcher: AutoPtrType), argumentCountIs(N: 1), |
92 | hasArgument(N: 0, InnerMatcher: MovableArgumentMatcher))), |
93 | Action: this); |
94 | } |
95 | |
96 | void ReplaceAutoPtrCheck::registerPPCallbacks(const SourceManager &SM, |
97 | Preprocessor *PP, |
98 | Preprocessor *ModuleExpanderPP) { |
99 | Inserter.registerPreprocessor(PP); |
100 | } |
101 | |
102 | void ReplaceAutoPtrCheck::check(const MatchFinder::MatchResult &Result) { |
103 | SourceManager &SM = *Result.SourceManager; |
104 | if (const auto *E = |
105 | Result.Nodes.getNodeAs<Expr>(ID: AutoPtrOwnershipTransferId)) { |
106 | CharSourceRange Range = Lexer::makeFileCharRange( |
107 | Range: CharSourceRange::getTokenRange(E->getSourceRange()), SM, LangOpts: LangOptions()); |
108 | |
109 | if (Range.isInvalid()) |
110 | return; |
111 | |
112 | auto Diag = diag(Loc: Range.getBegin(), Description: "use std::move to transfer ownership" ) |
113 | << FixItHint::CreateInsertion(InsertionLoc: Range.getBegin(), Code: "std::move(" ) |
114 | << FixItHint::CreateInsertion(InsertionLoc: Range.getEnd(), Code: ")" ) |
115 | << Inserter.createMainFileIncludeInsertion(Header: "<utility>" ); |
116 | |
117 | return; |
118 | } |
119 | |
120 | SourceLocation AutoPtrLoc; |
121 | if (const auto *TL = Result.Nodes.getNodeAs<TypeLoc>(ID: AutoPtrTokenId)) { |
122 | // std::auto_ptr<int> i; |
123 | // ^ |
124 | if (auto Loc = TL->getAs<TemplateSpecializationTypeLoc>()) |
125 | AutoPtrLoc = Loc.getTemplateNameLoc(); |
126 | } else if (const auto *D = |
127 | Result.Nodes.getNodeAs<UsingDecl>(ID: AutoPtrTokenId)) { |
128 | // using std::auto_ptr; |
129 | // ^ |
130 | AutoPtrLoc = D->getNameInfo().getBeginLoc(); |
131 | } else { |
132 | llvm_unreachable("Bad Callback. No node provided." ); |
133 | } |
134 | |
135 | if (AutoPtrLoc.isMacroID()) |
136 | AutoPtrLoc = SM.getSpellingLoc(Loc: AutoPtrLoc); |
137 | |
138 | // Ensure that only the 'auto_ptr' token is replaced and not the template |
139 | // aliases. |
140 | if (StringRef(SM.getCharacterData(SL: AutoPtrLoc), strlen(s: "auto_ptr" )) != |
141 | "auto_ptr" ) |
142 | return; |
143 | |
144 | SourceLocation EndLoc = |
145 | AutoPtrLoc.getLocWithOffset(Offset: strlen(s: "auto_ptr" ) - 1); |
146 | diag(Loc: AutoPtrLoc, Description: "auto_ptr is deprecated, use unique_ptr instead" ) |
147 | << FixItHint::CreateReplacement(RemoveRange: SourceRange(AutoPtrLoc, EndLoc), |
148 | Code: "unique_ptr" ); |
149 | } |
150 | |
151 | } // namespace clang::tidy::modernize |
152 | |