clang-tools 22.0.0git
UniqueptrResetReleaseCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
13using namespace clang::ast_matchers;
14
15namespace clang::tidy::misc {
16
18 StringRef Name, ClangTidyContext *Context)
19 : ClangTidyCheck(Name, Context),
20 Inserter(Options.getLocalOrGlobal("IncludeStyle",
21 utils::IncludeSorter::IS_LLVM),
22 areDiagsSelfContained()) {}
23
26 Options.store(Opts, "IncludeStyle", Inserter.getStyle());
27}
28
30 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
31 Inserter.registerPreprocessor(PP);
32}
33
35 Finder->addMatcher(
36 cxxMemberCallExpr(
37 callee(memberExpr(
38 member(cxxMethodDecl(
39 hasName("reset"),
40 ofClass(cxxRecordDecl(hasName("::std::unique_ptr"),
41 decl().bind("left_class"))))))
42 .bind("reset_member")),
43 hasArgument(
44 0, ignoringParenImpCasts(cxxMemberCallExpr(
45 on(expr().bind("right")),
46 callee(memberExpr(member(cxxMethodDecl(
47 hasName("release"),
48 ofClass(cxxRecordDecl(
49 hasName("::std::unique_ptr"),
50 decl().bind("right_class"))))))
51 .bind("release_member"))))))
52 .bind("reset_call"),
53 this);
54}
55
56static const Type *
57getDeleterForUniquePtr(const MatchFinder::MatchResult &Result, StringRef ID) {
58 const auto *Class =
59 Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>(ID);
60 if (!Class)
61 return nullptr;
62 auto DeleterArgument = Class->getTemplateArgs()[1];
63 if (DeleterArgument.getKind() != TemplateArgument::Type)
64 return nullptr;
65 return DeleterArgument.getAsType().getTypePtr();
66}
67
68static bool areDeletersCompatible(const MatchFinder::MatchResult &Result) {
69 const Type *LeftDeleterType = getDeleterForUniquePtr(Result, "left_class");
70 const Type *RightDeleterType = getDeleterForUniquePtr(Result, "right_class");
71
72 if (LeftDeleterType->getUnqualifiedDesugaredType() ==
73 RightDeleterType->getUnqualifiedDesugaredType()) {
74 // Same type. We assume they are compatible.
75 // This check handles the case where the deleters are function pointers.
76 return true;
77 }
78
79 const CXXRecordDecl *LeftDeleter = LeftDeleterType->getAsCXXRecordDecl();
80 const CXXRecordDecl *RightDeleter = RightDeleterType->getAsCXXRecordDecl();
81 if (!LeftDeleter || !RightDeleter)
82 return false;
83
84 if (LeftDeleter->getCanonicalDecl() == RightDeleter->getCanonicalDecl()) {
85 // Same class. We assume they are compatible.
86 return true;
87 }
88
89 const auto *LeftAsTemplate =
90 dyn_cast<ClassTemplateSpecializationDecl>(LeftDeleter);
91 const auto *RightAsTemplate =
92 dyn_cast<ClassTemplateSpecializationDecl>(RightDeleter);
93 if (LeftAsTemplate && RightAsTemplate &&
94 LeftAsTemplate->getSpecializedTemplate() ==
95 RightAsTemplate->getSpecializedTemplate()) {
96 // They are different instantiations of the same template. We assume they
97 // are compatible.
98 // This handles things like std::default_delete<Base> vs.
99 // std::default_delete<Derived>.
100 return true;
101 }
102 return false;
103}
104
105void UniqueptrResetReleaseCheck::check(const MatchFinder::MatchResult &Result) {
106 if (!areDeletersCompatible(Result))
107 return;
108
109 const auto *ResetMember = Result.Nodes.getNodeAs<MemberExpr>("reset_member");
110 const auto *ReleaseMember =
111 Result.Nodes.getNodeAs<MemberExpr>("release_member");
112 const auto *Right = Result.Nodes.getNodeAs<Expr>("right");
113 const auto *ResetCall =
114 Result.Nodes.getNodeAs<CXXMemberCallExpr>("reset_call");
115
116 StringRef AssignmentText = " = ";
117 StringRef TrailingText = "";
118 bool NeedsUtilityInclude = false;
119 if (ReleaseMember->isArrow()) {
120 AssignmentText = " = std::move(*";
121 TrailingText = ")";
122 NeedsUtilityInclude = true;
123 } else if (!Right->isPRValue()) {
124 AssignmentText = " = std::move(";
125 TrailingText = ")";
126 NeedsUtilityInclude = true;
127 }
128
129 auto D = diag(ResetMember->getExprLoc(),
130 "prefer 'unique_ptr<>' assignment over 'release' and 'reset'");
131 if (ResetMember->isArrow())
132 D << FixItHint::CreateInsertion(ResetMember->getBeginLoc(), "*");
133 D << FixItHint::CreateReplacement(
134 CharSourceRange::getCharRange(ResetMember->getOperatorLoc(),
135 Right->getBeginLoc()),
136 AssignmentText)
137 << FixItHint::CreateReplacement(
138 CharSourceRange::getTokenRange(ReleaseMember->getOperatorLoc(),
139 ResetCall->getEndLoc()),
140 TrailingText);
141 if (NeedsUtilityInclude)
142 D << Inserter.createIncludeInsertion(
143 Result.SourceManager->getFileID(ResetMember->getBeginLoc()),
144 "<utility>");
145}
146} // namespace clang::tidy::misc
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
UniqueptrResetReleaseCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
static bool areDeletersCompatible(const MatchFinder::MatchResult &Result)
static const Type * getDeleterForUniquePtr(const MatchFinder::MatchResult &Result, StringRef ID)
llvm::StringMap< ClangTidyValue > OptionMap