//===--- UniqueptrResetReleaseCheck.cpp - clang-tidy ----------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "UniqueptrResetReleaseCheck.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Lex/Lexer.h" using namespace clang::ast_matchers; namespace clang::tidy::misc { UniqueptrResetReleaseCheck::UniqueptrResetReleaseCheck( StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), Inserter(Options.getLocalOrGlobal("IncludeStyle", utils::IncludeSorter::IS_LLVM), areDiagsSelfContained()) {} void UniqueptrResetReleaseCheck::storeOptions( ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "IncludeStyle", Inserter.getStyle()); } void UniqueptrResetReleaseCheck::registerPPCallbacks( const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { Inserter.registerPreprocessor(PP); } void UniqueptrResetReleaseCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( cxxMemberCallExpr( callee(memberExpr( member(cxxMethodDecl( hasName("reset"), ofClass(cxxRecordDecl(hasName("::std::unique_ptr"), decl().bind("left_class")))))) .bind("reset_member")), hasArgument( 0, ignoringParenImpCasts(cxxMemberCallExpr( on(expr().bind("right")), callee(memberExpr(member(cxxMethodDecl( hasName("release"), ofClass(cxxRecordDecl( hasName("::std::unique_ptr"), decl().bind("right_class")))))) .bind("release_member")))))) .bind("reset_call"), this); } namespace { const Type *getDeleterForUniquePtr(const MatchFinder::MatchResult &Result, StringRef ID) { const auto *Class = Result.Nodes.getNodeAs(ID); if (!Class) return nullptr; auto DeleterArgument = Class->getTemplateArgs()[1]; if (DeleterArgument.getKind() != TemplateArgument::Type) return nullptr; return DeleterArgument.getAsType().getTypePtr(); } bool areDeletersCompatible(const MatchFinder::MatchResult &Result) { const Type *LeftDeleterType = getDeleterForUniquePtr(Result, "left_class"); const Type *RightDeleterType = getDeleterForUniquePtr(Result, "right_class"); if (LeftDeleterType->getUnqualifiedDesugaredType() == RightDeleterType->getUnqualifiedDesugaredType()) { // Same type. We assume they are compatible. // This check handles the case where the deleters are function pointers. return true; } const CXXRecordDecl *LeftDeleter = LeftDeleterType->getAsCXXRecordDecl(); const CXXRecordDecl *RightDeleter = RightDeleterType->getAsCXXRecordDecl(); if (!LeftDeleter || !RightDeleter) return false; if (LeftDeleter->getCanonicalDecl() == RightDeleter->getCanonicalDecl()) { // Same class. We assume they are compatible. return true; } const auto *LeftAsTemplate = dyn_cast(LeftDeleter); const auto *RightAsTemplate = dyn_cast(RightDeleter); if (LeftAsTemplate && RightAsTemplate && LeftAsTemplate->getSpecializedTemplate() == RightAsTemplate->getSpecializedTemplate()) { // They are different instantiations of the same template. We assume they // are compatible. // This handles things like std::default_delete vs. // std::default_delete. return true; } return false; } } // namespace void UniqueptrResetReleaseCheck::check(const MatchFinder::MatchResult &Result) { if (!areDeletersCompatible(Result)) return; const auto *ResetMember = Result.Nodes.getNodeAs("reset_member"); const auto *ReleaseMember = Result.Nodes.getNodeAs("release_member"); const auto *Right = Result.Nodes.getNodeAs("right"); const auto *ResetCall = Result.Nodes.getNodeAs("reset_call"); StringRef AssignmentText = " = "; StringRef TrailingText = ""; bool NeedsUtilityInclude = false; if (ReleaseMember->isArrow()) { AssignmentText = " = std::move(*"; TrailingText = ")"; NeedsUtilityInclude = true; } else if (!Right->isPRValue()) { AssignmentText = " = std::move("; TrailingText = ")"; NeedsUtilityInclude = true; } auto D = diag(ResetMember->getExprLoc(), "prefer 'unique_ptr<>' assignment over 'release' and 'reset'"); if (ResetMember->isArrow()) D << FixItHint::CreateInsertion(ResetMember->getBeginLoc(), "*"); D << FixItHint::CreateReplacement( CharSourceRange::getCharRange(ResetMember->getOperatorLoc(), Right->getBeginLoc()), AssignmentText) << FixItHint::CreateReplacement( CharSourceRange::getTokenRange(ReleaseMember->getOperatorLoc(), ResetCall->getEndLoc()), TrailingText); if (NeedsUtilityInclude) D << Inserter.createIncludeInsertion( Result.SourceManager->getFileID(ResetMember->getBeginLoc()), ""); } } // namespace clang::tidy::misc