Files
acceptance-tests
data
docs
external
Newtonsoft.Json
api-doc-tools
api-snapshot
aspnetwebstack
bdwgc
binary-reference-assemblies
bockbuild
boringssl
cecil
cecil-legacy
corefx
corert
helix-binaries
ikdasm
ikvm
illinker-test-assets
linker
llvm-project
clang
clang-tools-extra
change-namespace
clang-apply-replacements
clang-move
clang-query
clang-reorder-fields
clang-tidy
android
boost
bugprone
cert
cppcoreguidelines
fuchsia
google
hicpp
llvm
misc
CMakeLists.txt
DefinitionsInHeadersCheck.cpp
DefinitionsInHeadersCheck.h
ForwardingReferenceOverloadCheck.cpp
ForwardingReferenceOverloadCheck.h
IncorrectRoundings.cpp
IncorrectRoundings.h
LambdaFunctionNameCheck.cpp
LambdaFunctionNameCheck.h
MacroParenthesesCheck.cpp
MacroParenthesesCheck.h
MacroRepeatedSideEffectsCheck.cpp
MacroRepeatedSideEffectsCheck.h
MiscTidyModule.cpp
MisplacedConstCheck.cpp
MisplacedConstCheck.h
MisplacedWideningCastCheck.cpp
MisplacedWideningCastCheck.h
NewDeleteOverloadsCheck.cpp
NewDeleteOverloadsCheck.h
NonCopyableObjects.cpp
NonCopyableObjects.h
RedundantExpressionCheck.cpp
RedundantExpressionCheck.h
SizeofContainerCheck.cpp
SizeofContainerCheck.h
SizeofExpressionCheck.cpp
SizeofExpressionCheck.h
StaticAssertCheck.cpp
StaticAssertCheck.h
StringCompareCheck.cpp
StringCompareCheck.h
StringIntegerAssignmentCheck.cpp
StringIntegerAssignmentCheck.h
StringLiteralWithEmbeddedNulCheck.cpp
StringLiteralWithEmbeddedNulCheck.h
SuspiciousEnumUsageCheck.cpp
SuspiciousEnumUsageCheck.h
SuspiciousMissingCommaCheck.cpp
SuspiciousMissingCommaCheck.h
SuspiciousSemicolonCheck.cpp
SuspiciousSemicolonCheck.h
SuspiciousStringCompareCheck.cpp
SuspiciousStringCompareCheck.h
SwappedArgumentsCheck.cpp
SwappedArgumentsCheck.h
ThrowByValueCatchByReferenceCheck.cpp
ThrowByValueCatchByReferenceCheck.h
UnconventionalAssignOperatorCheck.cpp
UnconventionalAssignOperatorCheck.h
UndelegatedConstructor.cpp
UndelegatedConstructor.h
UniqueptrResetReleaseCheck.cpp
UniqueptrResetReleaseCheck.h
UnusedAliasDeclsCheck.cpp
UnusedAliasDeclsCheck.h
UnusedParametersCheck.cpp
UnusedParametersCheck.h
UnusedRAIICheck.cpp
UnusedRAIICheck.h
UnusedUsingDeclsCheck.cpp
UnusedUsingDeclsCheck.h
modernize
mpi
objc
performance
plugin
readability
tool
utils
CMakeLists.txt
ClangTidy.cpp
ClangTidy.h
ClangTidyDiagnosticConsumer.cpp
ClangTidyDiagnosticConsumer.h
ClangTidyModule.cpp
ClangTidyModule.h
ClangTidyModuleRegistry.h
ClangTidyOptions.cpp
ClangTidyOptions.h
add_new_check.py
rename_check.py
clang-tidy-vs
clangd
docs
include-fixer
modularize
pp-trace
test
tool-template
unittests
.arcconfig
.gitignore
CMakeLists.txt
CODE_OWNERS.TXT
LICENSE.TXT
README.txt
compiler-rt
libcxx
libcxxabi
libunwind
lld
lldb
llvm
openmp
polly
nuget-buildtasks
nunit-lite
roslyn-binaries
rx
xunit-binaries
how-to-bump-roslyn-binaries.md
ikvm-native
llvm
m4
man
mcs
mk
mono
msvc
netcore
po
runtime
samples
scripts
support
tools
COPYING.LIB
LICENSE
Makefile.am
Makefile.in
NEWS
README.md
acinclude.m4
aclocal.m4
autogen.sh
code_of_conduct.md
compile
config.guess
config.h.in
config.rpath
config.sub
configure.REMOVED.git-id
configure.ac.REMOVED.git-id
depcomp
install-sh
ltmain.sh.REMOVED.git-id
missing
mkinstalldirs
mono-uninstalled.pc.in
test-driver
winconfig.h
linux-packaging-mono/external/llvm-project/clang-tools-extra/clang-tidy/misc/NewDeleteOverloadsCheck.cpp
Xamarin Public Jenkins (auto-signing) 468663ddbb Imported Upstream version 6.10.0.49
Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
2020-01-16 16:38:04 +00:00

214 lines
7.3 KiB
C++

