UniqueptrResetReleaseCheck.cpp 5.6 KB

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