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