xref: /llvm-project/clang-tools-extra/clang-tidy/modernize/ReplaceAutoPtrCheck.cpp (revision 1c705d9c538d1d4a24f34e93be2582bc7e3a3da4)
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 {
20 namespace tidy {
21 namespace modernize {
22 
23 namespace {
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 } // namespace
73 
74 ReplaceAutoPtrCheck::ReplaceAutoPtrCheck(StringRef Name,
75                                          ClangTidyContext *Context)
76     : ClangTidyCheck(Name, Context),
77       IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
78           Options.getLocalOrGlobal("IncludeStyle", "llvm"))) {}
79 
80 void ReplaceAutoPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
81   Options.store(Opts, "IncludeStyle",
82                 utils::IncludeSorter::toString(IncludeStyle));
83 }
84 
85 void ReplaceAutoPtrCheck::registerMatchers(MatchFinder *Finder) {
86   // Only register the matchers for C++; the functionality currently does not
87   // provide any benefit to other languages, despite being benign.
88   if (!getLangOpts().CPlusPlus)
89     return;
90 
91   auto AutoPtrDecl = recordDecl(hasName("auto_ptr"), isFromStdNamespace());
92   auto AutoPtrType = qualType(hasDeclaration(AutoPtrDecl));
93 
94   //   std::auto_ptr<int> a;
95   //        ^~~~~~~~~~~~~
96   //
97   //   typedef std::auto_ptr<int> int_ptr_t;
98   //                ^~~~~~~~~~~~~
99   //
100   //   std::auto_ptr<int> fn(std::auto_ptr<int>);
101   //        ^~~~~~~~~~~~~         ^~~~~~~~~~~~~
102   Finder->addMatcher(typeLoc(loc(qualType(AutoPtrType,
103                                           // Skip elaboratedType() as the named
104                                           // type will match soon thereafter.
105                                           unless(elaboratedType()))))
106                          .bind(AutoPtrTokenId),
107                      this);
108 
109   //   using std::auto_ptr;
110   //   ^~~~~~~~~~~~~~~~~~~
111   Finder->addMatcher(usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(namedDecl(
112                                    hasName("auto_ptr"), isFromStdNamespace()))))
113                          .bind(AutoPtrTokenId),
114                      this);
115 
116   // Find ownership transfers via copy construction and assignment.
117   // AutoPtrOwnershipTransferId is bound to the the part that has to be wrapped
118   // into std::move().
119   //   std::auto_ptr<int> i, j;
120   //   i = j;
121   //   ~~~~^
122   auto MovableArgumentMatcher =
123       expr(isLValue(), hasType(AutoPtrType)).bind(AutoPtrOwnershipTransferId);
124 
125   Finder->addMatcher(
126       cxxOperatorCallExpr(hasOverloadedOperatorName("="),
127                           callee(cxxMethodDecl(ofClass(AutoPtrDecl))),
128                           hasArgument(1, MovableArgumentMatcher)),
129       this);
130   Finder->addMatcher(cxxConstructExpr(hasType(AutoPtrType), argumentCountIs(1),
131                                       hasArgument(0, MovableArgumentMatcher)),
132                      this);
133 }
134 
135 void ReplaceAutoPtrCheck::registerPPCallbacks(const SourceManager &SM,
136                                               Preprocessor *PP,
137                                               Preprocessor *ModuleExpanderPP) {
138   // Only register the preprocessor callbacks for C++; the functionality
139   // currently does not provide any benefit to other languages, despite being
140   // benign.
141   if (!getLangOpts().CPlusPlus)
142     return;
143   Inserter = std::make_unique<utils::IncludeInserter>(SM, getLangOpts(),
144                                                        IncludeStyle);
145   PP->addPPCallbacks(Inserter->CreatePPCallbacks());
146 }
147 
148 void ReplaceAutoPtrCheck::check(const MatchFinder::MatchResult &Result) {
149   SourceManager &SM = *Result.SourceManager;
150   if (const auto *E =
151           Result.Nodes.getNodeAs<Expr>(AutoPtrOwnershipTransferId)) {
152     CharSourceRange Range = Lexer::makeFileCharRange(
153         CharSourceRange::getTokenRange(E->getSourceRange()), SM, LangOptions());
154 
155     if (Range.isInvalid())
156       return;
157 
158     auto Diag = diag(Range.getBegin(), "use std::move to transfer ownership")
159                 << FixItHint::CreateInsertion(Range.getBegin(), "std::move(")
160                 << FixItHint::CreateInsertion(Range.getEnd(), ")");
161 
162     if (auto Fix =
163             Inserter->CreateIncludeInsertion(SM.getMainFileID(), "utility",
164                                              /*IsAngled=*/true))
165       Diag << *Fix;
166 
167     return;
168   }
169 
170   SourceLocation AutoPtrLoc;
171   if (const auto *TL = Result.Nodes.getNodeAs<TypeLoc>(AutoPtrTokenId)) {
172     //   std::auto_ptr<int> i;
173     //        ^
174     if (auto Loc = TL->getAs<TemplateSpecializationTypeLoc>())
175       AutoPtrLoc = Loc.getTemplateNameLoc();
176   } else if (const auto *D =
177                  Result.Nodes.getNodeAs<UsingDecl>(AutoPtrTokenId)) {
178     // using std::auto_ptr;
179     //            ^
180     AutoPtrLoc = D->getNameInfo().getBeginLoc();
181   } else {
182     llvm_unreachable("Bad Callback. No node provided.");
183   }
184 
185   if (AutoPtrLoc.isMacroID())
186     AutoPtrLoc = SM.getSpellingLoc(AutoPtrLoc);
187 
188   // Ensure that only the 'auto_ptr' token is replaced and not the template
189   // aliases.
190   if (StringRef(SM.getCharacterData(AutoPtrLoc), strlen("auto_ptr")) !=
191       "auto_ptr")
192     return;
193 
194   SourceLocation EndLoc =
195       AutoPtrLoc.getLocWithOffset(strlen("auto_ptr") - 1);
196   diag(AutoPtrLoc, "auto_ptr is deprecated, use unique_ptr instead")
197       << FixItHint::CreateReplacement(SourceRange(AutoPtrLoc, EndLoc),
198                                       "unique_ptr");
199 }
200 
201 } // namespace modernize
202 } // namespace tidy
203 } // namespace clang
204