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