brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.6 KiB · ab2077b Raw
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