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