llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-tools-extra Author: None (isuckatcs) <details> <summary>Changes</summary> Finds CRTP used in an error-prone way. 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. ```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. ```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. ``` 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; ``` --- Patch is 20.90 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/82403.diff 8 Files Affected: - (modified) clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp (+3) - (modified) clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt (+1) - (added) clang-tools-extra/clang-tidy/bugprone/UnsafeCrtpCheck.cpp (+167) - (added) clang-tools-extra/clang-tidy/bugprone/UnsafeCrtpCheck.h (+30) - (modified) clang-tools-extra/docs/ReleaseNotes.rst (+5) - (added) clang-tools-extra/docs/clang-tidy/checks/bugprone/unsafe-crtp.rst (+62) - (modified) clang-tools-extra/docs/clang-tidy/checks/list.rst (+1) - (added) clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-crtp.cpp (+232) ``````````diff diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp index 7a910037368c83..50b791ae10faf0 100644 --- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -82,6 +82,7 @@ #include "UnhandledExceptionAtNewCheck.h" #include "UnhandledSelfAssignmentCheck.h" #include "UniquePtrArrayMismatchCheck.h" +#include "UnsafeCrtpCheck.h" #include "UnsafeFunctionsCheck.h" #include "UnusedRaiiCheck.h" #include "UnusedReturnValueCheck.h" @@ -233,6 +234,8 @@ class BugproneModule : public ClangTidyModule { "bugprone-unhandled-exception-at-new"); CheckFactories.registerCheck<UniquePtrArrayMismatchCheck>( "bugprone-unique-ptr-array-mismatch"); + CheckFactories.registerCheck<UnsafeCrtpCheck>( + "bugprone-unsafe-crtp"); CheckFactories.registerCheck<UnsafeFunctionsCheck>( "bugprone-unsafe-functions"); CheckFactories.registerCheck<UnusedRaiiCheck>("bugprone-unused-raii"); diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt index d443fd8d1452f1..572ff622b47473 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt @@ -78,6 +78,7 @@ add_clang_library(clangTidyBugproneModule UnhandledExceptionAtNewCheck.cpp UnhandledSelfAssignmentCheck.cpp UniquePtrArrayMismatchCheck.cpp + UnsafeCrtpCheck.cpp UnsafeFunctionsCheck.cpp UnusedRaiiCheck.cpp UnusedReturnValueCheck.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/UnsafeCrtpCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UnsafeCrtpCheck.cpp new file mode 100644 index 00000000000000..7bb50aaf950cbe --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/UnsafeCrtpCheck.cpp @@ -0,0 +1,167 @@ +//===--- UnsafeCrtpCheck.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 "UnsafeCrtpCheck.h" +#include "../utils/LexerUtils.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::bugprone { + +namespace { +// Finds a node if it's already a bound node. +AST_MATCHER_P(CXXRecordDecl, isBoundNode, std::string, ID) { + return Builder->removeBindings( + [&](const ast_matchers::internal::BoundNodesMap &Nodes) { + const auto *BoundRecord = Nodes.getNodeAs<CXXRecordDecl>(ID); + return BoundRecord != &Node; + }); +} + +bool hasPrivateConstructor(const CXXRecordDecl *RD) { + for (auto &&Ctor : RD->ctors()) { + if (Ctor->getAccess() == AS_private) + return true; + } + + return false; +} + +bool isDerivedParameterBefriended(const CXXRecordDecl *CRTP, + const NamedDecl *Param) { + for (auto &&Friend : CRTP->friends()) { + const auto *TTPT = + dyn_cast<TemplateTypeParmType>(Friend->getFriendType()->getType()); + + if (TTPT && TTPT->getDecl() == Param) + return true; + } + + return false; +} + +bool isDerivedClassBefriended(const CXXRecordDecl *CRTP, + const CXXRecordDecl *Derived) { + for (auto &&Friend : CRTP->friends()) { + if (Friend->getFriendType()->getType()->getAsCXXRecordDecl() == Derived) + return true; + } + + return false; +} + +std::optional<const NamedDecl *> +getDerivedParameter(const ClassTemplateSpecializationDecl *CRTP, + const CXXRecordDecl *Derived) { + size_t Idx = 0; + bool Found = false; + for (auto &&TemplateArg : CRTP->getTemplateArgs().asArray()) { + if (TemplateArg.getKind() == TemplateArgument::Type && + TemplateArg.getAsType()->getAsCXXRecordDecl() == Derived) { + Found = true; + break; + } + ++Idx; + } + + if (!Found) + return std::nullopt; + + return CRTP->getSpecializedTemplate()->getTemplateParameters()->getParam(Idx); +} + +std::vector<FixItHint> hintMakeCtorPrivate(const CXXConstructorDecl *Ctor, + const std::string &OriginalAccess, + const SourceManager &SM, + const LangOptions &LangOpts) { + std::vector<FixItHint> Hints; + + Hints.emplace_back(FixItHint::CreateInsertion( + Ctor->getBeginLoc().getLocWithOffset(-1), "private:\n")); + + SourceLocation CtorEndLoc = + Ctor->isExplicitlyDefaulted() + ? utils::lexer::findNextTerminator(Ctor->getEndLoc(), SM, LangOpts) + : Ctor->getEndLoc(); + Hints.emplace_back(FixItHint::CreateInsertion( + CtorEndLoc.getLocWithOffset(1), '\n' + OriginalAccess + ':' + '\n')); + + return Hints; +} +} // namespace + +void UnsafeCrtpCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + classTemplateSpecializationDecl( + decl().bind("crtp"), + hasAnyTemplateArgument(refersToType(recordType(hasDeclaration( + cxxRecordDecl(isDerivedFrom(cxxRecordDecl(isBoundNode("crtp")))) + .bind("derived")))))), + this); +} + +void UnsafeCrtpCheck::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->hasUserDeclaredConstructor()) { + bool IsStruct = CRTPDeclaration->isStruct(); + + diag(CRTPDeclaration->getLocation(), + "the implicit default constructor of the CRTP is publicly accessible") + << CRTPDeclaration + << FixItHint::CreateInsertion( + CRTPDeclaration->getBraceRange().getBegin().getLocWithOffset(1), + (IsStruct ? "\nprivate:\n" : "\n") + + CRTPDeclaration->getNameAsString() + "() = default;\n" + + (IsStruct ? "public:\n" : "")); + diag(CRTPDeclaration->getLocation(), "consider making it private", + DiagnosticIDs::Note); + } + + const auto *DerivedTemplateParameter = + *getDerivedParameter(CRTPInstantiation, DerivedRecord); + + if (hasPrivateConstructor(CRTPDeclaration) && + !isDerivedParameterBefriended(CRTPDeclaration, + DerivedTemplateParameter) && + !isDerivedClassBefriended(CRTPDeclaration, DerivedRecord)) { + diag(CRTPDeclaration->getLocation(), + "the CRTP cannot be constructed from the derived class") + << CRTPDeclaration + << FixItHint::CreateInsertion( + CRTPDeclaration->getBraceRange().getEnd(), + "friend " + DerivedTemplateParameter->getNameAsString() + ';' + + '\n'); + diag(CRTPDeclaration->getLocation(), + "consider declaring the derived class as friend", DiagnosticIDs::Note); + } + + for (auto &&Ctor : CRTPDeclaration->ctors()) { + if (Ctor->getAccess() == AS_private) + continue; + + bool IsPublic = Ctor->getAccess() == AS_public; + std::string Access = IsPublic ? "public" : "protected"; + + diag(Ctor->getLocation(), + "%0 contructor allows the CRTP to be %select{inherited " + "from|constructed}1 as a regular template class") + << Access << IsPublic << Ctor + << hintMakeCtorPrivate(Ctor, Access, *Result.SourceManager, + getLangOpts()); + diag(Ctor->getLocation(), "consider making it private", + DiagnosticIDs::Note); + } +} + +} // namespace clang::tidy::bugprone diff --git a/clang-tools-extra/clang-tidy/bugprone/UnsafeCrtpCheck.h b/clang-tools-extra/clang-tidy/bugprone/UnsafeCrtpCheck.h new file mode 100644 index 00000000000000..ac1a8f09208f95 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/UnsafeCrtpCheck.h @@ -0,0 +1,30 @@ +//===--- UnsafeCrtpCheck.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_UNSAFECRTPCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNSAFECRTPCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::bugprone { + +/// Finds CRTP used in an error-prone way. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/unsafe-crtp.html +class UnsafeCrtpCheck : public ClangTidyCheck { +public: + UnsafeCrtpCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace clang::tidy::bugprone + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNSAFECRTPCHECK_H diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index b5348384e965ab..1506e4631806a4 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -167,6 +167,11 @@ New checks extracted from an optional-like type and then used to create a new instance of the same optional-like type. +- New :doc:`bugprone-unsafe-crtp + <clang-tidy/checks/bugprone/unsafe-crtp>` check. + + Detects error-prone CRTP usage. + - New :doc:`cppcoreguidelines-no-suspend-with-lock <clang-tidy/checks/cppcoreguidelines/no-suspend-with-lock>` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/unsafe-crtp.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/unsafe-crtp.rst new file mode 100644 index 00000000000000..8e6955999512a6 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/unsafe-crtp.rst @@ -0,0 +1,62 @@ +.. title:: clang-tidy - bugprone-unsafe-crtp + +bugprone-unsafe-crtp +==================== + +Finds CRTP used in an error-prone way. + +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. + +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; diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 6f987ba1672e3f..9083b6d28f1400 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -148,6 +148,7 @@ Clang-Tidy Checks :doc:`bugprone-unhandled-exception-at-new <bugprone/unhandled-exception-at-new>`, :doc:`bugprone-unhandled-self-assignment <bugprone/unhandled-self-assignment>`, :doc:`bugprone-unique-ptr-array-mismatch <bugprone/unique-ptr-array-mismatch>`, "Yes" + :doc:`bugprone-unsafe-crtp <bugprone/unsafe-crtp>`, "Yes" :doc:`bugprone-unsafe-functions <bugprone/unsafe-functions>`, :doc:`bugprone-unused-raii <bugprone/unused-raii>`, "Yes" :doc:`bugprone-unused-return-value <bugprone/unused-return-value>`, diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-crtp.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-crtp.cpp new file mode 100644 index 00000000000000..edcfd67677fd7b --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-crtp.cpp @@ -0,0 +1,232 @@ +// RUN: %check_clang_tidy %s bugprone-unsafe-crtp %t + +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 [bugprone-unsafe-crtp] +// CHECK-MESSAGES: :[[@LINE-2]]:7: note: consider making it private +// CHECK-FIXES: CRTP() = default; + +class A : CRTP<A> {}; +} // namespace class_implicit_ctor + +namespace class_uncostructible { +template <typename T> +class CRTP { +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: the CRTP cannot be constructed from the derived class [bugprone-unsafe-crtp] +// CHECK-MESSAGES: :[[@LINE-2]]:7: note: consider declaring the derived class as friend +// CHECK-FIXES: friend T; + CRTP() = default; +}; + +class A : CRTP<A> {}; +} // namespace class_uncostructible + +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 [bugprone-unsafe-crtp] + // CHECK-MESSAGES: :[[@LINE-2]]:5: note: consider making it private + // CHECK-FIXES: private:{{[[:space:]]*}}CRTP() = default;{{[[:space:]]*}}public: +}; + +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 [bugprone-unsafe-crtp] + // CHECK-MESSAGES: :[[@LINE-2]]:5: note: consider making it private + // CHECK-FIXES: private:{{[[:space:]]*}}CRTP(int) {}{{[[:space:]]*}}public: +}; + +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 [bugprone-unsafe-crtp] + // CHECK-MESSAGES: :[[@LINE-2]]:5: note: consider making it private + // 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 [bugprone-unsafe-crtp] + // CHECK-MESSAGES: :[[@LINE-2]]:5: note: consider making it private + // CHECK-FIXES: private:{{[[:space:]]*}}CRTP(float) {}{{[[:space:]]*}}public: +}; + +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 [bugprone-unsafe-crtp] + // CHECK-MESSAGES: :[[@LINE-2]]:5: note: consider making it private + // 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 [bugprone-unsafe-crtp] + // CHECK-MESSAGES: :[[@LINE-2]]:5: note: consider making it private + // 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 [bugprone-unsafe-crtp] + // CHECK-MESSAGES: :[[@LINE-2]]:5: note: consider making it private + // CHECK-FIXES: private:{{[[:space:]]*}}CRTP(float) {}{{[[:space:]]*}}protected: +}; + +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 [bugprone-unsafe-crtp] +// CHECK-MESSAGES: :[[@LINE-2]]:8: note: consider making it private +// CHECK-FIXES: private:{{[[:space:]]*}}CRTP() = default;{{[[:space:]]*}}public: + +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 [bugprone-unsafe-crtp] + // CHECK-MESSAGES: :[[@LINE-2]]:5: note: consider making it private + // CHECK-FIXES: private:{{[[:space:]]*}}CRTP() = default;{{[[:space:]]*}}public: +}; + +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 [bugprone-unsafe-crtp] +// CHECK-MESSAGES: :[[@LINE-2]]:8: note: consider making it private +// CHECK-FIXES: private:{{[[:space:]]*}}CRTP() = default;{{[[:space:]]*}}public: + +template <typename T> +struct CRTP2 {}; +// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: the implicit default constructor of the CRTP is publicly accessible [bugprone-unsafe-crtp] +// CHECK-MESSAGES: :[[@LINE-2]]:8: note: consider making it private +// CHECK-FIXES: private:{{[[:space:]]*}}CRTP2() = default;{{[[:space:]]*}}public: + +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 [bugprone-unsafe-crtp] +// CHECK-MESSAGES: :[[@LINE-2]]:7: note: consider declaring the derived class as friend +// 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 [bugprone-unsafe-crtp] +// CHECK-MESSAGES: :[[@LINE-2]]:7: note: consider declaring the derived class as friend +// 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 [bugprone-unsafe-crtp] +// CHECK-MESSAGES: :[[@LINE-2]]:7: note: consider declaring the derived class as friend +// 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 [bugprone-unsafe-crtp] +// CHECK-MESSAGES: :[[@LINE-2]]:7: note: consider making it private +// CHECK-FIXES: CRTP() = default; + +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 [bugprone-unsafe-crtp] +// CHECK-MESSAGES: :[[@LINE-2]]:7: note: consider making it private +// CHECK-FIXES: CRTP() = default; + +template<typename T> +class A : CRTP<A<T>> {}; + +te... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/82403 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits