clang-tools  10.0.0git
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  // Only register the matchers for C++11; the functionality currently does not
21  // provide any benefit to other languages, despite being benign.
22  if (!getLangOpts().CPlusPlus11)
23  return;
24 
25  Finder->addMatcher(
26  cxxMemberCallExpr(
27  on(expr().bind("left")), callee(memberExpr().bind("reset_member")),
28  callee(
29  cxxMethodDecl(hasName("reset"),
30  ofClass(cxxRecordDecl(hasName("::std::unique_ptr"),
31  decl().bind("left_class"))))),
32  has(ignoringParenImpCasts(cxxMemberCallExpr(
33  on(expr().bind("right")),
34  callee(memberExpr().bind("release_member")),
35  callee(cxxMethodDecl(
36  hasName("release"),
37  ofClass(cxxRecordDecl(hasName("::std::unique_ptr"),
38  decl().bind("right_class")))))))))
39  .bind("reset_call"),
40  this);
41 }
42 
43 namespace {
44 const Type *getDeleterForUniquePtr(const MatchFinder::MatchResult &Result,
45  StringRef ID) {
46  const auto *Class =
47  Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>(ID);
48  if (!Class)
49  return nullptr;
50  auto DeleterArgument = Class->getTemplateArgs()[1];
51  if (DeleterArgument.getKind() != TemplateArgument::Type)
52  return nullptr;
53  return DeleterArgument.getAsType().getTypePtr();
54 }
55 
56 bool areDeletersCompatible(const MatchFinder::MatchResult &Result) {
57  const Type *LeftDeleterType = getDeleterForUniquePtr(Result, "left_class");
58  const Type *RightDeleterType = getDeleterForUniquePtr(Result, "right_class");
59 
60  if (LeftDeleterType->getUnqualifiedDesugaredType() ==
61  RightDeleterType->getUnqualifiedDesugaredType()) {
62  // Same type. We assume they are compatible.
63  // This check handles the case where the deleters are function pointers.
64  return true;
65  }
66 
67  const CXXRecordDecl *LeftDeleter = LeftDeleterType->getAsCXXRecordDecl();
68  const CXXRecordDecl *RightDeleter = RightDeleterType->getAsCXXRecordDecl();
69  if (!LeftDeleter || !RightDeleter)
70  return false;
71 
72  if (LeftDeleter->getCanonicalDecl() == RightDeleter->getCanonicalDecl()) {
73  // Same class. We assume they are compatible.
74  return true;
75  }
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 
93 } // namespace
94 
95 void UniqueptrResetReleaseCheck::check(const MatchFinder::MatchResult &Result) {
96  if (!areDeletersCompatible(Result))
97  return;
98 
99  const auto *ResetMember = Result.Nodes.getNodeAs<MemberExpr>("reset_member");
100  const auto *ReleaseMember =
101  Result.Nodes.getNodeAs<MemberExpr>("release_member");
102  const auto *Right = Result.Nodes.getNodeAs<Expr>("right");
103  const auto *Left = Result.Nodes.getNodeAs<Expr>("left");
104  const auto *ResetCall =
105  Result.Nodes.getNodeAs<CXXMemberCallExpr>("reset_call");
106 
107  std::string LeftText = clang::Lexer::getSourceText(
108  CharSourceRange::getTokenRange(Left->getSourceRange()),
109  *Result.SourceManager, getLangOpts());
110  std::string RightText = clang::Lexer::getSourceText(
111  CharSourceRange::getTokenRange(Right->getSourceRange()),
112  *Result.SourceManager, getLangOpts());
113 
114  if (ResetMember->isArrow())
115  LeftText = "*" + LeftText;
116  if (ReleaseMember->isArrow())
117  RightText = "*" + RightText;
118  std::string DiagText;
119  // Even if x was rvalue, *x is not rvalue anymore.
120  if (!Right->isRValue() || ReleaseMember->isArrow()) {
121  RightText = "std::move(" + RightText + ")";
122  DiagText = "prefer ptr1 = std::move(ptr2) over ptr1.reset(ptr2.release())";
123  } else {
124  DiagText =
125  "prefer ptr = ReturnUnique() over ptr.reset(ReturnUnique().release())";
126  }
127  std::string NewText = LeftText + " = " + RightText;
128 
129  diag(ResetMember->getExprLoc(), DiagText) << FixItHint::CreateReplacement(
130  CharSourceRange::getTokenRange(ResetCall->getSourceRange()), NewText);
131 }
132 
133 } // namespace misc
134 } // namespace tidy
135 } // namespace clang
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
Definition: SourceCode.cpp:227
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
NodeType Type