[clang-tools-extra] 8e56fb8 - [clang-tidy] CRTP Constructor Accessibility Check (#82403)

via cfe-commits cfe-commits at lists.llvm.org
Mon Mar 4 16:09:43 PST 2024


Author: isuckatcs
Date: 2024-03-05T01:09:39+01:00
New Revision: 8e56fb824a43d54208d44a403366faa5d633ee8b

URL: https://github.com/llvm/llvm-project/commit/8e56fb824a43d54208d44a403366faa5d633ee8b
DIFF: https://github.com/llvm/llvm-project/commit/8e56fb824a43d54208d44a403366faa5d633ee8b.diff

LOG: [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.

Added: 
    clang-tools-extra/clang-tidy/bugprone/CrtpConstructorAccessibilityCheck.cpp
    clang-tools-extra/clang-tidy/bugprone/CrtpConstructorAccessibilityCheck.h
    clang-tools-extra/docs/clang-tidy/checks/bugprone/crtp-constructor-accessibility.rst
    clang-tools-extra/test/clang-tidy/checkers/bugprone/crtp-constructor-accessibility.cpp

Modified: 
    clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
    clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
    clang-tools-extra/docs/ReleaseNotes.rst
    clang-tools-extra/docs/clang-tidy/checks/list.rst

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index a8a23b045f80bb..e518a64abc52eb 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<UniquePtrArrayMismatchCheck>(
         "bugprone-unique-ptr-array-mismatch");
+    CheckFactories.registerCheck<CrtpConstructorAccessibilityCheck>(
+        "bugprone-crtp-constructor-accessibility");
     CheckFactories.registerCheck<UnsafeFunctionsCheck>(
         "bugprone-unsafe-functions");
     CheckFactories.registerCheck<UnusedLocalNonTrivialVariableCheck>(

diff  --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index 1cd6fb207d7625..638fba03a43587 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 00000000000000..6175fcdfd229c3
--- /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<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

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 00000000000000..785116218f4689
--- /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 1e1949756e0e2c..143ae230fc443c 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
+  <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.
 

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 00000000000000..afd88764b59673
--- /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 <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 
diff erent 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.
+

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 5e57bc0ee483fe..d03e7af688f007 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 <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>`,

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 00000000000000..cb41923df157cf
--- /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 <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


        


More information about the cfe-commits mailing list