//===--- ReplaceAutoPtrCheck.cpp - clang-tidy------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "ReplaceAutoPtrCheck.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/Lexer.h" #include "clang/Lex/Preprocessor.h" using namespace clang; using namespace clang::ast_matchers; namespace clang { namespace tidy { namespace modernize { static const char AutoPtrTokenId[] = "AutoPrTokenId"; static const char AutoPtrOwnershipTransferId[] = "AutoPtrOwnershipTransferId"; /// \brief Matches expressions that are lvalues. /// /// In the following example, a[0] matches expr(isLValue()): /// \code /// std::string a[2]; /// std::string b; /// b = a[0]; /// b = "this string won't match"; /// \endcode AST_MATCHER(Expr, isLValue) { return Node.getValueKind() == VK_LValue; } /// Matches declarations whose declaration context is the C++ standard library /// namespace std. /// /// Note that inline namespaces are silently ignored during the lookup since /// both libstdc++ and libc++ are known to use them for versioning purposes. /// /// Given: /// \code /// namespace ns { /// struct my_type {}; /// using namespace std; /// } /// /// using std::vector; /// using ns:my_type; /// using ns::list; /// \code /// /// usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(isFromStdNamespace()))) /// matches "using std::vector" and "using ns::list". AST_MATCHER(Decl, isFromStdNamespace) { const DeclContext *D = Node.getDeclContext(); while (D->isInlineNamespace()) D = D->getParent(); if (!D->isNamespace() || !D->getParent()->isTranslationUnit()) return false; const IdentifierInfo *Info = cast(D)->getIdentifier(); return (Info && Info->isStr("std")); } /// \brief Matcher that finds auto_ptr declarations. static DeclarationMatcher AutoPtrDecl = recordDecl(hasName("auto_ptr"), isFromStdNamespace()); /// \brief Matches types declared as auto_ptr. static TypeMatcher AutoPtrType = qualType(hasDeclaration(AutoPtrDecl)); /// \brief Matcher that finds expressions that are candidates to be wrapped with /// 'std::move'. /// /// Binds the id \c AutoPtrOwnershipTransferId to the expression. static StatementMatcher MovableArgumentMatcher = expr(allOf(isLValue(), hasType(AutoPtrType))) .bind(AutoPtrOwnershipTransferId); /// \brief Creates a matcher that finds the locations of types referring to the /// \c std::auto_ptr() type. /// /// \code /// std::auto_ptr a; /// ^~~~~~~~~~~~~ /// /// typedef std::auto_ptr int_ptr_t; /// ^~~~~~~~~~~~~ /// /// std::auto_ptr fn(std::auto_ptr); /// ^~~~~~~~~~~~~ ^~~~~~~~~~~~~ /// /// /// \endcode TypeLocMatcher makeAutoPtrTypeLocMatcher() { // Skip elaboratedType() as the named type will match soon thereafter. return typeLoc(loc(qualType(AutoPtrType, unless(elaboratedType())))) .bind(AutoPtrTokenId); } /// \brief Creates a matcher that finds the using declarations referring to /// \c std::auto_ptr. /// /// \code /// using std::auto_ptr; /// ^~~~~~~~~~~~~~~~~~~ /// \endcode DeclarationMatcher makeAutoPtrUsingDeclMatcher() { return usingDecl(hasAnyUsingShadowDecl(hasTargetDecl( allOf(hasName("auto_ptr"), isFromStdNamespace())))) .bind(AutoPtrTokenId); } /// \brief Creates a matcher that finds the \c std::auto_ptr copy-ctor and /// assign-operator expressions. /// /// \c AutoPtrOwnershipTransferId is assigned to the argument of the expression, /// this is the part that has to be wrapped by \c std::move(). /// /// \code /// std::auto_ptr i, j; /// i = j; /// ~~~~^ /// \endcode StatementMatcher makeTransferOwnershipExprMatcher() { return anyOf( cxxOperatorCallExpr(allOf(hasOverloadedOperatorName("="), callee(cxxMethodDecl(ofClass(AutoPtrDecl))), hasArgument(1, MovableArgumentMatcher))), cxxConstructExpr(allOf(hasType(AutoPtrType), argumentCountIs(1), hasArgument(0, MovableArgumentMatcher)))); } /// \brief Locates the \c auto_ptr token when it is referred by a \c TypeLoc. /// /// \code /// std::auto_ptr i; /// ^~~~~~~~~~~~~ /// \endcode /// /// The caret represents the location returned and the tildes cover the /// parameter \p AutoPtrTypeLoc. /// /// \return An invalid \c SourceLocation if not found, otherwise the location /// of the beginning of the \c auto_ptr token. static SourceLocation locateFromTypeLoc(const TypeLoc *AutoPtrTypeLoc, const SourceManager &SM) { auto TL = AutoPtrTypeLoc->getAs(); if (TL.isNull()) return SourceLocation(); return TL.getTemplateNameLoc(); } /// \brief Locates the \c auto_ptr token in using declarations. /// /// \code /// using std::auto_ptr; /// ^ /// \endcode /// /// The caret represents the location returned. /// /// \return An invalid \c SourceLocation if not found, otherwise the location /// of the beginning of the \c auto_ptr token. static SourceLocation locateFromUsingDecl(const UsingDecl *UsingAutoPtrDecl, const SourceManager &SM) { return UsingAutoPtrDecl->getNameInfo().getBeginLoc(); } /// \brief Verifies that the token at \p TokenStart is 'auto_ptr'. static bool checkTokenIsAutoPtr(SourceLocation TokenStart, const SourceManager &SM, const LangOptions &LO) { SmallVector Buffer; bool Invalid = false; StringRef Res = Lexer::getSpelling(TokenStart, Buffer, SM, LO, &Invalid); return (!Invalid && Res == "auto_ptr"); } ReplaceAutoPtrCheck::ReplaceAutoPtrCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), IncludeStyle(IncludeSorter::parseIncludeStyle( Options.get("IncludeStyle", "llvm"))) {} void ReplaceAutoPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "IncludeStyle", IncludeSorter::toString(IncludeStyle)); } void ReplaceAutoPtrCheck::registerMatchers(MatchFinder *Finder) { // Only register the matchers for C++; the functionality currently does not // provide any benefit to other languages, despite being benign. if (getLangOpts().CPlusPlus) { Finder->addMatcher(makeAutoPtrTypeLocMatcher(), this); Finder->addMatcher(makeAutoPtrUsingDeclMatcher(), this); Finder->addMatcher(makeTransferOwnershipExprMatcher(), this); } } void ReplaceAutoPtrCheck::registerPPCallbacks(CompilerInstance &Compiler) { // Only register the preprocessor callbacks for C++; the functionality // currently does not provide any benefit to other languages, despite being // benign. if (getLangOpts().CPlusPlus) { Inserter.reset(new IncludeInserter(Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle)); Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks()); } } void ReplaceAutoPtrCheck::check(const MatchFinder::MatchResult &Result) { SourceManager &SM = *Result.SourceManager; if (const auto *E = Result.Nodes.getNodeAs(AutoPtrOwnershipTransferId)) { CharSourceRange Range = Lexer::makeFileCharRange( CharSourceRange::getTokenRange(E->getSourceRange()), SM, LangOptions()); if (Range.isInvalid()) return; auto Diag = diag(Range.getBegin(), "use std::move to transfer ownership") << FixItHint::CreateInsertion(Range.getBegin(), "std::move(") << FixItHint::CreateInsertion(Range.getEnd(), ")"); auto Insertion = Inserter->CreateIncludeInsertion(SM.getMainFileID(), "utility", /*IsAngled=*/true); if (Insertion.hasValue()) Diag << Insertion.getValue(); return; } SourceLocation IdentifierLoc; if (const auto *TL = Result.Nodes.getNodeAs(AutoPtrTokenId)) { IdentifierLoc = locateFromTypeLoc(TL, SM); } else if (const auto *D = Result.Nodes.getNodeAs(AutoPtrTokenId)) { IdentifierLoc = locateFromUsingDecl(D, SM); } else { llvm_unreachable("Bad Callback. No node provided."); } if (IdentifierLoc.isMacroID()) IdentifierLoc = SM.getSpellingLoc(IdentifierLoc); // Ensure that only the 'auto_ptr' token is replaced and not the template // aliases. if (!checkTokenIsAutoPtr(IdentifierLoc, SM, LangOptions())) return; SourceLocation EndLoc = IdentifierLoc.getLocWithOffset(strlen("auto_ptr") - 1); diag(IdentifierLoc, "auto_ptr is deprecated, use unique_ptr instead") << FixItHint::CreateReplacement(SourceRange(IdentifierLoc, EndLoc), "unique_ptr"); } } // namespace modernize } // namespace tidy } // namespace clang