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