| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| # REQUIRES: system-linux | ||
|
|
||
| ## Check that BOLT correctly parses the Linux kernel .altinstructions section | ||
| ## and annotates alternative instructions. | ||
|
|
||
| # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o | ||
| # RUN: %clang %cflags -nostdlib %t.o -o %t.exe \ | ||
| # RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr,--no-pie | ||
| # RUN: llvm-bolt %t.exe --print-normalized --keep-nops -o %t.out \ | ||
| # RUN: --alt-inst-feature-size=2 | FileCheck %s | ||
|
|
||
| ## Older kernels used to have padlen field in alt_instr. Check compatibility. | ||
|
|
||
| # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown --defsym PADLEN=1 \ | ||
| # RUN: %s -o %t.o | ||
| # RUN: %clang %cflags -nostdlib %t.o -o %t.exe \ | ||
| # RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr,--no-pie | ||
| # RUN: llvm-bolt %t.exe --print-normalized --keep-nops --alt-inst-has-padlen \ | ||
| # RUN: -o %t.out | FileCheck %s | ||
|
|
||
| ## Check with a larger size of "feature" field in alt_instr. | ||
|
|
||
| # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown \ | ||
| # RUN: --defsym FEATURE_SIZE_4=1 %s -o %t.o | ||
| # RUN: %clang %cflags -nostdlib %t.o -o %t.exe \ | ||
| # RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr,--no-pie | ||
| # RUN: llvm-bolt %t.exe --print-normalized --keep-nops \ | ||
| # RUN: --alt-inst-feature-size=4 -o %t.out | FileCheck %s | ||
|
|
||
| ## Check that out-of-bounds read is handled properly. | ||
|
|
||
| # RUN: not llvm-bolt %t.exe --print-normalized --keep-nops \ | ||
| # RUN: --alt-inst-feature-size=2 -o %t.out | ||
|
|
||
| # CHECK: BOLT-INFO: Linux kernel binary detected | ||
| # CHECK: BOLT-INFO: parsed 2 alternative instruction entries | ||
|
|
||
| .text | ||
| .globl _start | ||
| .type _start, %function | ||
| _start: | ||
| # CHECK: Binary Function "_start" | ||
| .L0: | ||
| rdtsc | ||
| # CHECK: rdtsc | ||
| # CHECK-SAME: AltInst: 1 | ||
| # CHECK-SAME: AltInst2: 2 | ||
| nop | ||
| # CHECK-NEXT: nop | ||
| # CHECK-SAME: AltInst: 1 | ||
| # CHECK-SAME: AltInst2: 2 | ||
| nop | ||
| nop | ||
| .L1: | ||
| ret | ||
| .size _start, .-_start | ||
|
|
||
| .section .altinstr_replacement,"ax",@progbits | ||
| .A0: | ||
| lfence | ||
| rdtsc | ||
| .A1: | ||
| rdtscp | ||
| .Ae: | ||
|
|
||
| ## Alternative instruction info. | ||
| .section .altinstructions,"a",@progbits | ||
|
|
||
| .long .L0 - . # org instruction | ||
| .long .A0 - . # alt instruction | ||
| .ifdef FEATURE_SIZE_4 | ||
| .long 0x72 # feature flags | ||
| .else | ||
| .word 0x72 # feature flags | ||
| .endif | ||
| .byte .L1 - .L0 # org size | ||
| .byte .A1 - .A0 # alt size | ||
| .ifdef PADLEN | ||
| .byte 0 | ||
| .endif | ||
|
|
||
| .long .L0 - . # org instruction | ||
| .long .A1 - . # alt instruction | ||
| .ifdef FEATURE_SIZE_4 | ||
| .long 0x3b # feature flags | ||
| .else | ||
| .word 0x3b # feature flags | ||
| .endif | ||
| .byte .L1 - .L0 # org size | ||
| .byte .Ae - .A1 # alt size | ||
| .ifdef PADLEN | ||
| .byte 0 | ||
| .endif | ||
|
|
||
| ## Fake Linux Kernel sections. | ||
| .section __ksymtab,"a",@progbits | ||
| .section __ksymtab_gpl,"a",@progbits |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| # REQUIRES: system-linux | ||
|
|
||
| ## Check that BOLT correctly parses the Linux kernel __bug_table section. | ||
|
|
||
| # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o | ||
| # RUN: %clang %cflags -nostdlib %t.o -o %t.exe \ | ||
| # RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr,--no-pie | ||
|
|
||
| ## Verify bug entry bindings to instructions. | ||
|
|
||
| # RUN: llvm-bolt %t.exe --print-normalized -o %t.out | FileCheck %s | ||
|
|
||
| # CHECK: BOLT-INFO: Linux kernel binary detected | ||
| # CHECK: BOLT-INFO: parsed 2 bug table entries | ||
|
|
||
| .text | ||
| .globl _start | ||
| .type _start, %function | ||
| _start: | ||
| # CHECK: Binary Function "_start" | ||
| nop | ||
| .L0: | ||
| ud2 | ||
| # CHECK: ud2 | ||
| # CHECK-SAME: BugEntry: 1 | ||
| nop | ||
| .L1: | ||
| ud2 | ||
| # CHECK: ud2 | ||
| # CHECK-SAME: BugEntry: 2 | ||
| ret | ||
| .size _start, .-_start | ||
|
|
||
|
|
||
| ## Bug table. | ||
| .section __bug_table,"a",@progbits | ||
| 1: | ||
| .long .L0 - . # instruction | ||
| .org 1b + 12 | ||
| 2: | ||
| .long .L1 - . # instruction | ||
| .org 2b + 12 | ||
|
|
||
| ## Fake Linux Kernel sections. | ||
| .section __ksymtab,"a",@progbits | ||
| .section __ksymtab_gpl,"a",@progbits |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| # REQUIRES: system-linux | ||
|
|
||
| ## Check that BOLT correctly parses the Linux kernel exception table. | ||
|
|
||
| # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o | ||
| # RUN: %clang %cflags -nostdlib %t.o -o %t.exe \ | ||
| # RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr | ||
|
|
||
| ## Verify exception bindings to instructions. | ||
|
|
||
| # RUN: llvm-bolt %t.exe --print-normalized -o %t.out --keep-nops=0 \ | ||
| # RUN: --bolt-info=0 | FileCheck %s | ||
|
|
||
| ## Verify the bindings again on the rewritten binary with nops removed. | ||
|
|
||
| # RUN: llvm-bolt %t.out -o %t.out.1 --print-normalized | FileCheck %s | ||
|
|
||
| # CHECK: BOLT-INFO: Linux kernel binary detected | ||
| # CHECK: BOLT-INFO: parsed 2 exception table entries | ||
|
|
||
| .text | ||
| .globl _start | ||
| .type _start, %function | ||
| _start: | ||
| # CHECK: Binary Function "_start" | ||
| nop | ||
| .L0: | ||
| mov (%rdi), %rax | ||
| # CHECK: mov | ||
| # CHECK-SAME: ExceptionEntry: 1 # Fixup: [[FIXUP:[a-zA-Z0-9_]+]] | ||
| nop | ||
| .L1: | ||
| mov (%rsi), %rax | ||
| # CHECK: mov | ||
| # CHECK-SAME: ExceptionEntry: 2 # Fixup: [[FIXUP]] | ||
| nop | ||
| ret | ||
| .LF0: | ||
| # CHECK: Secondary Entry Point: [[FIXUP]] | ||
| jmp foo | ||
| .size _start, .-_start | ||
|
|
||
| .globl foo | ||
| .type foo, %function | ||
| foo: | ||
| ret | ||
| .size foo, .-foo | ||
|
|
||
|
|
||
| ## Exception table. | ||
| .section __ex_table,"a",@progbits | ||
| .align 4 | ||
|
|
||
| .long .L0 - . # instruction | ||
| .long .LF0 - . # fixup | ||
| .long 0 # data | ||
|
|
||
| .long .L1 - . # instruction | ||
| .long .LF0 - . # fixup | ||
| .long 0 # data | ||
|
|
||
| ## Fake Linux Kernel sections. | ||
| .section __ksymtab,"a",@progbits | ||
| .section __ksymtab_gpl,"a",@progbits |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| # REQUIRES: system-linux | ||
|
|
||
| ## Check that BOLT correctly parses the Linux kernel .parainstructions section. | ||
|
|
||
| # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o | ||
| # RUN: %clang %cflags -nostdlib %t.o -o %t.exe \ | ||
| # RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr,--no-pie | ||
|
|
||
| ## Verify paravirtual bindings to instructions. | ||
|
|
||
| # RUN: llvm-bolt %t.exe --print-normalized -o %t.out | FileCheck %s | ||
|
|
||
| # CHECK: BOLT-INFO: Linux kernel binary detected | ||
| # CHECK: BOLT-INFO: parsed 2 paravirtual patch sites | ||
|
|
||
| .rodata | ||
| fptr: | ||
| .quad 0 | ||
|
|
||
| .text | ||
| .globl _start | ||
| .type _start, %function | ||
| _start: | ||
| # CHECK: Binary Function "_start" | ||
| nop | ||
| .L1: | ||
| call *fptr(%rip) | ||
| # CHECK: call | ||
| # CHECK-SAME: ParaSite: 1 | ||
| nop | ||
| .L2: | ||
| call *fptr(%rip) | ||
| # CHECK: call | ||
| # CHECK-SAME: ParaSite: 2 | ||
| ret | ||
| .size _start, .-_start | ||
|
|
||
|
|
||
| ## Paravirtual patch sites. | ||
| .section .parainstructions,"a",@progbits | ||
|
|
||
| .balign 8 | ||
| .quad .L1 # instruction | ||
| .byte 1 # type | ||
| .byte 7 # length | ||
|
|
||
| .balign 8 | ||
| .quad .L2 # instruction | ||
| .byte 1 # type | ||
| .byte 7 # length | ||
|
|
||
| ## Fake Linux Kernel sections. | ||
| .section __ksymtab,"a",@progbits | ||
| .section __ksymtab_gpl,"a",@progbits |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,181 @@ | ||
| //===--- CrtpConstructorAccessibilityCheck.cpp - clang-tidy ---------------===// | ||
| // | ||
| // 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 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 && | ||
| 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."); | ||
|
|
||
| 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) -> const DiagnosticBuilder & { | ||
| if (NeedsFriend) | ||
| Diag << HintFriend; | ||
|
|
||
| return Diag; | ||
| }; | ||
|
|
||
| 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) | ||
| continue; | ||
|
|
||
| const bool IsPublic = Ctor->getAccess() == AS_public; | ||
| const std::string Access = IsPublic ? "public" : "protected"; | ||
|
|
||
| WithFriendHintIfNeeded( | ||
| diag(Ctor->getLocation(), | ||
| "%0 contructor 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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| //===--- CrtpConstructorAccessibilityCheck.h - clang-tidy -------*- 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_CRTPCONSTRUCTORACCESSIBILITYCHECK_H | ||
| #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_CRTPCONSTRUCTORACCESSIBILITYCHECK_H | ||
|
|
||
| #include "../ClangTidyCheck.h" | ||
|
|
||
| namespace clang::tidy::bugprone { | ||
|
|
||
| /// Detects error-prone Curiously Recurring Template Pattern usage, when the | ||
| /// CRTP can be constructed outside itself and the derived class. | ||
| /// | ||
| /// For the user-facing documentation see: | ||
| /// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/crtp-constructor-accessibility.html | ||
| class CrtpConstructorAccessibilityCheck : public ClangTidyCheck { | ||
| public: | ||
| CrtpConstructorAccessibilityCheck(StringRef Name, ClangTidyContext *Context) | ||
| : ClangTidyCheck(Name, Context) {} | ||
| void registerMatchers(ast_matchers::MatchFinder *Finder) override; | ||
| void check(const ast_matchers::MatchFinder::MatchResult &Result) override; | ||
| bool isLanguageVersionSupported(const LangOptions &LangOpts) const override; | ||
| }; | ||
|
|
||
| } // namespace clang::tidy::bugprone | ||
|
|
||
| #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_CRTPCONSTRUCTORACCESSIBILITYCHECK_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -77,43 +77,6 @@ class HeuristicResolver { | |
|
|
||
| private: | ||
| ASTContext &Ctx; | ||
| }; | ||
|
|
||
| } // namespace clangd | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| .. title:: clang-tidy - bugprone-crtp-constructor-accessibility | ||
|
|
||
| bugprone-crtp-constructor-accessibility | ||
| ======================================= | ||
|
|
||
| Detects error-prone Curiously Recurring Template Pattern usage, when the CRTP | ||
| can be constructed outside itself and the derived class. | ||
|
|
||
| The CRTP is an idiom, in which a class derives from a template class, where | ||
| itself is the template argument. It should be ensured that if a class is | ||
| intended to be a base class in this idiom, it can only be instantiated if | ||
| the derived class is it's template argument. | ||
|
|
||
| Example: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| template <typename T> class CRTP { | ||
| private: | ||
| CRTP() = default; | ||
| friend T; | ||
| }; | ||
|
|
||
| class Derived : CRTP<Derived> {}; | ||
|
|
||
| Below can be seen some common mistakes that will allow the breaking of the | ||
| idiom. | ||
|
|
||
| If the constructor of a class intended to be used in a CRTP is public, then | ||
| it allows users to construct that class on its own. | ||
|
|
||
| Example: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| template <typename T> class CRTP { | ||
| public: | ||
| CRTP() = default; | ||
| }; | ||
|
|
||
| class Good : CRTP<Good> {}; | ||
| Good GoodInstance; | ||
|
|
||
| CRTP<int> BadInstance; | ||
|
|
||
| If the constructor is protected, the possibility of an accidental instantiation | ||
| is prevented, however it can fade an error, when a different class is used as | ||
| the template parameter instead of the derived one. | ||
|
|
||
| Example: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| template <typename T> class CRTP { | ||
| protected: | ||
| CRTP() = default; | ||
| }; | ||
|
|
||
| class Good : CRTP<Good> {}; | ||
| Good GoodInstance; | ||
|
|
||
| class Bad : CRTP<Good> {}; | ||
| Bad BadInstance; | ||
|
|
||
| To ensure that no accidental instantiation happens, the best practice is to | ||
| make the constructor private and declare the derived class as friend. Note | ||
| that as a tradeoff, this also gives the derived class access to every other | ||
| private members of the CRTP. | ||
|
|
||
| Example: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| template <typename T> class CRTP { | ||
| CRTP() = default; | ||
| friend T; | ||
| }; | ||
|
|
||
| class Good : CRTP<Good> {}; | ||
| Good GoodInstance; | ||
|
|
||
| class Bad : CRTP<Good> {}; | ||
| Bad CompileTimeError; | ||
|
|
||
| CRTP<int> AlsoCompileTimeError; | ||
|
|
||
| Limitations: | ||
|
|
||
| * The check is not supported below C++11 | ||
|
|
||
| * The check does not handle when the derived class is passed as a variadic | ||
| template argument | ||
|
|
||
| * Accessible functions that can construct the CRTP, like factory functions | ||
| are not checked | ||
|
|
||
| The check also suggests a fix-its in some cases. | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,255 @@ | ||
| // RUN: %check_clang_tidy -std=c++11-or-later %s bugprone-crtp-constructor-accessibility %t -- -- -fno-delayed-template-parsing | ||
|
|
||
| namespace class_implicit_ctor { | ||
| template <typename T> | ||
| class CRTP {}; | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: the implicit default constructor of the CRTP is publicly accessible; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility] | ||
| // CHECK-FIXES: CRTP() = default; | ||
| // CHECK-FIXES: friend T; | ||
|
|
||
| class A : CRTP<A> {}; | ||
| } // namespace class_implicit_ctor | ||
|
|
||
| namespace class_unconstructible { | ||
| template <typename T> | ||
| class CRTP { | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: the CRTP cannot be constructed from the derived class; consider declaring the derived class as friend [bugprone-crtp-constructor-accessibility] | ||
| // CHECK-FIXES: friend T; | ||
| CRTP() = default; | ||
| }; | ||
|
|
||
| class A : CRTP<A> {}; | ||
| } // namespace class_unconstructible | ||
|
|
||
| namespace class_public_default_ctor { | ||
| template <typename T> | ||
| class CRTP { | ||
| public: | ||
| CRTP() = default; | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: public contructor allows the CRTP to be constructed as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility] | ||
| // CHECK-FIXES: private:{{[[:space:]]*}}CRTP() = default;{{[[:space:]]*}}public: | ||
| // CHECK-FIXES: friend T; | ||
| }; | ||
|
|
||
| class A : CRTP<A> {}; | ||
| } // namespace class_public_default_ctor | ||
|
|
||
| namespace class_public_user_provided_ctor { | ||
| template <typename T> | ||
| class CRTP { | ||
| public: | ||
| CRTP(int) {} | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: public contructor allows the CRTP to be constructed as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility] | ||
| // CHECK-FIXES: private:{{[[:space:]]*}}CRTP(int) {}{{[[:space:]]*}}public: | ||
| // CHECK-FIXES: friend T; | ||
| }; | ||
|
|
||
| class A : CRTP<A> {}; | ||
| } // namespace class_public_user_provided_ctor | ||
|
|
||
| namespace class_public_multiple_user_provided_ctors { | ||
| template <typename T> | ||
| class CRTP { | ||
| public: | ||
| CRTP(int) {} | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: public contructor allows the CRTP to be constructed as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility] | ||
| // CHECK-FIXES: private:{{[[:space:]]*}}CRTP(int) {}{{[[:space:]]*}}public: | ||
| CRTP(float) {} | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: public contructor allows the CRTP to be constructed as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility] | ||
| // CHECK-FIXES: private:{{[[:space:]]*}}CRTP(float) {}{{[[:space:]]*}}public: | ||
|
|
||
| // CHECK-FIXES: friend T; | ||
| // CHECK-FIXES: friend T; | ||
| }; | ||
|
|
||
| class A : CRTP<A> {}; | ||
| } // namespace class_public_multiple_user_provided_ctors | ||
|
|
||
| namespace class_protected_ctors { | ||
| template <typename T> | ||
| class CRTP { | ||
| protected: | ||
| CRTP(int) {} | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: protected contructor allows the CRTP to be inherited from as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility] | ||
| // CHECK-FIXES: private:{{[[:space:]]*}}CRTP(int) {}{{[[:space:]]*}}protected: | ||
| CRTP() = default; | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: protected contructor allows the CRTP to be inherited from as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility] | ||
| // CHECK-FIXES: private:{{[[:space:]]*}}CRTP() = default;{{[[:space:]]*}}protected: | ||
| CRTP(float) {} | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: protected contructor allows the CRTP to be inherited from as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility] | ||
| // CHECK-FIXES: private:{{[[:space:]]*}}CRTP(float) {}{{[[:space:]]*}}protected: | ||
|
|
||
| // CHECK-FIXES: friend T; | ||
| // CHECK-FIXES: friend T; | ||
| // CHECK-FIXES: friend T; | ||
| }; | ||
|
|
||
| class A : CRTP<A> {}; | ||
| } // namespace class_protected_ctors | ||
|
|
||
| namespace struct_implicit_ctor { | ||
| template <typename T> | ||
| struct CRTP {}; | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: the implicit default constructor of the CRTP is publicly accessible; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility] | ||
| // CHECK-FIXES: private:{{[[:space:]]*}}CRTP() = default;{{[[:space:]]*}}public: | ||
| // CHECK-FIXES: friend T; | ||
|
|
||
| class A : CRTP<A> {}; | ||
| } // namespace struct_implicit_ctor | ||
|
|
||
| namespace struct_default_ctor { | ||
| template <typename T> | ||
| struct CRTP { | ||
| CRTP() = default; | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: public contructor allows the CRTP to be constructed as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility] | ||
| // CHECK-FIXES: private:{{[[:space:]]*}}CRTP() = default;{{[[:space:]]*}}public: | ||
| // CHECK-FIXES: friend T; | ||
| }; | ||
|
|
||
| class A : CRTP<A> {}; | ||
| } // namespace struct_default_ctor | ||
|
|
||
| namespace same_class_multiple_crtps { | ||
| template <typename T> | ||
| struct CRTP {}; | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: the implicit default constructor of the CRTP is publicly accessible; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility] | ||
| // CHECK-FIXES: private:{{[[:space:]]*}}CRTP() = default;{{[[:space:]]*}}public: | ||
| // CHECK-FIXES: friend T; | ||
|
|
||
| template <typename T> | ||
| struct CRTP2 {}; | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: the implicit default constructor of the CRTP is publicly accessible; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility] | ||
| // CHECK-FIXES: private:{{[[:space:]]*}}CRTP2() = default;{{[[:space:]]*}}public: | ||
| // CHECK-FIXES: friend T; | ||
|
|
||
| class A : CRTP<A>, CRTP2<A> {}; | ||
| } // namespace same_class_multiple_crtps | ||
|
|
||
| namespace same_crtp_multiple_classes { | ||
| template <typename T> | ||
| class CRTP { | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: the CRTP cannot be constructed from the derived class; consider declaring the derived class as friend [bugprone-crtp-constructor-accessibility] | ||
| // CHECK-FIXES: friend T; | ||
| CRTP() = default; | ||
| }; | ||
|
|
||
| class A : CRTP<A> {}; | ||
| class B : CRTP<B> {}; | ||
| } // namespace same_crtp_multiple_classes | ||
|
|
||
| namespace crtp_template { | ||
| template <typename T, typename U> | ||
| class CRTP { | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: the CRTP cannot be constructed from the derived class; consider declaring the derived class as friend [bugprone-crtp-constructor-accessibility] | ||
| // CHECK-FIXES: friend U; | ||
| CRTP() = default; | ||
| }; | ||
|
|
||
| class A : CRTP<int, A> {}; | ||
| } // namespace crtp_template | ||
|
|
||
| namespace crtp_template2 { | ||
| template <typename T, typename U> | ||
| class CRTP { | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: the CRTP cannot be constructed from the derived class; consider declaring the derived class as friend [bugprone-crtp-constructor-accessibility] | ||
| // CHECK-FIXES: friend T; | ||
| CRTP() = default; | ||
| }; | ||
|
|
||
| class A : CRTP<A, A> {}; | ||
| } // namespace crtp_template2 | ||
|
|
||
| namespace template_derived { | ||
| template <typename T> | ||
| class CRTP {}; | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: the implicit default constructor of the CRTP is publicly accessible; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility] | ||
| // CHECK-FIXES: CRTP() = default; | ||
| // CHECK-FIXES: friend T; | ||
|
|
||
| template<typename T> | ||
| class A : CRTP<A<T>> {}; | ||
|
|
||
| // FIXME: Ideally the warning should be triggered without instantiation. | ||
| void foo() { | ||
| A<int> A; | ||
| (void) A; | ||
| } | ||
| } // namespace template_derived | ||
|
|
||
| namespace template_derived_explicit_specialization { | ||
| template <typename T> | ||
| class CRTP {}; | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: the implicit default constructor of the CRTP is publicly accessible; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility] | ||
| // CHECK-FIXES: CRTP() = default; | ||
| // CHECK-FIXES: friend T; | ||
|
|
||
| template<typename T> | ||
| class A : CRTP<A<T>> {}; | ||
|
|
||
| template<> | ||
| class A<int> : CRTP<A<int>> {}; | ||
| } // namespace template_derived_explicit_specialization | ||
|
|
||
| namespace explicit_derived_friend { | ||
| class A; | ||
|
|
||
| template <typename T> | ||
| class CRTP { | ||
| CRTP() = default; | ||
| friend A; | ||
| }; | ||
|
|
||
| class A : CRTP<A> {}; | ||
| } // namespace explicit_derived_friend | ||
|
|
||
| namespace explicit_derived_friend_multiple { | ||
| class A; | ||
|
|
||
| template <typename T> | ||
| class CRTP { | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: the CRTP cannot be constructed from the derived class; consider declaring the derived class as friend [bugprone-crtp-constructor-accessibility] | ||
| // CHECK-FIXES: friend T; | ||
| CRTP() = default; | ||
| friend A; | ||
| }; | ||
|
|
||
| class A : CRTP<A> {}; | ||
| class B : CRTP<B> {}; | ||
| } // namespace explicit_derived_friend_multiple | ||
|
|
||
| namespace no_need_for_friend { | ||
| class A; | ||
|
|
||
| template <typename T> | ||
| class CRTP { | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: the implicit default constructor of the CRTP is publicly accessible; consider making it private [bugprone-crtp-constructor-accessibility] | ||
| // CHECK-FIXES: CRTP() = default; | ||
| friend A; | ||
| }; | ||
|
|
||
| class A : CRTP<A> {}; | ||
| } // namespace no_need_for_friend | ||
|
|
||
| namespace no_warning { | ||
| template <typename T> | ||
| class CRTP | ||
| { | ||
| CRTP() = default; | ||
| friend T; | ||
| }; | ||
|
|
||
| class A : CRTP<A> {}; | ||
| } // namespace no_warning | ||
|
|
||
| namespace no_warning_unsupported { | ||
| template<typename... Types> | ||
| class CRTP | ||
| {}; | ||
|
|
||
| class A : CRTP<A> {}; | ||
|
|
||
| void foo() { | ||
| A A; | ||
| (void) A; | ||
| } | ||
| } // namespace no_warning_unsupported |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| Checks: readability-identifier-naming | ||
| CheckOptions: | ||
| readability-identifier-naming.AbstractClassCase: CamelCase | ||
| readability-identifier-naming.StructCase: CamelCase | ||
| readability-identifier-naming.UnionCase: camelBack | ||
| readability-identifier-naming.ClassCase: CamelCase | ||
| readability-identifier-naming.ClassConstantCase: CamelCase | ||
| readability-identifier-naming.ClassMemberCase: CamelCase | ||
| readability-identifier-naming.ConstantCase: CamelCase | ||
| readability-identifier-naming.ConstantMemberCase: CamelCase | ||
| readability-identifier-naming.ConstantParameterCase: CamelCase | ||
| readability-identifier-naming.ConstantPointerParameterCase: CamelCase | ||
| readability-identifier-naming.ConstexprVariableCase: CamelCase | ||
| readability-identifier-naming.EnumConstantCase: CamelCase | ||
| readability-identifier-naming.GlobalConstantCase: CamelCase | ||
| readability-identifier-naming.GlobalConstantPointerCase: CamelCase | ||
| readability-identifier-naming.GlobalPointerCase: CamelCase | ||
| readability-identifier-naming.GlobalVariableCase: CamelCase | ||
| readability-identifier-naming.LocalConstantCase: CamelCase | ||
| readability-identifier-naming.LocalConstantPointerCase: CamelCase | ||
| readability-identifier-naming.LocalPointerCase: CamelCase | ||
| readability-identifier-naming.LocalVariableCase: CamelCase | ||
| readability-identifier-naming.MemberCase: CamelCase | ||
| readability-identifier-naming.ParameterCase: CamelCase | ||
| readability-identifier-naming.PointerParameterCase: CamelCase | ||
| readability-identifier-naming.PrivateMemberCase: CamelCase | ||
| readability-identifier-naming.ProtectedMemberCase: CamelCase | ||
| readability-identifier-naming.PublicMemberCase: CamelCase | ||
| readability-identifier-naming.ScopedEnumConstantCase: CamelCase | ||
| readability-identifier-naming.StaticConstantCase: CamelCase | ||
| readability-identifier-naming.StaticVariableCase: CamelCase | ||
| readability-identifier-naming.VariableCase: CamelCase | ||
| readability-identifier-naming.AbstractClassHungarianPrefix: LowerCase | ||
| readability-identifier-naming.ClassHungarianPrefix: LowerCase | ||
| readability-identifier-naming.ClassConstantHungarianPrefix: LowerCase | ||
| readability-identifier-naming.ClassMemberHungarianPrefix: LowerCase | ||
| readability-identifier-naming.ConstantHungarianPrefix: LowerCase | ||
| readability-identifier-naming.ConstantMemberHungarianPrefix: LowerCase | ||
| readability-identifier-naming.ConstantParameterHungarianPrefix: LowerCase | ||
| readability-identifier-naming.ConstantPointerParameterHungarianPrefix: LowerCase | ||
| readability-identifier-naming.ConstexprVariableHungarianPrefix: LowerCase | ||
| readability-identifier-naming.EnumConstantHungarianPrefix: LowerCase | ||
| readability-identifier-naming.GlobalConstantHungarianPrefix: LowerCase | ||
| readability-identifier-naming.GlobalConstantPointerHungarianPrefix: LowerCase | ||
| readability-identifier-naming.GlobalPointerHungarianPrefix: LowerCase | ||
| readability-identifier-naming.GlobalVariableHungarianPrefix: LowerCase | ||
| readability-identifier-naming.LocalConstantHungarianPrefix: LowerCase | ||
| readability-identifier-naming.LocalConstantPointerHungarianPrefix: LowerCase | ||
| readability-identifier-naming.LocalPointerHungarianPrefix: LowerCase | ||
| readability-identifier-naming.LocalVariableHungarianPrefix: LowerCase | ||
| readability-identifier-naming.MemberHungarianPrefix: LowerCase | ||
| readability-identifier-naming.ParameterHungarianPrefix: LowerCase | ||
| readability-identifier-naming.PointerParameterHungarianPrefix: LowerCase | ||
| readability-identifier-naming.PrivateMemberHungarianPrefix: LowerCase | ||
| readability-identifier-naming.ProtectedMemberHungarianPrefix: LowerCase | ||
| readability-identifier-naming.PublicMemberHungarianPrefix: LowerCase | ||
| readability-identifier-naming.ScopedEnumConstantHungarianPrefix: LowerCase | ||
| readability-identifier-naming.StaticConstantHungarianPrefix: LowerCase | ||
| readability-identifier-naming.StaticVariableHungarianPrefix: LowerCase | ||
| readability-identifier-naming.VariableHungarianPrefix: LowerCase |