[clang-tidy] CRTP Constructor Accessibility Check (#82403)
Detects error-prone Curiously Recurring Template Pattern usage, when the CRTP can be constructed outside itself and the derived class.
This commit is contained in:
parent
05390df497
commit
8e56fb824a
@ -20,6 +20,7 @@
|
||||
#include "ChainedComparisonCheck.h"
|
||||
#include "ComparePointerToMemberVirtualFunctionCheck.h"
|
||||
#include "CopyConstructorInitCheck.h"
|
||||
#include "CrtpConstructorAccessibilityCheck.h"
|
||||
#include "DanglingHandleCheck.h"
|
||||
#include "DynamicStaticInitializersCheck.h"
|
||||
#include "EasilySwappableParametersCheck.h"
|
||||
@ -237,6 +238,8 @@ public:
|
||||
"bugprone-unhandled-exception-at-new");
|
||||
CheckFactories.registerCheck<UniquePtrArrayMismatchCheck>(
|
||||
"bugprone-unique-ptr-array-mismatch");
|
||||
CheckFactories.registerCheck<CrtpConstructorAccessibilityCheck>(
|
||||
"bugprone-crtp-constructor-accessibility");
|
||||
CheckFactories.registerCheck<UnsafeFunctionsCheck>(
|
||||
"bugprone-unsafe-functions");
|
||||
CheckFactories.registerCheck<UnusedLocalNonTrivialVariableCheck>(
|
||||
|
@ -79,6 +79,7 @@ add_clang_library(clangTidyBugproneModule
|
||||
UnhandledExceptionAtNewCheck.cpp
|
||||
UnhandledSelfAssignmentCheck.cpp
|
||||
UniquePtrArrayMismatchCheck.cpp
|
||||
CrtpConstructorAccessibilityCheck.cpp
|
||||
UnsafeFunctionsCheck.cpp
|
||||
UnusedLocalNonTrivialVariableCheck.cpp
|
||||
UnusedRaiiCheck.cpp
|
||||
|
@ -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
|
@ -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
|
@ -104,6 +104,12 @@ Improvements to clang-tidy
|
||||
New checks
|
||||
^^^^^^^^^^
|
||||
|
||||
- New :doc:`bugprone-crtp-constructor-accessibility
|
||||
<clang-tidy/checks/bugprone/crtp-constructor-accessibility>` check.
|
||||
|
||||
Detects error-prone Curiously Recurring Template Pattern usage, when the CRTP
|
||||
can be constructed outside itself and the derived class.
|
||||
|
||||
- New :doc:`modernize-use-designated-initializers
|
||||
<clang-tidy/checks/modernize/use-designated-initializers>` check.
|
||||
|
||||
|
@ -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.
|
||||
|
@ -86,6 +86,7 @@ Clang-Tidy Checks
|
||||
:doc:`bugprone-chained-comparison <bugprone/chained-comparison>`,
|
||||
:doc:`bugprone-compare-pointer-to-member-virtual-function <bugprone/compare-pointer-to-member-virtual-function>`,
|
||||
:doc:`bugprone-copy-constructor-init <bugprone/copy-constructor-init>`, "Yes"
|
||||
:doc:`bugprone-crtp-constructor-accessibility <bugprone/crtp-constructor-accessibility>`, "Yes"
|
||||
:doc:`bugprone-dangling-handle <bugprone/dangling-handle>`,
|
||||
:doc:`bugprone-dynamic-static-initializers <bugprone/dynamic-static-initializers>`,
|
||||
:doc:`bugprone-easily-swappable-parameters <bugprone/easily-swappable-parameters>`,
|
||||
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user