[clang-tools-extra] [clang-tidy] Unsafe CRTP check (PR #82403)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 20 10:51:48 PST 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-tidy
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
More information about the cfe-commits
mailing list