diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp index a8a23b045f80b..e518a64abc52e 100644 --- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -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 @@ class BugproneModule : public ClangTidyModule { "bugprone-unhandled-exception-at-new"); CheckFactories.registerCheck( "bugprone-unique-ptr-array-mismatch"); + CheckFactories.registerCheck( + "bugprone-crtp-constructor-accessibility"); CheckFactories.registerCheck( "bugprone-unsafe-functions"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt index 1cd6fb207d762..638fba03a4358 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt @@ -79,6 +79,7 @@ add_clang_library(clangTidyBugproneModule UnhandledExceptionAtNewCheck.cpp UnhandledSelfAssignmentCheck.cpp UniquePtrArrayMismatchCheck.cpp + CrtpConstructorAccessibilityCheck.cpp UnsafeFunctionsCheck.cpp UnusedLocalNonTrivialVariableCheck.cpp UnusedRaiiCheck.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/CrtpConstructorAccessibilityCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/CrtpConstructorAccessibilityCheck.cpp new file mode 100644 index 0000000000000..6175fcdfd229c --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/CrtpConstructorAccessibilityCheck.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(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 +hintMakeCtorPrivate(const CXXConstructorDecl *Ctor, + const std::string &OriginalAccess) { + std::vector 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("crtp"); + const auto *DerivedRecord = Result.Nodes.getNodeAs("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 diff --git a/clang-tools-extra/clang-tidy/bugprone/CrtpConstructorAccessibilityCheck.h b/clang-tools-extra/clang-tidy/bugprone/CrtpConstructorAccessibilityCheck.h new file mode 100644 index 0000000000000..785116218f468 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/CrtpConstructorAccessibilityCheck.h @@ -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 diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 0d2467210fc66..20af1d0232edd 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -104,6 +104,12 @@ Improvements to clang-tidy New checks ^^^^^^^^^^ +- New :doc:`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 ` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/crtp-constructor-accessibility.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/crtp-constructor-accessibility.rst new file mode 100644 index 0000000000000..afd88764b5967 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/crtp-constructor-accessibility.rst @@ -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 class CRTP { + private: + CRTP() = default; + friend T; + }; + + class Derived : CRTP {}; + +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 class CRTP { + public: + CRTP() = default; + }; + + class Good : CRTP {}; + Good GoodInstance; + + CRTP 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 class CRTP { + protected: + CRTP() = default; + }; + + class Good : CRTP {}; + Good GoodInstance; + + class Bad : CRTP {}; + 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 class CRTP { + CRTP() = default; + friend T; + }; + + class Good : CRTP {}; + Good GoodInstance; + + class Bad : CRTP {}; + Bad CompileTimeError; + + CRTP 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. + diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 5e57bc0ee483f..d03e7af688f00 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -86,6 +86,7 @@ Clang-Tidy Checks :doc:`bugprone-chained-comparison `, :doc:`bugprone-compare-pointer-to-member-virtual-function `, :doc:`bugprone-copy-constructor-init `, "Yes" + :doc:`bugprone-crtp-constructor-accessibility `, "Yes" :doc:`bugprone-dangling-handle `, :doc:`bugprone-dynamic-static-initializers `, :doc:`bugprone-easily-swappable-parameters `, diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/crtp-constructor-accessibility.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/crtp-constructor-accessibility.cpp new file mode 100644 index 0000000000000..cb41923df157c --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/crtp-constructor-accessibility.cpp @@ -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 +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 {}; +} // namespace class_implicit_ctor + +namespace class_unconstructible { +template +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 {}; +} // namespace class_unconstructible + +namespace class_public_default_ctor { +template +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 {}; +} // namespace class_public_default_ctor + +namespace class_public_user_provided_ctor { +template +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 {}; +} // namespace class_public_user_provided_ctor + +namespace class_public_multiple_user_provided_ctors { +template +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 {}; +} // namespace class_public_multiple_user_provided_ctors + +namespace class_protected_ctors { +template +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 {}; +} // namespace class_protected_ctors + +namespace struct_implicit_ctor { +template +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 {}; +} // namespace struct_implicit_ctor + +namespace struct_default_ctor { +template +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 {}; +} // namespace struct_default_ctor + +namespace same_class_multiple_crtps { +template +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 +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, CRTP2 {}; +} // namespace same_class_multiple_crtps + +namespace same_crtp_multiple_classes { +template +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 {}; +class B : CRTP {}; +} // namespace same_crtp_multiple_classes + +namespace crtp_template { +template +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 {}; +} // namespace crtp_template + +namespace crtp_template2 { +template +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 {}; +} // namespace crtp_template2 + +namespace template_derived { +template +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 +class A : CRTP> {}; + +// FIXME: Ideally the warning should be triggered without instantiation. +void foo() { + A A; + (void) A; +} +} // namespace template_derived + +namespace template_derived_explicit_specialization { +template +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 +class A : CRTP> {}; + +template<> +class A : CRTP> {}; +} // namespace template_derived_explicit_specialization + +namespace explicit_derived_friend { +class A; + +template +class CRTP { + CRTP() = default; + friend A; +}; + +class A : CRTP {}; +} // namespace explicit_derived_friend + +namespace explicit_derived_friend_multiple { +class A; + +template +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 {}; +class B : CRTP {}; +} // namespace explicit_derived_friend_multiple + +namespace no_need_for_friend { +class A; + +template +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 {}; +} // namespace no_need_for_friend + +namespace no_warning { +template +class CRTP +{ + CRTP() = default; + friend T; +}; + +class A : CRTP {}; +} // namespace no_warning + +namespace no_warning_unsupported { +template +class CRTP +{}; + +class A : CRTP {}; + +void foo() { + A A; + (void) A; +} +} // namespace no_warning_unsupported