clang-tools  14.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 UniqueptrResetReleaseCheck::UniqueptrResetReleaseCheck(
20  StringRef Name, ClangTidyContext *Context)
21  : ClangTidyCheck(Name, Context),
22  Inserter(Options.getLocalOrGlobal("IncludeStyle",
23  utils::IncludeSorter::IS_LLVM)) {}
24 
27  Options.store(Opts, "IncludeStyle", Inserter.getStyle());
28 }
29 
31  const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
32  Inserter.registerPreprocessor(PP);
33 }
34 
36  Finder->addMatcher(
37  cxxMemberCallExpr(
38  callee(memberExpr(
39  member(cxxMethodDecl(
40  hasName("reset"),
41  ofClass(cxxRecordDecl(hasName("::std::unique_ptr"),
42  decl().bind("left_class"))))))
43  .bind("reset_member")),
44  hasArgument(
45  0, ignoringParenImpCasts(cxxMemberCallExpr(
46  on(expr().bind("right")),
47  callee(memberExpr(member(cxxMethodDecl(
48  hasName("release"),
49  ofClass(cxxRecordDecl(
50  hasName("::std::unique_ptr"),
51  decl().bind("right_class"))))))
52  .bind("release_member"))))))
53  .bind("reset_call"),
54  this);
55 }
56 
57 namespace {
58 const Type *getDeleterForUniquePtr(const MatchFinder::MatchResult &Result,
59  StringRef ID) {
60  const auto *Class =
61  Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>(ID);
62  if (!Class)
63  return nullptr;
64  auto DeleterArgument = Class->getTemplateArgs()[1];
65  if (DeleterArgument.getKind() != TemplateArgument::Type)
66  return nullptr;
67  return DeleterArgument.getAsType().getTypePtr();
68 }
69 
70 bool areDeletersCompatible(const MatchFinder::MatchResult &Result) {
71  const Type *LeftDeleterType = getDeleterForUniquePtr(Result, "left_class");
72  const Type *RightDeleterType = getDeleterForUniquePtr(Result, "right_class");
73 
74  if (LeftDeleterType->getUnqualifiedDesugaredType() ==
75  RightDeleterType->getUnqualifiedDesugaredType()) {
76  // Same type. We assume they are compatible.
77  // This check handles the case where the deleters are function pointers.
78  return true;
79  }
80 
81  const CXXRecordDecl *LeftDeleter = LeftDeleterType->getAsCXXRecordDecl();
82  const CXXRecordDecl *RightDeleter = RightDeleterType->getAsCXXRecordDecl();
83  if (!LeftDeleter || !RightDeleter)
84  return false;
85 
86  if (LeftDeleter->getCanonicalDecl() == RightDeleter->getCanonicalDecl()) {
87  // Same class. We assume they are compatible.
88  return true;
89  }
90 
91  const auto *LeftAsTemplate =
92  dyn_cast<ClassTemplateSpecializationDecl>(LeftDeleter);
93  const auto *RightAsTemplate =
94  dyn_cast<ClassTemplateSpecializationDecl>(RightDeleter);
95  if (LeftAsTemplate && RightAsTemplate &&
96  LeftAsTemplate->getSpecializedTemplate() ==
97  RightAsTemplate->getSpecializedTemplate()) {
98  // They are different instantiations of the same template. We assume they
99  // are compatible.
100  // This handles things like std::default_delete<Base> vs.
101  // std::default_delete<Derived>.
102  return true;
103  }
104  return false;
105 }
106 
107 } // namespace
108 
109 void UniqueptrResetReleaseCheck::check(const MatchFinder::MatchResult &Result) {
110  if (!areDeletersCompatible(Result))
111  return;
112 
113  const auto *ResetMember = Result.Nodes.getNodeAs<MemberExpr>("reset_member");
114  const auto *ReleaseMember =
115  Result.Nodes.getNodeAs<MemberExpr>("release_member");
116  const auto *Right = Result.Nodes.getNodeAs<Expr>("right");
117  const auto *ResetCall =
118  Result.Nodes.getNodeAs<CXXMemberCallExpr>("reset_call");
119 
120  StringRef AssignmentText = " = ";
121  StringRef TrailingText = "";
122  bool NeedsUtilityInclude = false;
123  if (ReleaseMember->isArrow()) {
124  AssignmentText = " = std::move(*";
125  TrailingText = ")";
126  NeedsUtilityInclude = true;
127  } else if (!Right->isPRValue()) {
128  AssignmentText = " = std::move(";
129  TrailingText = ")";
130  NeedsUtilityInclude = true;
131  }
132 
133  auto D = diag(ResetMember->getExprLoc(),
134  "prefer 'unique_ptr<>' assignment over 'release' and 'reset'");
135  if (ResetMember->isArrow())
136  D << FixItHint::CreateInsertion(ResetMember->getBeginLoc(), "*");
137  D << FixItHint::CreateReplacement(
138  CharSourceRange::getCharRange(ResetMember->getOperatorLoc(),
139  Right->getBeginLoc()),
140  AssignmentText)
141  << FixItHint::CreateReplacement(
142  CharSourceRange::getTokenRange(ReleaseMember->getOperatorLoc(),
143  ResetCall->getEndLoc()),
144  TrailingText);
145  if (NeedsUtilityInclude)
146  D << Inserter.createIncludeInsertion(
147  Result.SourceManager->getFileID(ResetMember->getBeginLoc()),
148  "<utility>");
149 }
150 } // namespace misc
151 } // namespace tidy
152 } // namespace clang
clang::tidy::ClangTidyOptions::OptionMap
llvm::StringMap< ClangTidyValue > OptionMap
Definition: ClangTidyOptions.h:115
Type
NodeType Type
Definition: HTMLGenerator.cpp:73
clang::tidy::misc::UniqueptrResetReleaseCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Definition: UniqueptrResetReleaseCheck.cpp:25
clang::tidy::utils::IncludeInserter::registerPreprocessor
void registerPreprocessor(Preprocessor *PP)
Registers this with the Preprocessor PP, must be called before this class is used.
Definition: IncludeInserter.cpp:42
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:54
clang::ast_matchers
Definition: AbseilMatcher.h:14
ns1::ns2::D
@ D
Definition: CategoricalFeature.h:3
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:416
clang::tidy::misc::UniqueptrResetReleaseCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: UniqueptrResetReleaseCheck.cpp:109
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:71
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:28
clang::tidy::misc::UniqueptrResetReleaseCheck::registerPPCallbacks
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
Override this to register PPCallbacks in the preprocessor.
Definition: UniqueptrResetReleaseCheck.cpp:30
UniqueptrResetReleaseCheck.h
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidyCheck.cpp:25
clang::tidy::bugprone::PP
static Preprocessor * PP
Definition: BadSignalToKillThreadCheck.cpp:29
clang::tidy::utils::IncludeInserter::getStyle
IncludeSorter::IncludeStyle getStyle() const
Definition: IncludeInserter.h:84
ID
static char ID
Definition: Logger.cpp:74
clang::tidy::misc::UniqueptrResetReleaseCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: UniqueptrResetReleaseCheck.cpp:35
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::utils::IncludeInserter::createIncludeInsertion
llvm::Optional< FixItHint > createIncludeInsertion(FileID FileID, llvm::StringRef Header)
Creates a Header inclusion directive fixit in the File FileID.
Definition: IncludeInserter.cpp:70
SM
const SourceManager & SM
Definition: IncludeCleaner.cpp:140
clang::tidy::ClangTidyCheck::OptionsView::store
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Definition: ClangTidyCheck.cpp:120