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
AST_MATCHER(Expr,isLValue)36 AST_MATCHER(Expr, isLValue) { return Node.getValueKind() == VK_LValue; }
37 
38 } // namespace
39 
ReplaceAutoPtrCheck(StringRef Name,ClangTidyContext * Context)40 ReplaceAutoPtrCheck::ReplaceAutoPtrCheck(StringRef Name,
41                                          ClangTidyContext *Context)
42     : ClangTidyCheck(Name, Context),
43       Inserter(Options.getLocalOrGlobal("IncludeStyle",
44                                         utils::IncludeSorter::IS_LLVM),
45                areDiagsSelfContained()) {}
46 
storeOptions(ClangTidyOptions::OptionMap & Opts)47 void ReplaceAutoPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
48   Options.store(Opts, "IncludeStyle", Inserter.getStyle());
49 }
50 
registerMatchers(MatchFinder * Finder)51 void ReplaceAutoPtrCheck::registerMatchers(MatchFinder *Finder) {
52   auto AutoPtrDecl = recordDecl(hasName("auto_ptr"), isInStdNamespace());
53   auto AutoPtrType = qualType(hasDeclaration(AutoPtrDecl));
54 
55   //   std::auto_ptr<int> a;
56   //        ^~~~~~~~~~~~~
57   //
58   //   typedef std::auto_ptr<int> int_ptr_t;
59   //                ^~~~~~~~~~~~~
60   //
61   //   std::auto_ptr<int> fn(std::auto_ptr<int>);
62   //        ^~~~~~~~~~~~~         ^~~~~~~~~~~~~
63   Finder->addMatcher(typeLoc(loc(qualType(AutoPtrType,
64                                           // Skip elaboratedType() as the named
65                                           // type will match soon thereafter.
66                                           unless(elaboratedType()))))
67                          .bind(AutoPtrTokenId),
68                      this);
69 
70   //   using std::auto_ptr;
71   //   ^~~~~~~~~~~~~~~~~~~
72   Finder->addMatcher(usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(namedDecl(
73                                    hasName("auto_ptr"), isInStdNamespace()))))
74                          .bind(AutoPtrTokenId),
75                      this);
76 
77   // Find ownership transfers via copy construction and assignment.
78   // AutoPtrOwnershipTransferId is bound to the part that has to be wrapped
79   // into std::move().
80   //   std::auto_ptr<int> i, j;
81   //   i = j;
82   //   ~~~~^
83   auto MovableArgumentMatcher =
84       expr(isLValue(), hasType(AutoPtrType)).bind(AutoPtrOwnershipTransferId);
85 
86   Finder->addMatcher(
87       cxxOperatorCallExpr(hasOverloadedOperatorName("="),
88                           callee(cxxMethodDecl(ofClass(AutoPtrDecl))),
89                           hasArgument(1, MovableArgumentMatcher)),
90       this);
91   Finder->addMatcher(
92       traverse(TK_AsIs,
93                cxxConstructExpr(hasType(AutoPtrType), argumentCountIs(1),
94                                 hasArgument(0, MovableArgumentMatcher))),
95       this);
96 }
97 
registerPPCallbacks(const SourceManager & SM,Preprocessor * PP,Preprocessor * ModuleExpanderPP)98 void ReplaceAutoPtrCheck::registerPPCallbacks(const SourceManager &SM,
99                                               Preprocessor *PP,
100                                               Preprocessor *ModuleExpanderPP) {
101   Inserter.registerPreprocessor(PP);
102 }
103 
check(const MatchFinder::MatchResult & Result)104 void ReplaceAutoPtrCheck::check(const MatchFinder::MatchResult &Result) {
105   SourceManager &SM = *Result.SourceManager;
106   if (const auto *E =
107           Result.Nodes.getNodeAs<Expr>(AutoPtrOwnershipTransferId)) {
108     CharSourceRange Range = Lexer::makeFileCharRange(
109         CharSourceRange::getTokenRange(E->getSourceRange()), SM, LangOptions());
110 
111     if (Range.isInvalid())
112       return;
113 
114     auto Diag = diag(Range.getBegin(), "use std::move to transfer ownership")
115                 << FixItHint::CreateInsertion(Range.getBegin(), "std::move(")
116                 << FixItHint::CreateInsertion(Range.getEnd(), ")")
117                 << Inserter.createMainFileIncludeInsertion("<utility>");
118 
119     return;
120   }
121 
122   SourceLocation AutoPtrLoc;
123   if (const auto *TL = Result.Nodes.getNodeAs<TypeLoc>(AutoPtrTokenId)) {
124     //   std::auto_ptr<int> i;
125     //        ^
126     if (auto Loc = TL->getAs<TemplateSpecializationTypeLoc>())
127       AutoPtrLoc = Loc.getTemplateNameLoc();
128   } else if (const auto *D =
129                  Result.Nodes.getNodeAs<UsingDecl>(AutoPtrTokenId)) {
130     // using std::auto_ptr;
131     //            ^
132     AutoPtrLoc = D->getNameInfo().getBeginLoc();
133   } else {
134     llvm_unreachable("Bad Callback. No node provided.");
135   }
136 
137   if (AutoPtrLoc.isMacroID())
138     AutoPtrLoc = SM.getSpellingLoc(AutoPtrLoc);
139 
140   // Ensure that only the 'auto_ptr' token is replaced and not the template
141   // aliases.
142   if (StringRef(SM.getCharacterData(AutoPtrLoc), strlen("auto_ptr")) !=
143       "auto_ptr")
144     return;
145 
146   SourceLocation EndLoc =
147       AutoPtrLoc.getLocWithOffset(strlen("auto_ptr") - 1);
148   diag(AutoPtrLoc, "auto_ptr is deprecated, use unique_ptr instead")
149       << FixItHint::CreateReplacement(SourceRange(AutoPtrLoc, EndLoc),
150                                       "unique_ptr");
151 }
152 
153 } // namespace modernize
154 } // namespace tidy
155 } // namespace clang
156