clang-tools  4.0.0
ReplaceAutoPtrCheck.cpp
Go to the documentation of this file.
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 static const char AutoPtrTokenId[] = "AutoPrTokenId";
25 static const char AutoPtrOwnershipTransferId[] = "AutoPtrOwnershipTransferId";
26 
27 /// \brief Matches expressions that are lvalues.
28 ///
29 /// In the following example, a[0] matches expr(isLValue()):
30 /// \code
31 /// std::string a[2];
32 /// std::string b;
33 /// b = a[0];
34 /// b = "this string won't match";
35 /// \endcode
36 AST_MATCHER(Expr, isLValue) { return Node.getValueKind() == VK_LValue; }
37 
38 /// Matches declarations whose declaration context is the C++ standard library
39 /// namespace std.
40 ///
41 /// Note that inline namespaces are silently ignored during the lookup since
42 /// both libstdc++ and libc++ are known to use them for versioning purposes.
43 ///
44 /// Given:
45 /// \code
46 /// namespace ns {
47 /// struct my_type {};
48 /// using namespace std;
49 /// }
50 ///
51 /// using std::vector;
52 /// using ns:my_type;
53 /// using ns::list;
54 /// \code
55 ///
56 /// usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(isFromStdNamespace())))
57 /// matches "using std::vector" and "using ns::list".
58 AST_MATCHER(Decl, isFromStdNamespace) {
59  const DeclContext *D = Node.getDeclContext();
60 
61  while (D->isInlineNamespace())
62  D = D->getParent();
63 
64  if (!D->isNamespace() || !D->getParent()->isTranslationUnit())
65  return false;
66 
67  const IdentifierInfo *Info = cast<NamespaceDecl>(D)->getIdentifier();
68 
69  return (Info && Info->isStr("std"));
70 }
71 
72 /// \brief Matcher that finds auto_ptr declarations.
73 static DeclarationMatcher AutoPtrDecl =
74  recordDecl(hasName("auto_ptr"), isFromStdNamespace());
75 
76 /// \brief Matches types declared as auto_ptr.
77 static TypeMatcher AutoPtrType = qualType(hasDeclaration(AutoPtrDecl));
78 
79 /// \brief Matcher that finds expressions that are candidates to be wrapped with
80 /// 'std::move'.
81 ///
82 /// Binds the id \c AutoPtrOwnershipTransferId to the expression.
83 static StatementMatcher MovableArgumentMatcher =
84  expr(allOf(isLValue(), hasType(AutoPtrType)))
86 
87 /// \brief Creates a matcher that finds the locations of types referring to the
88 /// \c std::auto_ptr() type.
89 ///
90 /// \code
91 /// std::auto_ptr<int> a;
92 /// ^~~~~~~~~~~~~
93 ///
94 /// typedef std::auto_ptr<int> int_ptr_t;
95 /// ^~~~~~~~~~~~~
96 ///
97 /// std::auto_ptr<int> fn(std::auto_ptr<int>);
98 /// ^~~~~~~~~~~~~ ^~~~~~~~~~~~~
99 ///
100 /// <etc...>
101 /// \endcode
102 TypeLocMatcher makeAutoPtrTypeLocMatcher() {
103  // Skip elaboratedType() as the named type will match soon thereafter.
104  return typeLoc(loc(qualType(AutoPtrType, unless(elaboratedType()))))
105  .bind(AutoPtrTokenId);
106 }
107 
108 /// \brief Creates a matcher that finds the using declarations referring to
109 /// \c std::auto_ptr.
110 ///
111 /// \code
112 /// using std::auto_ptr;
113 /// ^~~~~~~~~~~~~~~~~~~
114 /// \endcode
115 DeclarationMatcher makeAutoPtrUsingDeclMatcher() {
116  return usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(
117  allOf(hasName("auto_ptr"), isFromStdNamespace()))))
118  .bind(AutoPtrTokenId);
119 }
120 
121 /// \brief Creates a matcher that finds the \c std::auto_ptr copy-ctor and
122 /// assign-operator expressions.
123 ///
124 /// \c AutoPtrOwnershipTransferId is assigned to the argument of the expression,
125 /// this is the part that has to be wrapped by \c std::move().
126 ///
127 /// \code
128 /// std::auto_ptr<int> i, j;
129 /// i = j;
130 /// ~~~~^
131 /// \endcode
133  return anyOf(
134  cxxOperatorCallExpr(allOf(hasOverloadedOperatorName("="),
135  callee(cxxMethodDecl(ofClass(AutoPtrDecl))),
136  hasArgument(1, MovableArgumentMatcher))),
137  cxxConstructExpr(allOf(hasType(AutoPtrType), argumentCountIs(1),
138  hasArgument(0, MovableArgumentMatcher))));
139 }
140 
141 /// \brief Locates the \c auto_ptr token when it is referred by a \c TypeLoc.
142 ///
143 /// \code
144 /// std::auto_ptr<int> i;
145 /// ^~~~~~~~~~~~~
146 /// \endcode
147 ///
148 /// The caret represents the location returned and the tildes cover the
149 /// parameter \p AutoPtrTypeLoc.
150 ///
151 /// \return An invalid \c SourceLocation if not found, otherwise the location
152 /// of the beginning of the \c auto_ptr token.
153 static SourceLocation locateFromTypeLoc(const TypeLoc *AutoPtrTypeLoc,
154  const SourceManager &SM) {
155  auto TL = AutoPtrTypeLoc->getAs<TemplateSpecializationTypeLoc>();
156  if (TL.isNull())
157  return SourceLocation();
158 
159  return TL.getTemplateNameLoc();
160 }
161 
162 /// \brief Locates the \c auto_ptr token in using declarations.
163 ///
164 /// \code
165 /// using std::auto_ptr;
166 /// ^
167 /// \endcode
168 ///
169 /// The caret represents the location returned.
170 ///
171 /// \return An invalid \c SourceLocation if not found, otherwise the location
172 /// of the beginning of the \c auto_ptr token.
173 static SourceLocation locateFromUsingDecl(const UsingDecl *UsingAutoPtrDecl,
174  const SourceManager &SM) {
175  return UsingAutoPtrDecl->getNameInfo().getBeginLoc();
176 }
177 
178 /// \brief Verifies that the token at \p TokenStart is 'auto_ptr'.
179 static bool checkTokenIsAutoPtr(SourceLocation TokenStart,
180  const SourceManager &SM,
181  const LangOptions &LO) {
182  SmallVector<char, 8> Buffer;
183  bool Invalid = false;
184  StringRef Res = Lexer::getSpelling(TokenStart, Buffer, SM, LO, &Invalid);
185 
186  return (!Invalid && Res == "auto_ptr");
187 }
188 
189 ReplaceAutoPtrCheck::ReplaceAutoPtrCheck(StringRef Name,
191  : ClangTidyCheck(Name, Context),
192  IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
193  Options.get("IncludeStyle", "llvm"))) {}
194 
196  Options.store(Opts, "IncludeStyle",
197  utils::IncludeSorter::toString(IncludeStyle));
198 }
199 
201  // Only register the matchers for C++; the functionality currently does not
202  // provide any benefit to other languages, despite being benign.
203  if (getLangOpts().CPlusPlus) {
204  Finder->addMatcher(makeAutoPtrTypeLocMatcher(), this);
205  Finder->addMatcher(makeAutoPtrUsingDeclMatcher(), this);
206  Finder->addMatcher(makeTransferOwnershipExprMatcher(), this);
207  }
208 }
209 
210 void ReplaceAutoPtrCheck::registerPPCallbacks(CompilerInstance &Compiler) {
211  // Only register the preprocessor callbacks for C++; the functionality
212  // currently does not provide any benefit to other languages, despite being
213  // benign.
214  if (getLangOpts().CPlusPlus) {
215  Inserter.reset(new utils::IncludeInserter(
216  Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
217  Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
218  }
219 }
220 
221 void ReplaceAutoPtrCheck::check(const MatchFinder::MatchResult &Result) {
222  SourceManager &SM = *Result.SourceManager;
223  if (const auto *E =
224  Result.Nodes.getNodeAs<Expr>(AutoPtrOwnershipTransferId)) {
225  CharSourceRange Range = Lexer::makeFileCharRange(
226  CharSourceRange::getTokenRange(E->getSourceRange()), SM, LangOptions());
227 
228  if (Range.isInvalid())
229  return;
230 
231  auto Diag = diag(Range.getBegin(), "use std::move to transfer ownership")
232  << FixItHint::CreateInsertion(Range.getBegin(), "std::move(")
233  << FixItHint::CreateInsertion(Range.getEnd(), ")");
234 
235  auto Insertion =
236  Inserter->CreateIncludeInsertion(SM.getMainFileID(), "utility",
237  /*IsAngled=*/true);
238  if (Insertion.hasValue())
239  Diag << Insertion.getValue();
240 
241  return;
242  }
243 
244  SourceLocation IdentifierLoc;
245  if (const auto *TL = Result.Nodes.getNodeAs<TypeLoc>(AutoPtrTokenId)) {
246  IdentifierLoc = locateFromTypeLoc(TL, SM);
247  } else if (const auto *D =
248  Result.Nodes.getNodeAs<UsingDecl>(AutoPtrTokenId)) {
249  IdentifierLoc = locateFromUsingDecl(D, SM);
250  } else {
251  llvm_unreachable("Bad Callback. No node provided.");
252  }
253 
254  if (IdentifierLoc.isMacroID())
255  IdentifierLoc = SM.getSpellingLoc(IdentifierLoc);
256 
257  // Ensure that only the 'auto_ptr' token is replaced and not the template
258  // aliases.
259  if (!checkTokenIsAutoPtr(IdentifierLoc, SM, LangOptions()))
260  return;
261 
262  SourceLocation EndLoc =
263  IdentifierLoc.getLocWithOffset(strlen("auto_ptr") - 1);
264  diag(IdentifierLoc, "auto_ptr is deprecated, use unique_ptr instead")
265  << FixItHint::CreateReplacement(SourceRange(IdentifierLoc, EndLoc),
266  "unique_ptr");
267 }
268 
269 } // namespace modernize
270 } // namespace tidy
271 } // namespace clang
const std::string Name
Definition: USRFinder.cpp:164
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:187
static SourceLocation locateFromUsingDecl(const UsingDecl *UsingAutoPtrDecl, const SourceManager &SM)
Locates the auto_ptr token in using declarations.
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:262
static const char AutoPtrOwnershipTransferId[]
static StringRef toString(IncludeStyle Style)
Converts IncludeStyle to string representation.
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
static const char AutoPtrTokenId[]
StatementMatcher makeTransferOwnershipExprMatcher()
Creates a matcher that finds the std::auto_ptr copy-ctor and assign-operator expressions.
SourceManager & SM
TypeLocMatcher makeAutoPtrTypeLocMatcher()
Matches declarations whose declaration context is the C++ standard library namespace std...
static bool checkTokenIsAutoPtr(SourceLocation TokenStart, const SourceManager &SM, const LangOptions &LO)
Verifies that the token at TokenStart is 'auto_ptr'.
AST_MATCHER(Expr, isLValue)
Matches expressions that are lvalues.
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Definition: ClangTidy.cpp:436
std::map< std::string, std::string > OptionMap
DeclarationMatcher makeAutoPtrUsingDeclMatcher()
Creates a matcher that finds the using declarations referring to std::auto_ptr.
void registerPPCallbacks(CompilerInstance &Compiler) override
Override this to register PPCallbacks with Compiler.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
CharSourceRange Range
SourceRange for the file name.
Produces fixes to insert specified includes to source files, if not yet present.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
ClangTidyContext & Context
Definition: ClangTidy.cpp:87
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
static SourceLocation locateFromTypeLoc(const TypeLoc *AutoPtrTypeLoc, const SourceManager &SM)
Locates the auto_ptr token when it is referred by a TypeLoc.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidy.cpp:403
const NamedDecl * Result
Definition: USRFinder.cpp:162