clang-tools  11.0.0
UniqueptrResetReleaseCheck.cpp
Go to the documentation of this file.
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 
10 #include "clang/ASTMatchers/ASTMatchFinder.h"
11 #include "clang/Lex/Lexer.h"
12 
13 using namespace clang::ast_matchers;
14 
15 namespace clang {
16 namespace tidy {
17 namespace misc {
18 
19 void UniqueptrResetReleaseCheck::registerMatchers(MatchFinder *Finder) {
20  Finder->addMatcher(
21  cxxMemberCallExpr(
22  on(expr().bind("left")), callee(memberExpr().bind("reset_member")),
23  callee(
24  cxxMethodDecl(hasName("reset"),
25  ofClass(cxxRecordDecl(hasName("::std::unique_ptr"),
26  decl().bind("left_class"))))),
27  has(ignoringParenImpCasts(cxxMemberCallExpr(
28  on(expr().bind("right")),
29  callee(memberExpr().bind("release_member")),
30  callee(cxxMethodDecl(
31  hasName("release"),
32  ofClass(cxxRecordDecl(hasName("::std::unique_ptr"),
33  decl().bind("right_class")))))))))
34  .bind("reset_call"),
35  this);
36 }
37 
38 namespace {
39 const Type *getDeleterForUniquePtr(const MatchFinder::MatchResult &Result,
40  StringRef ID) {
41  const auto *Class =
42  Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>(ID);
43  if (!Class)
44  return nullptr;
45  auto DeleterArgument = Class->getTemplateArgs()[1];
46  if (DeleterArgument.getKind() != TemplateArgument::Type)
47  return nullptr;
48  return DeleterArgument.getAsType().getTypePtr();
49 }
50 
51 bool areDeletersCompatible(const MatchFinder::MatchResult &Result) {
52  const Type *LeftDeleterType = getDeleterForUniquePtr(Result, "left_class");
53  const Type *RightDeleterType = getDeleterForUniquePtr(Result, "right_class");
54 
55  if (LeftDeleterType->getUnqualifiedDesugaredType() ==
56  RightDeleterType->getUnqualifiedDesugaredType()) {
57  // Same type. We assume they are compatible.
58  // This check handles the case where the deleters are function pointers.
59  return true;
60  }
61 
62  const CXXRecordDecl *LeftDeleter = LeftDeleterType->getAsCXXRecordDecl();
63  const CXXRecordDecl *RightDeleter = RightDeleterType->getAsCXXRecordDecl();
64  if (!LeftDeleter || !RightDeleter)
65  return false;
66 
67  if (LeftDeleter->getCanonicalDecl() == RightDeleter->getCanonicalDecl()) {
68  // Same class. We assume they are compatible.
69  return true;
70  }
71 
72  const auto *LeftAsTemplate =
73  dyn_cast<ClassTemplateSpecializationDecl>(LeftDeleter);
74  const auto *RightAsTemplate =
75  dyn_cast<ClassTemplateSpecializationDecl>(RightDeleter);
76  if (LeftAsTemplate && RightAsTemplate &&
77  LeftAsTemplate->getSpecializedTemplate() ==
78  RightAsTemplate->getSpecializedTemplate()) {
79  // They are different instantiations of the same template. We assume they
80  // are compatible.
81  // This handles things like std::default_delete<Base> vs.
82  // std::default_delete<Derived>.
83  return true;
84  }
85  return false;
86 }
87 
88 } // namespace
89 
90 void UniqueptrResetReleaseCheck::check(const MatchFinder::MatchResult &Result) {
91  if (!areDeletersCompatible(Result))
92  return;
93 
94  const auto *ResetMember = Result.Nodes.getNodeAs<MemberExpr>("reset_member");
95  const auto *ReleaseMember =
96  Result.Nodes.getNodeAs<MemberExpr>("release_member");
97  const auto *Right = Result.Nodes.getNodeAs<Expr>("right");
98  const auto *Left = Result.Nodes.getNodeAs<Expr>("left");
99  const auto *ResetCall =
100  Result.Nodes.getNodeAs<CXXMemberCallExpr>("reset_call");
101 
102  std::string LeftText = std::string(clang::Lexer::getSourceText(
103  CharSourceRange::getTokenRange(Left->getSourceRange()),
104  *Result.SourceManager, getLangOpts()));
105  std::string RightText = std::string(clang::Lexer::getSourceText(
106  CharSourceRange::getTokenRange(Right->getSourceRange()),
107  *Result.SourceManager, getLangOpts()));
108 
109  if (ResetMember->isArrow())
110  LeftText = "*" + LeftText;
111  if (ReleaseMember->isArrow())
112  RightText = "*" + RightText;
113  std::string DiagText;
114  // Even if x was rvalue, *x is not rvalue anymore.
115  if (!Right->isRValue() || ReleaseMember->isArrow()) {
116  RightText = "std::move(" + RightText + ")";
117  DiagText = "prefer ptr1 = std::move(ptr2) over ptr1.reset(ptr2.release())";
118  } else {
119  DiagText =
120  "prefer ptr = ReturnUnique() over ptr.reset(ReturnUnique().release())";
121  }
122  std::string NewText = LeftText + " = " + RightText;
123 
124  diag(ResetMember->getExprLoc(), DiagText) << FixItHint::CreateReplacement(
125  CharSourceRange::getTokenRange(ResetCall->getSourceRange()), NewText);
126 }
127 
128 } // namespace misc
129 } // namespace tidy
130 } // namespace clang
Type
NodeType Type
Definition: HTMLGenerator.cpp:73
clang::ast_matchers
Definition: AbseilMatcher.h:14
UniqueptrResetReleaseCheck.h
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27