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