147 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "UniqueptrResetReleaseCheck.h"10#include "clang/ASTMatchers/ASTMatchFinder.h"11#include "clang/Lex/Lexer.h"12 13using namespace clang::ast_matchers;14 15namespace clang::tidy::misc {16 17UniqueptrResetReleaseCheck::UniqueptrResetReleaseCheck(18 StringRef Name, ClangTidyContext *Context)19 : ClangTidyCheck(Name, Context),20 Inserter(Options.getLocalOrGlobal("IncludeStyle",21 utils::IncludeSorter::IS_LLVM),22 areDiagsSelfContained()) {}23 24void UniqueptrResetReleaseCheck::storeOptions(25 ClangTidyOptions::OptionMap &Opts) {26 Options.store(Opts, "IncludeStyle", Inserter.getStyle());27}28 29void UniqueptrResetReleaseCheck::registerPPCallbacks(30 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {31 Inserter.registerPreprocessor(PP);32}33 34void UniqueptrResetReleaseCheck::registerMatchers(MatchFinder *Finder) {35 Finder->addMatcher(36 cxxMemberCallExpr(37 callee(memberExpr(38 member(cxxMethodDecl(39 hasName("reset"),40 ofClass(cxxRecordDecl(hasName("::std::unique_ptr"),41 decl().bind("left_class"))))))42 .bind("reset_member")),43 hasArgument(44 0, ignoringParenImpCasts(cxxMemberCallExpr(45 on(expr().bind("right")),46 callee(memberExpr(member(cxxMethodDecl(47 hasName("release"),48 ofClass(cxxRecordDecl(49 hasName("::std::unique_ptr"),50 decl().bind("right_class"))))))51 .bind("release_member"))))))52 .bind("reset_call"),53 this);54}55 56static const Type *57getDeleterForUniquePtr(const MatchFinder::MatchResult &Result, StringRef ID) {58 const auto *Class =59 Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>(ID);60 if (!Class)61 return nullptr;62 auto DeleterArgument = Class->getTemplateArgs()[1];63 if (DeleterArgument.getKind() != TemplateArgument::Type)64 return nullptr;65 return DeleterArgument.getAsType().getTypePtr();66}67 68static bool areDeletersCompatible(const MatchFinder::MatchResult &Result) {69 const Type *LeftDeleterType = getDeleterForUniquePtr(Result, "left_class");70 const Type *RightDeleterType = getDeleterForUniquePtr(Result, "right_class");71 72 if (LeftDeleterType->getUnqualifiedDesugaredType() ==73 RightDeleterType->getUnqualifiedDesugaredType()) {74 // Same type. We assume they are compatible.75 // This check handles the case where the deleters are function pointers.76 return true;77 }78 79 const CXXRecordDecl *LeftDeleter = LeftDeleterType->getAsCXXRecordDecl();80 const CXXRecordDecl *RightDeleter = RightDeleterType->getAsCXXRecordDecl();81 if (!LeftDeleter || !RightDeleter)82 return false;83 84 if (LeftDeleter->getCanonicalDecl() == RightDeleter->getCanonicalDecl()) {85 // Same class. We assume they are compatible.86 return true;87 }88 89 const auto *LeftAsTemplate =90 dyn_cast<ClassTemplateSpecializationDecl>(LeftDeleter);91 const auto *RightAsTemplate =92 dyn_cast<ClassTemplateSpecializationDecl>(RightDeleter);93 if (LeftAsTemplate && RightAsTemplate &&94 LeftAsTemplate->getSpecializedTemplate() ==95 RightAsTemplate->getSpecializedTemplate()) {96 // They are different instantiations of the same template. We assume they97 // are compatible.98 // This handles things like std::default_delete<Base> vs.99 // std::default_delete<Derived>.100 return true;101 }102 return false;103}104 105void UniqueptrResetReleaseCheck::check(const MatchFinder::MatchResult &Result) {106 if (!areDeletersCompatible(Result))107 return;108 109 const auto *ResetMember = Result.Nodes.getNodeAs<MemberExpr>("reset_member");110 const auto *ReleaseMember =111 Result.Nodes.getNodeAs<MemberExpr>("release_member");112 const auto *Right = Result.Nodes.getNodeAs<Expr>("right");113 const auto *ResetCall =114 Result.Nodes.getNodeAs<CXXMemberCallExpr>("reset_call");115 116 StringRef AssignmentText = " = ";117 StringRef TrailingText = "";118 bool NeedsUtilityInclude = false;119 if (ReleaseMember->isArrow()) {120 AssignmentText = " = std::move(*";121 TrailingText = ")";122 NeedsUtilityInclude = true;123 } else if (!Right->isPRValue()) {124 AssignmentText = " = std::move(";125 TrailingText = ")";126 NeedsUtilityInclude = true;127 }128 129 auto D = diag(ResetMember->getExprLoc(),130 "prefer 'unique_ptr<>' assignment over 'release' and 'reset'");131 if (ResetMember->isArrow())132 D << FixItHint::CreateInsertion(ResetMember->getBeginLoc(), "*");133 D << FixItHint::CreateReplacement(134 CharSourceRange::getCharRange(ResetMember->getOperatorLoc(),135 Right->getBeginLoc()),136 AssignmentText)137 << FixItHint::CreateReplacement(138 CharSourceRange::getTokenRange(ReleaseMember->getOperatorLoc(),139 ResetCall->getEndLoc()),140 TrailingText);141 if (NeedsUtilityInclude)142 D << Inserter.createIncludeInsertion(143 Result.SourceManager->getFileID(ResetMember->getBeginLoc()),144 "<utility>");145}146} // namespace clang::tidy::misc147