1 //===--- ReplaceAutoPtrCheck.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 "ReplaceAutoPtrCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Frontend/CompilerInstance.h"
14 #include "clang/Lex/Lexer.h"
15 #include "clang/Lex/Preprocessor.h"
16
17 using namespace clang;
18 using namespace clang::ast_matchers;
19
20 namespace clang {
21 namespace tidy {
22 namespace modernize {
23
24 namespace {
25 static const char AutoPtrTokenId[] = "AutoPrTokenId";
26 static const char AutoPtrOwnershipTransferId[] = "AutoPtrOwnershipTransferId";
27
28 /// \brief Matches expressions that are lvalues.
29 ///
30 /// In the following example, a[0] matches expr(isLValue()):
31 /// \code
32 /// std::string a[2];
33 /// std::string b;
34 /// b = a[0];
35 /// b = "this string won't match";
36 /// \endcode
AST_MATCHER(Expr,isLValue)37 AST_MATCHER(Expr, isLValue) { return Node.getValueKind() == VK_LValue; }
38
39 /// Matches declarations whose declaration context is the C++ standard library
40 /// namespace std.
41 ///
42 /// Note that inline namespaces are silently ignored during the lookup since
43 /// both libstdc++ and libc++ are known to use them for versioning purposes.
44 ///
45 /// Given:
46 /// \code
47 /// namespace ns {
48 /// struct my_type {};
49 /// using namespace std;
50 /// }
51 ///
52 /// using std::vector;
53 /// using ns:my_type;
54 /// using ns::list;
55 /// \code
56 ///
57 /// usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(isFromStdNamespace())))
58 /// matches "using std::vector" and "using ns::list".
AST_MATCHER(Decl,isFromStdNamespace)59 AST_MATCHER(Decl, isFromStdNamespace) {
60 const DeclContext *D = Node.getDeclContext();
61
62 while (D->isInlineNamespace())
63 D = D->getParent();
64
65 if (!D->isNamespace() || !D->getParent()->isTranslationUnit())
66 return false;
67
68 const IdentifierInfo *Info = cast<NamespaceDecl>(D)->getIdentifier();
69
70 return (Info && Info->isStr("std"));
71 }
72
73 } // namespace
74
ReplaceAutoPtrCheck(StringRef Name,ClangTidyContext * Context)75 ReplaceAutoPtrCheck::ReplaceAutoPtrCheck(StringRef Name,
76 ClangTidyContext *Context)
77 : ClangTidyCheck(Name, Context),
78 IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
79 Options.getLocalOrGlobal("IncludeStyle", "llvm"))) {}
80
storeOptions(ClangTidyOptions::OptionMap & Opts)81 void ReplaceAutoPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
82 Options.store(Opts, "IncludeStyle",
83 utils::IncludeSorter::toString(IncludeStyle));
84 }
85
registerMatchers(MatchFinder * Finder)86 void ReplaceAutoPtrCheck::registerMatchers(MatchFinder *Finder) {
87 // Only register the matchers for C++; the functionality currently does not
88 // provide any benefit to other languages, despite being benign.
89 if (!getLangOpts().CPlusPlus)
90 return;
91
92 auto AutoPtrDecl = recordDecl(hasName("auto_ptr"), isFromStdNamespace());
93 auto AutoPtrType = qualType(hasDeclaration(AutoPtrDecl));
94
95 // std::auto_ptr<int> a;
96 // ^~~~~~~~~~~~~
97 //
98 // typedef std::auto_ptr<int> int_ptr_t;
99 // ^~~~~~~~~~~~~
100 //
101 // std::auto_ptr<int> fn(std::auto_ptr<int>);
102 // ^~~~~~~~~~~~~ ^~~~~~~~~~~~~
103 Finder->addMatcher(typeLoc(loc(qualType(AutoPtrType,
104 // Skip elaboratedType() as the named
105 // type will match soon thereafter.
106 unless(elaboratedType()))))
107 .bind(AutoPtrTokenId),
108 this);
109
110 // using std::auto_ptr;
111 // ^~~~~~~~~~~~~~~~~~~
112 Finder->addMatcher(usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(namedDecl(
113 hasName("auto_ptr"), isFromStdNamespace()))))
114 .bind(AutoPtrTokenId),
115 this);
116
117 // Find ownership transfers via copy construction and assignment.
118 // AutoPtrOwnershipTransferId is bound to the the part that has to be wrapped
119 // into std::move().
120 // std::auto_ptr<int> i, j;
121 // i = j;
122 // ~~~~^
123 auto MovableArgumentMatcher =
124 expr(isLValue(), hasType(AutoPtrType)).bind(AutoPtrOwnershipTransferId);
125
126 Finder->addMatcher(
127 cxxOperatorCallExpr(hasOverloadedOperatorName("="),
128 callee(cxxMethodDecl(ofClass(AutoPtrDecl))),
129 hasArgument(1, MovableArgumentMatcher)),
130 this);
131 Finder->addMatcher(cxxConstructExpr(hasType(AutoPtrType), argumentCountIs(1),
132 hasArgument(0, MovableArgumentMatcher)),
133 this);
134 }
135
registerPPCallbacks(CompilerInstance & Compiler)136 void ReplaceAutoPtrCheck::registerPPCallbacks(CompilerInstance &Compiler) {
137 // Only register the preprocessor callbacks for C++; the functionality
138 // currently does not provide any benefit to other languages, despite being
139 // benign.
140 if (!getLangOpts().CPlusPlus)
141 return;
142 Inserter.reset(new utils::IncludeInserter(
143 Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
144 Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
145 }
146
check(const MatchFinder::MatchResult & Result)147 void ReplaceAutoPtrCheck::check(const MatchFinder::MatchResult &Result) {
148 SourceManager &SM = *Result.SourceManager;
149 if (const auto *E =
150 Result.Nodes.getNodeAs<Expr>(AutoPtrOwnershipTransferId)) {
151 CharSourceRange Range = Lexer::makeFileCharRange(
152 CharSourceRange::getTokenRange(E->getSourceRange()), SM, LangOptions());
153
154 if (Range.isInvalid())
155 return;
156
157 auto Diag = diag(Range.getBegin(), "use std::move to transfer ownership")
158 << FixItHint::CreateInsertion(Range.getBegin(), "std::move(")
159 << FixItHint::CreateInsertion(Range.getEnd(), ")");
160
161 if (auto Fix =
162 Inserter->CreateIncludeInsertion(SM.getMainFileID(), "utility",
163 /*IsAngled=*/true))
164 Diag << *Fix;
165
166 return;
167 }
168
169 SourceLocation AutoPtrLoc;
170 if (const auto *TL = Result.Nodes.getNodeAs<TypeLoc>(AutoPtrTokenId)) {
171 // std::auto_ptr<int> i;
172 // ^
173 if (auto Loc = TL->getAs<TemplateSpecializationTypeLoc>())
174 AutoPtrLoc = Loc.getTemplateNameLoc();
175 } else if (const auto *D =
176 Result.Nodes.getNodeAs<UsingDecl>(AutoPtrTokenId)) {
177 // using std::auto_ptr;
178 // ^
179 AutoPtrLoc = D->getNameInfo().getBeginLoc();
180 } else {
181 llvm_unreachable("Bad Callback. No node provided.");
182 }
183
184 if (AutoPtrLoc.isMacroID())
185 AutoPtrLoc = SM.getSpellingLoc(AutoPtrLoc);
186
187 // Ensure that only the 'auto_ptr' token is replaced and not the template
188 // aliases.
189 if (StringRef(SM.getCharacterData(AutoPtrLoc), strlen("auto_ptr")) !=
190 "auto_ptr")
191 return;
192
193 SourceLocation EndLoc =
194 AutoPtrLoc.getLocWithOffset(strlen("auto_ptr") - 1);
195 diag(AutoPtrLoc, "auto_ptr is deprecated, use unique_ptr instead")
196 << FixItHint::CreateReplacement(SourceRange(AutoPtrLoc, EndLoc),
197 "unique_ptr");
198 }
199
200 } // namespace modernize
201 } // namespace tidy
202 } // namespace clang
203