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
179 lines
6.5 KiB
C++
179 lines
6.5 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 "CrtpConstructorAccessibilityCheck.h"
|
|
#include "../utils/LexerUtils.h"
|
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
|
|
using namespace clang::ast_matchers;
|
|
|
|
namespace clang::tidy::bugprone {
|
|
|
|
static bool hasPrivateConstructor(const CXXRecordDecl *RD) {
|
|
return llvm::any_of(RD->ctors(), [](const CXXConstructorDecl *Ctor) {
|
|
return Ctor->getAccess() == AS_private;
|
|
});
|
|
}
|
|
|
|
static bool isDerivedParameterBefriended(const CXXRecordDecl *CRTP,
|
|
const NamedDecl *Param) {
|
|
return llvm::any_of(CRTP->friends(), [&](const FriendDecl *Friend) {
|
|
const TypeSourceInfo *const FriendType = Friend->getFriendType();
|
|
if (!FriendType)
|
|
return false;
|
|
|
|
const auto *const TTPT =
|
|
dyn_cast<TemplateTypeParmType>(FriendType->getType());
|
|
|
|
return TTPT && TTPT->getDecl() == Param;
|
|
});
|
|
}
|
|
|
|
static bool isDerivedClassBefriended(const CXXRecordDecl *CRTP,
|
|
const CXXRecordDecl *Derived) {
|
|
return llvm::any_of(CRTP->friends(), [&](const FriendDecl *Friend) {
|
|
const TypeSourceInfo *const FriendType = Friend->getFriendType();
|
|
if (!FriendType)
|
|
return false;
|
|
|
|
return declaresSameEntity(FriendType->getType()->getAsCXXRecordDecl(),
|
|
Derived);
|
|
});
|
|
}
|
|
|
|
static const NamedDecl *
|
|
getDerivedParameter(const ClassTemplateSpecializationDecl *CRTP,
|
|
const CXXRecordDecl *Derived) {
|
|
size_t Idx = 0;
|
|
const bool AnyOf = llvm::any_of(
|
|
CRTP->getTemplateArgs().asArray(), [&](const TemplateArgument &Arg) {
|
|
++Idx;
|
|
return Arg.getKind() == TemplateArgument::Type &&
|
|
declaresSameEntity(Arg.getAsType()->getAsCXXRecordDecl(),
|
|
Derived);
|
|
});
|
|
|
|
return AnyOf ? CRTP->getSpecializedTemplate()
|
|
->getTemplateParameters()
|
|
->getParam(Idx - 1)
|
|
: nullptr;
|
|
}
|
|
|
|
static std::vector<FixItHint>
|
|
hintMakeCtorPrivate(const CXXConstructorDecl *Ctor,
|
|
const std::string &OriginalAccess) {
|
|
std::vector<FixItHint> Hints;
|
|
|
|
Hints.emplace_back(FixItHint::CreateInsertion(
|
|
Ctor->getBeginLoc().getLocWithOffset(-1), "private:\n"));
|
|
|
|
const ASTContext &ASTCtx = Ctor->getASTContext();
|
|
const SourceLocation CtorEndLoc =
|
|
Ctor->isExplicitlyDefaulted()
|
|
? utils::lexer::findNextTerminator(Ctor->getEndLoc(),
|
|
ASTCtx.getSourceManager(),
|
|
ASTCtx.getLangOpts())
|
|
: Ctor->getEndLoc();
|
|
Hints.emplace_back(FixItHint::CreateInsertion(
|
|
CtorEndLoc.getLocWithOffset(1), '\n' + OriginalAccess + ':' + '\n'));
|
|
|
|
return Hints;
|
|
}
|
|
|
|
void CrtpConstructorAccessibilityCheck::registerMatchers(MatchFinder *Finder) {
|
|
Finder->addMatcher(
|
|
classTemplateSpecializationDecl(
|
|
decl().bind("crtp"),
|
|
hasAnyTemplateArgument(refersToType(recordType(hasDeclaration(
|
|
cxxRecordDecl(
|
|
isDerivedFrom(cxxRecordDecl(equalsBoundNode("crtp"))))
|
|
.bind("derived")))))),
|
|
this);
|
|
}
|
|
|
|
void CrtpConstructorAccessibilityCheck::check(
|
|
const MatchFinder::MatchResult &Result) {
|
|
const auto *CRTPInstantiation =
|
|
Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("crtp");
|
|
const auto *DerivedRecord = Result.Nodes.getNodeAs<CXXRecordDecl>("derived");
|
|
const CXXRecordDecl *CRTPDeclaration =
|
|
CRTPInstantiation->getSpecializedTemplate()->getTemplatedDecl();
|
|
|
|
if (!CRTPDeclaration->hasDefinition())
|
|
return;
|
|
|
|
const auto *DerivedTemplateParameter =
|
|
getDerivedParameter(CRTPInstantiation, DerivedRecord);
|
|
|
|
assert(DerivedTemplateParameter &&
|
|
"No template parameter corresponds to the derived class of the CRTP.");
|
|
|
|
const bool NeedsFriend =
|
|
!isDerivedParameterBefriended(CRTPDeclaration,
|
|
DerivedTemplateParameter) &&
|
|
!isDerivedClassBefriended(CRTPDeclaration, DerivedRecord);
|
|
|
|
const FixItHint HintFriend = FixItHint::CreateInsertion(
|
|
CRTPDeclaration->getBraceRange().getEnd(),
|
|
"friend " + DerivedTemplateParameter->getNameAsString() + ';' + '\n');
|
|
|
|
if (hasPrivateConstructor(CRTPDeclaration) && NeedsFriend) {
|
|
diag(CRTPDeclaration->getLocation(),
|
|
"the CRTP cannot be constructed from the derived class; consider "
|
|
"declaring the derived class as friend")
|
|
<< HintFriend;
|
|
}
|
|
|
|
auto WithFriendHintIfNeeded = [&](const DiagnosticBuilder &Diag,
|
|
bool NeedsFriend) {
|
|
if (NeedsFriend)
|
|
Diag << HintFriend;
|
|
};
|
|
|
|
if (!CRTPDeclaration->hasUserDeclaredConstructor()) {
|
|
const bool IsStruct = CRTPDeclaration->isStruct();
|
|
|
|
WithFriendHintIfNeeded(
|
|
diag(CRTPDeclaration->getLocation(),
|
|
"the implicit default constructor of the CRTP is publicly "
|
|
"accessible; consider making it private%select{| and declaring "
|
|
"the derived class as friend}0")
|
|
<< NeedsFriend
|
|
<< FixItHint::CreateInsertion(
|
|
CRTPDeclaration->getBraceRange().getBegin().getLocWithOffset(
|
|
1),
|
|
(IsStruct ? "\nprivate:\n" : "\n") +
|
|
CRTPDeclaration->getNameAsString() + "() = default;\n" +
|
|
(IsStruct ? "public:\n" : "")),
|
|
NeedsFriend);
|
|
}
|
|
|
|
for (auto &&Ctor : CRTPDeclaration->ctors()) {
|
|
if (Ctor->getAccess() == AS_private || Ctor->isDeleted())
|
|
continue;
|
|
|
|
const bool IsPublic = Ctor->getAccess() == AS_public;
|
|
const std::string Access = IsPublic ? "public" : "protected";
|
|
|
|
WithFriendHintIfNeeded(
|
|
diag(Ctor->getLocation(),
|
|
"%0 constructor allows the CRTP to be %select{inherited "
|
|
"from|constructed}1 as a regular template class; consider making "
|
|
"it private%select{| and declaring the derived class as friend}2")
|
|
<< Access << IsPublic << NeedsFriend
|
|
<< hintMakeCtorPrivate(Ctor, Access),
|
|
NeedsFriend);
|
|
}
|
|
}
|
|
|
|
bool CrtpConstructorAccessibilityCheck::isLanguageVersionSupported(
|
|
const LangOptions &LangOpts) const {
|
|
return LangOpts.CPlusPlus11;
|
|
}
|
|
} // namespace clang::tidy::bugprone
|