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