123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- //===--- 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<ClassTemplateSpecializationDecl>(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<ClassTemplateSpecializationDecl>(LeftDeleter);
- const auto *RightAsTemplate =
- dyn_cast<ClassTemplateSpecializationDecl>(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<Base> vs.
- // std::default_delete<Derived>.
- return true;
- }
- return false;
- }
- } // namespace
- void UniqueptrResetReleaseCheck::check(const MatchFinder::MatchResult &Result) {
- if (!areDeletersCompatible(Result))
- return;
- const auto *ResetMember = Result.Nodes.getNodeAs<MemberExpr>("reset_member");
- const auto *ReleaseMember =
- Result.Nodes.getNodeAs<MemberExpr>("release_member");
- const auto *Right = Result.Nodes.getNodeAs<Expr>("right");
- const auto *ResetCall =
- Result.Nodes.getNodeAs<CXXMemberCallExpr>("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()),
- "<utility>");
- }
- } // namespace clang::tidy::misc
|