• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
9 #include "UniqueptrResetReleaseCheck.h"
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 
registerMatchers(MatchFinder * Finder)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 {
getDeleterForUniquePtr(const MatchFinder::MatchResult & Result,StringRef ID)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 
areDeletersCompatible(const MatchFinder::MatchResult & Result)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 
check(const MatchFinder::MatchResult & Result)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
131