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