1 //===--- ReplaceAutoPtrCheck.cpp - clang-tidy------------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "ReplaceAutoPtrCheck.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 13 #include "clang/Frontend/CompilerInstance.h" 14 #include "clang/Lex/Lexer.h" 15 #include "clang/Lex/Preprocessor.h" 16 17 using namespace clang; 18 using namespace clang::ast_matchers; 19 20 namespace clang { 21 namespace tidy { 22 namespace modernize { 23 24 namespace { 25 static const char AutoPtrTokenId[] = "AutoPrTokenId"; 26 static const char AutoPtrOwnershipTransferId[] = "AutoPtrOwnershipTransferId"; 27 28 /// \brief Matches expressions that are lvalues. 29 /// 30 /// In the following example, a[0] matches expr(isLValue()): 31 /// \code 32 /// std::string a[2]; 33 /// std::string b; 34 /// b = a[0]; 35 /// b = "this string won't match"; 36 /// \endcode 37 AST_MATCHER(Expr, isLValue) { return Node.getValueKind() == VK_LValue; } 38 39 /// Matches declarations whose declaration context is the C++ standard library 40 /// namespace std. 41 /// 42 /// Note that inline namespaces are silently ignored during the lookup since 43 /// both libstdc++ and libc++ are known to use them for versioning purposes. 44 /// 45 /// Given: 46 /// \code 47 /// namespace ns { 48 /// struct my_type {}; 49 /// using namespace std; 50 /// } 51 /// 52 /// using std::vector; 53 /// using ns:my_type; 54 /// using ns::list; 55 /// \code 56 /// 57 /// usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(isFromStdNamespace()))) 58 /// matches "using std::vector" and "using ns::list". 59 AST_MATCHER(Decl, isFromStdNamespace) { 60 const DeclContext *D = Node.getDeclContext(); 61 62 while (D->isInlineNamespace()) 63 D = D->getParent(); 64 65 if (!D->isNamespace() || !D->getParent()->isTranslationUnit()) 66 return false; 67 68 const IdentifierInfo *Info = cast<NamespaceDecl>(D)->getIdentifier(); 69 70 return (Info && Info->isStr("std")); 71 } 72 73 } // namespace 74 75 ReplaceAutoPtrCheck::ReplaceAutoPtrCheck(StringRef Name, 76 ClangTidyContext *Context) 77 : ClangTidyCheck(Name, Context), 78 IncludeStyle(utils::IncludeSorter::parseIncludeStyle( 79 Options.getLocalOrGlobal("IncludeStyle", "llvm"))) {} 80 81 void ReplaceAutoPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 82 Options.store(Opts, "IncludeStyle", 83 utils::IncludeSorter::toString(IncludeStyle)); 84 } 85 86 void ReplaceAutoPtrCheck::registerMatchers(MatchFinder *Finder) { 87 // Only register the matchers for C++; the functionality currently does not 88 // provide any benefit to other languages, despite being benign. 89 if (!getLangOpts().CPlusPlus) 90 return; 91 92 auto AutoPtrDecl = recordDecl(hasName("auto_ptr"), isFromStdNamespace()); 93 auto AutoPtrType = qualType(hasDeclaration(AutoPtrDecl)); 94 95 // std::auto_ptr<int> a; 96 // ^~~~~~~~~~~~~ 97 // 98 // typedef std::auto_ptr<int> int_ptr_t; 99 // ^~~~~~~~~~~~~ 100 // 101 // std::auto_ptr<int> fn(std::auto_ptr<int>); 102 // ^~~~~~~~~~~~~ ^~~~~~~~~~~~~ 103 Finder->addMatcher(typeLoc(loc(qualType(AutoPtrType, 104 // Skip elaboratedType() as the named 105 // type will match soon thereafter. 106 unless(elaboratedType())))) 107 .bind(AutoPtrTokenId), 108 this); 109 110 // using std::auto_ptr; 111 // ^~~~~~~~~~~~~~~~~~~ 112 Finder->addMatcher(usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(namedDecl( 113 hasName("auto_ptr"), isFromStdNamespace())))) 114 .bind(AutoPtrTokenId), 115 this); 116 117 // Find ownership transfers via copy construction and assignment. 118 // AutoPtrOwnershipTransferId is bound to the the part that has to be wrapped 119 // into std::move(). 120 // std::auto_ptr<int> i, j; 121 // i = j; 122 // ~~~~^ 123 auto MovableArgumentMatcher = 124 expr(isLValue(), hasType(AutoPtrType)).bind(AutoPtrOwnershipTransferId); 125 126 Finder->addMatcher( 127 cxxOperatorCallExpr(hasOverloadedOperatorName("="), 128 callee(cxxMethodDecl(ofClass(AutoPtrDecl))), 129 hasArgument(1, MovableArgumentMatcher)), 130 this); 131 Finder->addMatcher(cxxConstructExpr(hasType(AutoPtrType), argumentCountIs(1), 132 hasArgument(0, MovableArgumentMatcher)), 133 this); 134 } 135 136 void ReplaceAutoPtrCheck::registerPPCallbacks(CompilerInstance &Compiler) { 137 // Only register the preprocessor callbacks for C++; the functionality 138 // currently does not provide any benefit to other languages, despite being 139 // benign. 140 if (!getLangOpts().CPlusPlus) 141 return; 142 Inserter.reset(new utils::IncludeInserter( 143 Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle)); 144 Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks()); 145 } 146 147 void ReplaceAutoPtrCheck::check(const MatchFinder::MatchResult &Result) { 148 SourceManager &SM = *Result.SourceManager; 149 if (const auto *E = 150 Result.Nodes.getNodeAs<Expr>(AutoPtrOwnershipTransferId)) { 151 CharSourceRange Range = Lexer::makeFileCharRange( 152 CharSourceRange::getTokenRange(E->getSourceRange()), SM, LangOptions()); 153 154 if (Range.isInvalid()) 155 return; 156 157 auto Diag = diag(Range.getBegin(), "use std::move to transfer ownership") 158 << FixItHint::CreateInsertion(Range.getBegin(), "std::move(") 159 << FixItHint::CreateInsertion(Range.getEnd(), ")"); 160 161 if (auto Fix = 162 Inserter->CreateIncludeInsertion(SM.getMainFileID(), "utility", 163 /*IsAngled=*/true)) 164 Diag << *Fix; 165 166 return; 167 } 168 169 SourceLocation AutoPtrLoc; 170 if (const auto *TL = Result.Nodes.getNodeAs<TypeLoc>(AutoPtrTokenId)) { 171 // std::auto_ptr<int> i; 172 // ^ 173 if (auto Loc = TL->getAs<TemplateSpecializationTypeLoc>()) 174 AutoPtrLoc = Loc.getTemplateNameLoc(); 175 } else if (const auto *D = 176 Result.Nodes.getNodeAs<UsingDecl>(AutoPtrTokenId)) { 177 // using std::auto_ptr; 178 // ^ 179 AutoPtrLoc = D->getNameInfo().getBeginLoc(); 180 } else { 181 llvm_unreachable("Bad Callback. No node provided."); 182 } 183 184 if (AutoPtrLoc.isMacroID()) 185 AutoPtrLoc = SM.getSpellingLoc(AutoPtrLoc); 186 187 // Ensure that only the 'auto_ptr' token is replaced and not the template 188 // aliases. 189 if (StringRef(SM.getCharacterData(AutoPtrLoc), strlen("auto_ptr")) != 190 "auto_ptr") 191 return; 192 193 SourceLocation EndLoc = 194 AutoPtrLoc.getLocWithOffset(strlen("auto_ptr") - 1); 195 diag(AutoPtrLoc, "auto_ptr is deprecated, use unique_ptr instead") 196 << FixItHint::CreateReplacement(SourceRange(AutoPtrLoc, EndLoc), 197 "unique_ptr"); 198 } 199 200 } // namespace modernize 201 } // namespace tidy 202 } // namespace clang 203