//===--- NewDeleteOverloadsCheck.cpp - clang-tidy--------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "NewDeleteOverloadsCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace misc {
namespace {
AST_MATCHER(FunctionDecl, isPlacementOverload) {
bool New;
switch (Node.getOverloadedOperator()) {
default:
return false;
case OO_New:
case OO_Array_New:
New = true;
break;
case OO_Delete:
case OO_Array_Delete:
New = false;
break;
}
// Variadic functions are always placement functions.
if (Node.isVariadic())
return true;
// Placement new is easy: it always has more than one parameter (the first
// parameter is always the size). If it's an overload of delete or delete[]
// that has only one parameter, it's never a placement delete.
if (New)
return Node.getNumParams() > 1;
if (Node.getNumParams() == 1)
return false;
// Placement delete is a little more challenging. They always have more than
// one parameter with the first parameter being a pointer. However, the
// second parameter can be a size_t for sized deallocation, and that is never
// a placement delete operator.
if (Node.getNumParams() <= 1 || Node.getNumParams() > 2)
return true;
const auto *FPT = Node.getType()->castAs<FunctionProtoType>();
ASTContext &Ctx = Node.getASTContext();
if (Ctx.getLangOpts().SizedDeallocation &&
Ctx.hasSameType(FPT->getParamType(1), Ctx.getSizeType()))
return false;
return true;
}
OverloadedOperatorKind getCorrespondingOverload(const FunctionDecl *FD) {
switch (FD->getOverloadedOperator()) {
default:
break;
case OO_New:
return OO_Delete;
case OO_Delete:
return OO_New;
case OO_Array_New:
return OO_Array_Delete;
case OO_Array_Delete:
return OO_Array_New;
}
llvm_unreachable("Not an overloaded allocation operator");
}
const char *getOperatorName(OverloadedOperatorKind K) {
switch (K) {
default:
break;
case OO_New:
return "operator new";
case OO_Delete:
return "operator delete";
case OO_Array_New:
return "operator new[]";
case OO_Array_Delete:
return "operator delete[]";
}
llvm_unreachable("Not an overloaded allocation operator");
}
bool areCorrespondingOverloads(const FunctionDecl *LHS,
const FunctionDecl *RHS) {
return RHS->getOverloadedOperator() == getCorrespondingOverload(LHS);
}
bool hasCorrespondingOverloadInBaseClass(const CXXMethodDecl *MD,
const CXXRecordDecl *RD = nullptr) {
if (RD) {
// Check the methods in the given class and accessible to derived classes.
for (const auto *BMD : RD->methods())
if (BMD->isOverloadedOperator() && BMD->getAccess() != AS_private &&
areCorrespondingOverloads(MD, BMD))
return true;
} else {
// Get the parent class of the method; we do not need to care about checking
// the methods in this class as the caller has already done that by looking
// at the declaration contexts.
RD = MD->getParent();
}
for (const auto &BS : RD->bases()) {
// We can't say much about a dependent base class, but to avoid false
// positives assume it can have a corresponding overload.
if (BS.getType()->isDependentType())
return true;
if (const auto *BaseRD = BS.getType()->getAsCXXRecordDecl())
if (hasCorrespondingOverloadInBaseClass(MD, BaseRD))
return true;
}
return false;
}
} // anonymous namespace
void NewDeleteOverloadsCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
// Match all operator new and operator delete overloads (including the array
// forms). Do not match implicit operators, placement operators, or
// deleted/private operators.
//
// Technically, trivially-defined operator delete seems like a reasonable
// thing to also skip. e.g., void operator delete(void *) {}
// However, I think it's more reasonable to warn in this case as the user
// should really be writing that as a deleted function.
Finder->addMatcher(
functionDecl(unless(anyOf(isImplicit(), isPlacementOverload(),
isDeleted(), cxxMethodDecl(isPrivate()))),
anyOf(hasOverloadedOperatorName("new"),
hasOverloadedOperatorName("new[]"),
hasOverloadedOperatorName("delete"),
hasOverloadedOperatorName("delete[]")))
.bind("func"),
this);
}
void NewDeleteOverloadsCheck::check(const MatchFinder::MatchResult &Result) {
// Add any matches we locate to the list of things to be checked at the
// end of the translation unit.
const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("func");
const CXXRecordDecl *RD = nullptr;
if (const auto *MD = dyn_cast<CXXMethodDecl>(FD))
RD = MD->getParent();
Overloads[RD].push_back(FD);
}
void NewDeleteOverloadsCheck::onEndOfTranslationUnit() {
// Walk over the list of declarations we've found to see if there is a
// corresponding overload at the same declaration context or within a base
// class. If there is not, add the element to the list of declarations to
// diagnose.
SmallVector<const FunctionDecl *, 4> Diagnose;
for (const auto &RP : Overloads) {
// We don't care about the CXXRecordDecl key in the map; we use it as a way
// to shard the overloads by declaration context to reduce the algorithmic
// complexity when searching for corresponding free store functions.
for (const auto *Overload : RP.second) {
const auto *Match =
std::find_if(RP.second.begin(), RP.second.end(),
[&Overload](const FunctionDecl *FD) {
if (FD == Overload)
return false;
// If the declaration contexts don't match, we don't
// need to check any further.
if (FD->getDeclContext() != Overload->getDeclContext())
return false;
// Since the declaration contexts match, see whether
// the current element is the corresponding operator.
if (!areCorrespondingOverloads(Overload, FD))
return false;
return true;
});
if (Match == RP.second.end()) {
// Check to see if there is a corresponding overload in a base class
// context. If there isn't, or if the overload is not a class member
// function, then we should diagnose.
const auto *MD = dyn_cast<CXXMethodDecl>(Overload);
if (!MD || !hasCorrespondingOverloadInBaseClass(MD))
Diagnose.push_back(Overload);
}
}
}
for (const auto *FD : Diagnose)
diag(FD->getLocation(), "declaration of %0 has no matching declaration "
"of '%1' at the same scope")
<< FD << getOperatorName(getCorrespondingOverload(FD));
}
} // namespace misc
} // namespace tidy
} // namespace clang