CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
97 lines
3.7 KiB
C++
97 lines
3.7 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "MultipleInheritanceCheck.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
|
|
using namespace clang;
|
|
using namespace clang::ast_matchers;
|
|
|
|
namespace clang::tidy::misc {
|
|
|
|
namespace {
|
|
AST_MATCHER(CXXRecordDecl, hasBases) {
|
|
return Node.hasDefinition() && Node.getNumBases() > 0;
|
|
}
|
|
} // namespace
|
|
|
|
bool MultipleInheritanceCheck::isInterface(const CXXBaseSpecifier &Base) {
|
|
const CXXRecordDecl *const Node = Base.getType()->getAsCXXRecordDecl();
|
|
if (!Node)
|
|
return true;
|
|
|
|
assert(Node->isCompleteDefinition());
|
|
|
|
// Short circuit the lookup if we have analyzed this record before.
|
|
if (const auto CachedValue = InterfaceMap.find(Node);
|
|
CachedValue != InterfaceMap.end())
|
|
return CachedValue->second;
|
|
|
|
// To be an interface, a class must have...
|
|
const bool CurrentClassIsInterface =
|
|
// ...no bases that aren't interfaces...
|
|
llvm::none_of(Node->bases(),
|
|
[&](const CXXBaseSpecifier &I) {
|
|
return !I.isVirtual() && !isInterface(I);
|
|
}) &&
|
|
// ...no fields, and...
|
|
Node->field_empty() &&
|
|
// ...no methods that aren't pure virtual.
|
|
llvm::none_of(Node->methods(), [](const CXXMethodDecl *M) {
|
|
return M->isUserProvided() && !M->isPureVirtual() && !M->isStatic();
|
|
});
|
|
|
|
InterfaceMap.try_emplace(Node, CurrentClassIsInterface);
|
|
return CurrentClassIsInterface;
|
|
}
|
|
|
|
void MultipleInheritanceCheck::registerMatchers(MatchFinder *Finder) {
|
|
Finder->addMatcher(cxxRecordDecl(hasBases(), isDefinition()).bind("decl"),
|
|
this);
|
|
}
|
|
|
|
void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) {
|
|
const auto &D = *Result.Nodes.getNodeAs<CXXRecordDecl>("decl");
|
|
// Collect the direct and virtual concrete bases of the class.
|
|
SmallVector<const CXXRecordDecl *> DirectConcreteBases;
|
|
for (const CXXBaseSpecifier &Base : D.bases())
|
|
if (!Base.isVirtual() && !isInterface(Base))
|
|
DirectConcreteBases.push_back(Base.getType()->getAsCXXRecordDecl());
|
|
|
|
SmallVector<const CXXRecordDecl *> VirtualConcreteBases;
|
|
for (const CXXBaseSpecifier &VBase : D.vbases())
|
|
if (!isInterface(VBase))
|
|
VirtualConcreteBases.push_back(VBase.getType()->getAsCXXRecordDecl());
|
|
|
|
unsigned NumConcrete = DirectConcreteBases.size();
|
|
|
|
// Count only virtual concrete bases that introduce an additional
|
|
// implementation base, skipping those already represented by a more derived
|
|
// concrete base.
|
|
NumConcrete += llvm::count_if(
|
|
VirtualConcreteBases, [&](const CXXRecordDecl *VirtualBase) {
|
|
const bool HiddenByMoreDerivedVirtualBase = llvm::any_of(
|
|
VirtualConcreteBases, [&](const CXXRecordDecl *OtherVirtualBase) {
|
|
return VirtualBase != OtherVirtualBase &&
|
|
OtherVirtualBase->isVirtuallyDerivedFrom(VirtualBase);
|
|
});
|
|
const bool HiddenByDirectConcreteBase = llvm::any_of(
|
|
DirectConcreteBases, [&](const CXXRecordDecl *DirectBase) {
|
|
return DirectBase->isVirtuallyDerivedFrom(VirtualBase);
|
|
});
|
|
return !HiddenByMoreDerivedVirtualBase && !HiddenByDirectConcreteBase;
|
|
});
|
|
|
|
if (NumConcrete > 1)
|
|
diag(D.getBeginLoc(), "inheriting multiple classes that aren't "
|
|
"pure virtual is discouraged");
|
|
}
|
|
|
|
} // namespace clang::tidy::misc
|