1 //===--- NewDeleteOverloadsCheck.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 "NewDeleteOverloadsCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 using namespace clang::ast_matchers
;
15 namespace clang::tidy::misc
{
19 AST_MATCHER(FunctionDecl
, isPlacementOverload
) {
21 switch (Node
.getOverloadedOperator()) {
34 // Variadic functions are always placement functions.
35 if (Node
.isVariadic())
38 // Placement new is easy: it always has more than one parameter (the first
39 // parameter is always the size). If it's an overload of delete or delete[]
40 // that has only one parameter, it's never a placement delete.
42 return Node
.getNumParams() > 1;
43 if (Node
.getNumParams() == 1)
46 // Placement delete is a little more challenging. They always have more than
47 // one parameter with the first parameter being a pointer. However, the
48 // second parameter can be a size_t for sized deallocation, and that is never
49 // a placement delete operator.
50 if (Node
.getNumParams() <= 1 || Node
.getNumParams() > 2)
53 const auto *FPT
= Node
.getType()->castAs
<FunctionProtoType
>();
54 ASTContext
&Ctx
= Node
.getASTContext();
55 if (Ctx
.getLangOpts().SizedDeallocation
&&
56 Ctx
.hasSameType(FPT
->getParamType(1), Ctx
.getSizeType()))
62 OverloadedOperatorKind
getCorrespondingOverload(const FunctionDecl
*FD
) {
63 switch (FD
->getOverloadedOperator()) {
71 return OO_Array_Delete
;
75 llvm_unreachable("Not an overloaded allocation operator");
78 const char *getOperatorName(OverloadedOperatorKind K
) {
83 return "operator new";
85 return "operator delete";
87 return "operator new[]";
89 return "operator delete[]";
91 llvm_unreachable("Not an overloaded allocation operator");
94 bool areCorrespondingOverloads(const FunctionDecl
*LHS
,
95 const FunctionDecl
*RHS
) {
96 return RHS
->getOverloadedOperator() == getCorrespondingOverload(LHS
);
99 bool hasCorrespondingOverloadInBaseClass(const CXXMethodDecl
*MD
,
100 const CXXRecordDecl
*RD
= nullptr) {
102 // Check the methods in the given class and accessible to derived classes.
103 for (const auto *BMD
: RD
->methods())
104 if (BMD
->isOverloadedOperator() && BMD
->getAccess() != AS_private
&&
105 areCorrespondingOverloads(MD
, BMD
))
108 // Get the parent class of the method; we do not need to care about checking
109 // the methods in this class as the caller has already done that by looking
110 // at the declaration contexts.
111 RD
= MD
->getParent();
114 for (const auto &BS
: RD
->bases()) {
115 // We can't say much about a dependent base class, but to avoid false
116 // positives assume it can have a corresponding overload.
117 if (BS
.getType()->isDependentType())
119 if (const auto *BaseRD
= BS
.getType()->getAsCXXRecordDecl())
120 if (hasCorrespondingOverloadInBaseClass(MD
, BaseRD
))
127 } // anonymous namespace
129 void NewDeleteOverloadsCheck::registerMatchers(MatchFinder
*Finder
) {
130 // Match all operator new and operator delete overloads (including the array
131 // forms). Do not match implicit operators, placement operators, or
132 // deleted/private operators.
134 // Technically, trivially-defined operator delete seems like a reasonable
135 // thing to also skip. e.g., void operator delete(void *) {}
136 // However, I think it's more reasonable to warn in this case as the user
137 // should really be writing that as a deleted function.
139 functionDecl(unless(anyOf(isImplicit(), isPlacementOverload(),
140 isDeleted(), cxxMethodDecl(isPrivate()))),
141 anyOf(hasOverloadedOperatorName("new"),
142 hasOverloadedOperatorName("new[]"),
143 hasOverloadedOperatorName("delete"),
144 hasOverloadedOperatorName("delete[]")))
149 void NewDeleteOverloadsCheck::check(const MatchFinder::MatchResult
&Result
) {
150 // Add any matches we locate to the list of things to be checked at the
151 // end of the translation unit.
152 const auto *FD
= Result
.Nodes
.getNodeAs
<FunctionDecl
>("func");
153 const CXXRecordDecl
*RD
= nullptr;
154 if (const auto *MD
= dyn_cast
<CXXMethodDecl
>(FD
))
155 RD
= MD
->getParent();
156 Overloads
[RD
].push_back(FD
);
159 void NewDeleteOverloadsCheck::onEndOfTranslationUnit() {
160 // Walk over the list of declarations we've found to see if there is a
161 // corresponding overload at the same declaration context or within a base
162 // class. If there is not, add the element to the list of declarations to
164 SmallVector
<const FunctionDecl
*, 4> Diagnose
;
165 for (const auto &RP
: Overloads
) {
166 // We don't care about the CXXRecordDecl key in the map; we use it as a way
167 // to shard the overloads by declaration context to reduce the algorithmic
168 // complexity when searching for corresponding free store functions.
169 for (const auto *Overload
: RP
.second
) {
171 std::find_if(RP
.second
.begin(), RP
.second
.end(),
172 [&Overload
](const FunctionDecl
*FD
) {
175 // If the declaration contexts don't match, we don't
176 // need to check any further.
177 if (FD
->getDeclContext() != Overload
->getDeclContext())
180 // Since the declaration contexts match, see whether
181 // the current element is the corresponding operator.
182 if (!areCorrespondingOverloads(Overload
, FD
))
188 if (Match
== RP
.second
.end()) {
189 // Check to see if there is a corresponding overload in a base class
190 // context. If there isn't, or if the overload is not a class member
191 // function, then we should diagnose.
192 const auto *MD
= dyn_cast
<CXXMethodDecl
>(Overload
);
193 if (!MD
|| !hasCorrespondingOverloadInBaseClass(MD
))
194 Diagnose
.push_back(Overload
);
199 for (const auto *FD
: Diagnose
)
200 diag(FD
->getLocation(), "declaration of %0 has no matching declaration "
201 "of '%1' at the same scope")
202 << FD
<< getOperatorName(getCorrespondingOverload(FD
));
205 } // namespace clang::tidy::misc