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
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(LocalName: "IncludeStyle",
21 Default: utils::IncludeSorter::IS_LLVM),
22 areDiagsSelfContained()) {}
23
24void UniqueptrResetReleaseCheck::storeOptions(
25 ClangTidyOptions::OptionMap &Opts) {
26 Options.store(Options&: Opts, LocalName: "IncludeStyle", Value: 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 NodeMatch: cxxMemberCallExpr(
37 callee(InnerMatcher: memberExpr(
38 member(InnerMatcher: cxxMethodDecl(
39 hasName(Name: "reset"),
40 ofClass(InnerMatcher: cxxRecordDecl(hasName(Name: "::std::unique_ptr"),
41 decl().bind(ID: "left_class"))))))
42 .bind(ID: "reset_member")),
43 hasArgument(
44 N: 0, InnerMatcher: ignoringParenImpCasts(InnerMatcher: cxxMemberCallExpr(
45 on(InnerMatcher: expr().bind(ID: "right")),
46 callee(InnerMatcher: memberExpr(member(InnerMatcher: cxxMethodDecl(
47 hasName(Name: "release"),
48 ofClass(InnerMatcher: cxxRecordDecl(
49 hasName(Name: "::std::unique_ptr"),
50 decl().bind(ID: "right_class"))))))
51 .bind(ID: "release_member"))))))
52 .bind(ID: "reset_call"),
53 Action: this);
54}
55
56namespace {
57const Type *getDeleterForUniquePtr(const MatchFinder::MatchResult &Result,
58 StringRef ID) {
59 const auto *Class =
60 Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>(ID);
61 if (!Class)
62 return nullptr;
63 auto DeleterArgument = Class->getTemplateArgs()[1];
64 if (DeleterArgument.getKind() != TemplateArgument::Type)
65 return nullptr;
66 return DeleterArgument.getAsType().getTypePtr();
67}
68
69bool areDeletersCompatible(const MatchFinder::MatchResult &Result) {
70 const Type *LeftDeleterType = getDeleterForUniquePtr(Result, ID: "left_class");
71 const Type *RightDeleterType = getDeleterForUniquePtr(Result, ID: "right_class");
72
73 if (LeftDeleterType->getUnqualifiedDesugaredType() ==
74 RightDeleterType->getUnqualifiedDesugaredType()) {
75 // Same type. We assume they are compatible.
76 // This check handles the case where the deleters are function pointers.
77 return true;
78 }
79
80 const CXXRecordDecl *LeftDeleter = LeftDeleterType->getAsCXXRecordDecl();
81 const CXXRecordDecl *RightDeleter = RightDeleterType->getAsCXXRecordDecl();
82 if (!LeftDeleter || !RightDeleter)
83 return false;
84
85 if (LeftDeleter->getCanonicalDecl() == RightDeleter->getCanonicalDecl()) {
86 // Same class. We assume they are compatible.
87 return true;
88 }
89
90 const auto *LeftAsTemplate =
91 dyn_cast<ClassTemplateSpecializationDecl>(Val: LeftDeleter);
92 const auto *RightAsTemplate =
93 dyn_cast<ClassTemplateSpecializationDecl>(Val: RightDeleter);
94 if (LeftAsTemplate && RightAsTemplate &&
95 LeftAsTemplate->getSpecializedTemplate() ==
96 RightAsTemplate->getSpecializedTemplate()) {
97 // They are different instantiations of the same template. We assume they
98 // are compatible.
99 // This handles things like std::default_delete<Base> vs.
100 // std::default_delete<Derived>.
101 return true;
102 }
103 return false;
104}
105
106} // namespace
107
108void UniqueptrResetReleaseCheck::check(const MatchFinder::MatchResult &Result) {
109 if (!areDeletersCompatible(Result))
110 return;
111
112 const auto *ResetMember = Result.Nodes.getNodeAs<MemberExpr>(ID: "reset_member");
113 const auto *ReleaseMember =
114 Result.Nodes.getNodeAs<MemberExpr>(ID: "release_member");
115 const auto *Right = Result.Nodes.getNodeAs<Expr>(ID: "right");
116 const auto *ResetCall =
117 Result.Nodes.getNodeAs<CXXMemberCallExpr>(ID: "reset_call");
118
119 StringRef AssignmentText = " = ";
120 StringRef TrailingText = "";
121 bool NeedsUtilityInclude = false;
122 if (ReleaseMember->isArrow()) {
123 AssignmentText = " = std::move(*";
124 TrailingText = ")";
125 NeedsUtilityInclude = true;
126 } else if (!Right->isPRValue()) {
127 AssignmentText = " = std::move(";
128 TrailingText = ")";
129 NeedsUtilityInclude = true;
130 }
131
132 auto D = diag(Loc: ResetMember->getExprLoc(),
133 Description: "prefer 'unique_ptr<>' assignment over 'release' and 'reset'");
134 if (ResetMember->isArrow())
135 D << FixItHint::CreateInsertion(InsertionLoc: ResetMember->getBeginLoc(), Code: "*");
136 D << FixItHint::CreateReplacement(
137 CharSourceRange::getCharRange(ResetMember->getOperatorLoc(),
138 Right->getBeginLoc()),
139 AssignmentText)
140 << FixItHint::CreateReplacement(
141 CharSourceRange::getTokenRange(ReleaseMember->getOperatorLoc(),
142 ResetCall->getEndLoc()),
143 TrailingText);
144 if (NeedsUtilityInclude)
145 D << Inserter.createIncludeInsertion(
146 FileID: Result.SourceManager->getFileID(SpellingLoc: ResetMember->getBeginLoc()),
147 Header: "<utility>");
148}
149} // namespace clang::tidy::misc
150

source code of clang-tools-extra/clang-tidy/misc/UniqueptrResetReleaseCheck.cpp