1 //===--- UniqueptrResetReleaseCheck.cpp - clang-tidy ----------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "UniqueptrResetReleaseCheck.h"
10 #include "clang/ASTMatchers/ASTMatchFinder.h"
11 #include "clang/Lex/Lexer.h"
13 using namespace clang::ast_matchers
;
15 namespace clang::tidy::misc
{
17 UniqueptrResetReleaseCheck::UniqueptrResetReleaseCheck(
18 StringRef Name
, ClangTidyContext
*Context
)
19 : ClangTidyCheck(Name
, Context
),
20 Inserter(Options
.getLocalOrGlobal("IncludeStyle",
21 utils::IncludeSorter::IS_LLVM
),
22 areDiagsSelfContained()) {}
24 void UniqueptrResetReleaseCheck::storeOptions(
25 ClangTidyOptions::OptionMap
&Opts
) {
26 Options
.store(Opts
, "IncludeStyle", Inserter
.getStyle());
29 void UniqueptrResetReleaseCheck::registerPPCallbacks(
30 const SourceManager
&SM
, Preprocessor
*PP
, Preprocessor
*ModuleExpanderPP
) {
31 Inserter
.registerPreprocessor(PP
);
34 void UniqueptrResetReleaseCheck::registerMatchers(MatchFinder
*Finder
) {
40 ofClass(cxxRecordDecl(hasName("::std::unique_ptr"),
41 decl().bind("left_class"))))))
42 .bind("reset_member")),
44 0, ignoringParenImpCasts(cxxMemberCallExpr(
45 on(expr().bind("right")),
46 callee(memberExpr(member(cxxMethodDecl(
48 ofClass(cxxRecordDecl(
49 hasName("::std::unique_ptr"),
50 decl().bind("right_class"))))))
51 .bind("release_member"))))))
57 const Type
*getDeleterForUniquePtr(const MatchFinder::MatchResult
&Result
,
60 Result
.Nodes
.getNodeAs
<ClassTemplateSpecializationDecl
>(ID
);
63 auto DeleterArgument
= Class
->getTemplateArgs()[1];
64 if (DeleterArgument
.getKind() != TemplateArgument::Type
)
66 return DeleterArgument
.getAsType().getTypePtr();
69 bool areDeletersCompatible(const MatchFinder::MatchResult
&Result
) {
70 const Type
*LeftDeleterType
= getDeleterForUniquePtr(Result
, "left_class");
71 const Type
*RightDeleterType
= getDeleterForUniquePtr(Result
, "right_class");
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.
80 const CXXRecordDecl
*LeftDeleter
= LeftDeleterType
->getAsCXXRecordDecl();
81 const CXXRecordDecl
*RightDeleter
= RightDeleterType
->getAsCXXRecordDecl();
82 if (!LeftDeleter
|| !RightDeleter
)
85 if (LeftDeleter
->getCanonicalDecl() == RightDeleter
->getCanonicalDecl()) {
86 // Same class. We assume they are compatible.
90 const auto *LeftAsTemplate
=
91 dyn_cast
<ClassTemplateSpecializationDecl
>(LeftDeleter
);
92 const auto *RightAsTemplate
=
93 dyn_cast
<ClassTemplateSpecializationDecl
>(RightDeleter
);
94 if (LeftAsTemplate
&& RightAsTemplate
&&
95 LeftAsTemplate
->getSpecializedTemplate() ==
96 RightAsTemplate
->getSpecializedTemplate()) {
97 // They are different instantiations of the same template. We assume they
99 // This handles things like std::default_delete<Base> vs.
100 // std::default_delete<Derived>.
108 void UniqueptrResetReleaseCheck::check(const MatchFinder::MatchResult
&Result
) {
109 if (!areDeletersCompatible(Result
))
112 const auto *ResetMember
= Result
.Nodes
.getNodeAs
<MemberExpr
>("reset_member");
113 const auto *ReleaseMember
=
114 Result
.Nodes
.getNodeAs
<MemberExpr
>("release_member");
115 const auto *Right
= Result
.Nodes
.getNodeAs
<Expr
>("right");
116 const auto *ResetCall
=
117 Result
.Nodes
.getNodeAs
<CXXMemberCallExpr
>("reset_call");
119 StringRef AssignmentText
= " = ";
120 StringRef TrailingText
= "";
121 bool NeedsUtilityInclude
= false;
122 if (ReleaseMember
->isArrow()) {
123 AssignmentText
= " = std::move(*";
125 NeedsUtilityInclude
= true;
126 } else if (!Right
->isPRValue()) {
127 AssignmentText
= " = std::move(";
129 NeedsUtilityInclude
= true;
132 auto D
= diag(ResetMember
->getExprLoc(),
133 "prefer 'unique_ptr<>' assignment over 'release' and 'reset'");
134 if (ResetMember
->isArrow())
135 D
<< FixItHint::CreateInsertion(ResetMember
->getBeginLoc(), "*");
136 D
<< FixItHint::CreateReplacement(
137 CharSourceRange::getCharRange(ResetMember
->getOperatorLoc(),
138 Right
->getBeginLoc()),
140 << FixItHint::CreateReplacement(
141 CharSourceRange::getTokenRange(ReleaseMember
->getOperatorLoc(),
142 ResetCall
->getEndLoc()),
144 if (NeedsUtilityInclude
)
145 D
<< Inserter
.createIncludeInsertion(
146 Result
.SourceManager
->getFileID(ResetMember
->getBeginLoc()),
149 } // namespace clang::tidy::misc