[clang-tools-extra] 0e95921 - [clang-tidy] Improve check cert-dcl58-cpp.

Balázs Kéri via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 14 00:04:57 PDT 2022


Author: Balázs Kéri
Date: 2022-07-14T09:04:03+02:00
New Revision: 0e95921bc303a206cc6ae1c21ee90ec223bf9e78

URL: https://github.com/llvm/llvm-project/commit/0e95921bc303a206cc6ae1c21ee90ec223bf9e78
DIFF: https://github.com/llvm/llvm-project/commit/0e95921bc303a206cc6ae1c21ee90ec223bf9e78.diff

LOG: [clang-tidy] Improve check cert-dcl58-cpp.

Detect template specializations that should be handled specially.
In some cases it is allowed to extend the `std` namespace with
template specializations.

Reviewed By: aaron.ballman

Differential Revision: https://reviews.llvm.org/D129353

Added: 
    

Modified: 
    clang-tools-extra/clang-tidy/cert/DontModifyStdNamespaceCheck.cpp
    clang-tools-extra/docs/ReleaseNotes.rst
    clang-tools-extra/docs/clang-tidy/checks/cert/dcl58-cpp.rst
    clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/system-header-simulation.h
    clang-tools-extra/test/clang-tidy/checkers/cert/dcl58-cpp.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/cert/DontModifyStdNamespaceCheck.cpp b/clang-tools-extra/clang-tidy/cert/DontModifyStdNamespaceCheck.cpp
index f4051c34aad9c..c63673b91a4fd 100644
--- a/clang-tools-extra/clang-tidy/cert/DontModifyStdNamespaceCheck.cpp
+++ b/clang-tools-extra/clang-tidy/cert/DontModifyStdNamespaceCheck.cpp
@@ -9,37 +9,126 @@
 #include "DontModifyStdNamespaceCheck.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchersInternal.h"
 
+using namespace clang;
 using namespace clang::ast_matchers;
 
+namespace {
+
+AST_POLYMORPHIC_MATCHER_P(
+    hasAnyTemplateArgumentIncludingPack,
+    AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl,
+                                    TemplateSpecializationType, FunctionDecl),
+    clang::ast_matchers::internal::Matcher<TemplateArgument>, InnerMatcher) {
+  ArrayRef<TemplateArgument> Args =
+      clang::ast_matchers::internal::getTemplateSpecializationArgs(Node);
+  for (const auto &Arg : Args) {
+    if (Arg.getKind() != TemplateArgument::Pack)
+      continue;
+    ArrayRef<TemplateArgument> PackArgs = Arg.getPackAsArray();
+    if (matchesFirstInRange(InnerMatcher, PackArgs.begin(), PackArgs.end(),
+                            Finder, Builder) != PackArgs.end())
+      return true;
+  }
+  return matchesFirstInRange(InnerMatcher, Args.begin(), Args.end(), Finder,
+                             Builder) != Args.end();
+}
+
+} // namespace
+
 namespace clang {
 namespace tidy {
 namespace cert {
 
 void DontModifyStdNamespaceCheck::registerMatchers(MatchFinder *Finder) {
-  Finder->addMatcher(
-      namespaceDecl(unless(isExpansionInSystemHeader()),
-                    hasAnyName("std", "posix"),
-                    has(decl(unless(anyOf(
-                        functionDecl(isExplicitTemplateSpecialization()),
-                        cxxRecordDecl(isExplicitTemplateSpecialization()))))))
-          .bind("nmspc"),
-      this);
-}
+  auto HasStdParent =
+      hasDeclContext(namespaceDecl(hasAnyName("std", "posix"),
+                                   unless(hasParent(namespaceDecl())))
+                         .bind("nmspc"));
+  auto UserDefinedType = qualType(
+      hasUnqualifiedDesugaredType(tagType(unless(hasDeclaration(tagDecl(
+          hasAncestor(namespaceDecl(hasAnyName("std", "posix"),
+                                    unless(hasParent(namespaceDecl()))))))))));
+  auto HasNoProgramDefinedTemplateArgument = unless(
+      hasAnyTemplateArgumentIncludingPack(refersToType(UserDefinedType)));
+  auto InsideStdClassOrClassTemplateSpecialization = hasDeclContext(
+      anyOf(cxxRecordDecl(HasStdParent),
+            classTemplateSpecializationDecl(
+                HasStdParent, HasNoProgramDefinedTemplateArgument)));
 
-void DontModifyStdNamespaceCheck::check(
-    const MatchFinder::MatchResult &Result) {
-  const auto *N = Result.Nodes.getNodeAs<NamespaceDecl>("nmspc");
+  // Try to follow exactly CERT rule DCL58-CPP (this text is taken from C++
+  // standard into the CERT rule):
+  // "
+  // 1 The behavior of a C++ program is undefined if it adds declarations or
+  // definitions to namespace std or to a namespace within namespace std unless
+  // otherwise specified. A program may add a template specialization for any
+  // standard library template to namespace std only if the declaration depends
+  // on a user-defined type and the specialization meets the standard library
+  // requirements for the original template and is not explicitly prohibited. 2
+  // The behavior of a C++ program is undefined if it declares — an explicit
+  // specialization of any member function of a standard library class template,
+  // or — an explicit specialization of any member function template of a
+  // standard library class or class template, or — an explicit or partial
+  // specialization of any member class template of a standard library class or
+  // class template.
+  // "
+  // The "standard library requirements" and explicit prohibition are not
+  // checked.
 
-  // Only consider top level namespaces.
-  if (N->getParent() != Result.Context->getTranslationUnitDecl())
-    return;
+  auto BadNonTemplateSpecializationDecl =
+      decl(unless(anyOf(functionDecl(isExplicitTemplateSpecialization()),
+                        varDecl(isExplicitTemplateSpecialization()),
+                        cxxRecordDecl(isExplicitTemplateSpecialization()))),
+           HasStdParent);
+  auto BadClassTemplateSpec = classTemplateSpecializationDecl(
+      HasNoProgramDefinedTemplateArgument, HasStdParent);
+  auto BadInnerClassTemplateSpec = classTemplateSpecializationDecl(
+      InsideStdClassOrClassTemplateSpecialization);
+  auto BadFunctionTemplateSpec =
+      functionDecl(unless(cxxMethodDecl()), isExplicitTemplateSpecialization(),
+                   HasNoProgramDefinedTemplateArgument, HasStdParent);
+  auto BadMemberFunctionSpec =
+      cxxMethodDecl(isExplicitTemplateSpecialization(),
+                    InsideStdClassOrClassTemplateSpecialization);
 
-  diag(N->getLocation(),
-       "modification of %0 namespace can result in undefined behavior")
-      << N;
+  Finder->addMatcher(decl(anyOf(BadNonTemplateSpecializationDecl,
+                                BadClassTemplateSpec, BadInnerClassTemplateSpec,
+                                BadFunctionTemplateSpec, BadMemberFunctionSpec),
+                          unless(isExpansionInSystemHeader()))
+                         .bind("decl"),
+                     this);
 }
-
 } // namespace cert
 } // namespace tidy
 } // namespace clang
+
+static const NamespaceDecl *getTopLevelLexicalNamespaceDecl(const Decl *D) {
+  const NamespaceDecl *LastNS = nullptr;
+  while (D) {
+    if (const auto *NS = dyn_cast<NamespaceDecl>(D))
+      LastNS = NS;
+    D = dyn_cast_or_null<Decl>(D->getLexicalDeclContext());
+  }
+  return LastNS;
+}
+
+void clang::tidy::cert::DontModifyStdNamespaceCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *D = Result.Nodes.getNodeAs<Decl>("decl");
+  const auto *NS = Result.Nodes.getNodeAs<NamespaceDecl>("nmspc");
+  if (!D || !NS)
+    return;
+
+  diag(D->getLocation(),
+       "modification of %0 namespace can result in undefined behavior")
+      << NS;
+  // 'NS' is not always the namespace declaration that lexically contains 'D',
+  // try to find such a namespace.
+  if (const NamespaceDecl *LexNS = getTopLevelLexicalNamespaceDecl(D)) {
+    assert(NS->getCanonicalDecl() == LexNS->getCanonicalDecl() &&
+           "Mismatch in found namespace");
+    diag(LexNS->getLocation(), "%0 namespace opened here", DiagnosticIDs::Note)
+        << LexNS;
+  }
+}

diff  --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 80bb85d1fc344..c7fcd52578686 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -183,6 +183,11 @@ Changes in existing checks
 
   - Don't emit an erroneous warning on self-moves.
 
+- Improved :doc:`cert-dcl58-cpp
+  <clang-tidy/checks/cert/dcl58-cpp>` check.
+
+  The check now detects explicit template specializations that are handled specially.
+
 - Made :doc:`cert-oop57-cpp <clang-tidy/checks/cert/oop57-cpp>` more sensitive
   by checking for an arbitrary expression in the second argument of ``memset``.
 

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/cert/dcl58-cpp.rst b/clang-tools-extra/docs/clang-tidy/checks/cert/dcl58-cpp.rst
index a86d1b981ee21..fbcc6281a8898 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/cert/dcl58-cpp.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/cert/dcl58-cpp.rst
@@ -6,15 +6,54 @@ cert-dcl58-cpp
 Modification of the ``std`` or ``posix`` namespace can result in undefined
 behavior.
 This check warns for such modifications.
+The ``std`` (or ``posix``) namespace is allowed to be extended with (class or
+function) template specializations that depend on an user-defined type (a type
+that is not defined in the standard system headers).
+
+The check detects the following (user provided) declarations in namespace ``std`` or ``posix``:
+
+- Anything that is not a template specialization.
+- Explicit specializations of any standard library function template or class template, if it does not have any user-defined type as template argument.
+- Explicit specializations of any member function of a standard library class template.
+- Explicit specializations of any member function template of a standard library class or class template.
+- Explicit or partial specialization of any member class template of a standard library class or class template.
 
 Examples:
 
 .. code-block:: c++
 
   namespace std {
-    int x; // May cause undefined behavior.
+    int x; // warning: modification of 'std' namespace can result in undefined behavior [cert-dcl58-cpp]
   }
 
+  namespace posix::a { // warning: modification of 'posix' namespace can result in undefined behavior
+  }
+
+  template <>
+  struct ::std::hash<long> { // warning: modification of 'std' namespace can result in undefined behavior
+    unsigned long operator()(const long &K) const {
+      return K;
+    }
+  };
+
+  struct MyData { long data; };
+
+  template <>
+  struct ::std::hash<MyData> { // no warning: specialization with user-defined type
+    unsigned long operator()(const MyData &K) const {
+      return K.data;
+    }
+  };
+
+  namespace std {
+    template <>
+    void swap<bool>(bool &a, bool &b); // warning: modification of 'std' namespace can result in undefined behavior
+
+    template <>
+    bool less<void>::operator()<MyData &&, MyData &&>(MyData &&, MyData &&) const { // warning: modification of 'std' namespace can result in undefined behavior
+      return true;
+    }
+  }
 
 This check corresponds to the CERT C++ Coding Standard rule
 `DCL58-CPP. Do not modify the standard namespaces

diff  --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/system-header-simulation.h b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/system-header-simulation.h
index 0df5305e5afb3..b6977cd9ce6c6 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/system-header-simulation.h
+++ b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/system-header-simulation.h
@@ -18,7 +18,60 @@ using false_type = bool_constant<false>;
 template<class T>
 struct is_error_code_enum : false_type {};
 
-template<class T>
+template <class T>
 void swap(T &a, T &b);
+
+enum class io_errc {
+  stream = 1,
+};
+
+template <class... Types>
+class tuple;
+
+template <typename T = void>
+class less;
+
+template <>
+class less<void> {
+public:
+  template <typename T, typename U>
+  bool operator()(T &&Lhs, U &&Rhs) const {
+    return static_cast<T &&>(Lhs) < static_cast<U &&>(Rhs);
+  }
+  template <typename A, typename B = int>
+  struct X {};
+};
+
+template <class Key>
+struct hash;
+
+template <class T>
+class numeric_limits;
+
+struct Outer {
+  struct Inner {};
+};
+
+namespace detail {
+struct X {};
+} // namespace detail
+
+} // namespace std
+
+// Template specializations that are in a system-header file.
+// The purpose is to test cert-dcl58-cpp (no warnings here).
+namespace std {
+template <>
+void swap<short>(short &, short &){};
+
+template <>
+struct is_error_code_enum<short> : true_type {};
+
+template <>
+bool less<void>::operator()<short &&, short &&>(short &&, short &&) const {
+  return false;
 }
 
+template <>
+struct less<void>::X<short> {};
+} // namespace std

diff  --git a/clang-tools-extra/test/clang-tidy/checkers/cert/dcl58-cpp.cpp b/clang-tools-extra/test/clang-tidy/checkers/cert/dcl58-cpp.cpp
index 56eb843046d1f..79beae12792ff 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/cert/dcl58-cpp.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/cert/dcl58-cpp.cpp
@@ -1,4 +1,4 @@
-// RUN: %check_clang_tidy %s cert-dcl58-cpp %t -- -- -std=c++1z -I %clang_tidy_headers
+// RUN: %check_clang_tidy -std=c++17-or-later %s cert-dcl58-cpp %t -- -- -I %clang_tidy_headers
 
 #include "system-header-simulation.h"
 
@@ -15,14 +15,20 @@ namespace A {
 }
 
 namespace posix {
-// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: modification of 'posix' namespace can result in undefined behavior [cert-dcl58-cpp]
-  namespace vmi {
-  }
+// CHECK-MESSAGES: :[[@LINE+2]]:11: warning: modification of 'posix' namespace can result in undefined behavior [cert-dcl58-cpp]
+// CHECK-MESSAGES: :[[@LINE-2]]:11: note: 'posix' namespace opened here
+namespace foo {
+int foobar;
+}
 }
 
 namespace std {
-// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: modification of 'std' namespace can
-  int stdInt;
+// CHECK-MESSAGES: :[[@LINE+2]]:5: warning: modification of 'std' namespace
+// CHECK-MESSAGES: :[[@LINE-2]]:11: note: 'std' namespace opened here
+int stdInt;
+// CHECK-MESSAGES: :[[@LINE+2]]:5: warning: modification of 'std' namespace
+// CHECK-MESSAGES: :[[@LINE-5]]:11: note: 'std' namespace opened here
+int stdInt1;
 }
 
 namespace foobar {
@@ -31,37 +37,234 @@ namespace foobar {
   }
 }
 
+namespace posix {
+// CHECK-MESSAGES: :[[@LINE+2]]:11: warning: modification of 'posix' namespace
+// CHECK-MESSAGES: :[[@LINE-2]]:11: note: 'posix' namespace opened here
+namespace std {
+}
+} // namespace posix
+
 namespace posix::a {
-// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: modification of 'posix' namespace 
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: modification of 'posix' namespace
+// CHECK-MESSAGES: :[[@LINE-2]]:11: note: 'posix' namespace opened here
 }
 
+namespace std {
+// no-warning: empty
+} // namespace std
+
+namespace std {
+// Warn for non-NamedDecls as well.
+// CHECK-MESSAGES: :[[@LINE+2]]:1: warning: modification of 'std' namespace
+// CHECK-MESSAGES: :[[@LINE-3]]:11: note: 'std' namespace opened here
+static_assert(1 == 1, "non-NamedDecl");
+} // namespace std
+
 enum class MyError {
   ErrorA,
   ErrorB
 };
 
 namespace std {
+// no-warning: Class template specialized by a program-defined type.
 template <>
 struct is_error_code_enum<MyError> : std::true_type {};
 
+// no-warning: Function template specialized by a program-defined type.
 template<>
 void swap<MyError>(MyError &a, MyError &b);
 }
 
-enum class MyError2 {
-  Error2A,
-  Error2B
-};
+using ConstBoolPtr = const bool *;
 
 namespace std {
-// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: modification of 'std' namespace 
+// class template, builtin type
+// CHECK-MESSAGES: :[[@LINE+3]]:8: warning: modification of 'std' namespace
+// CHECK-MESSAGES: :[[@LINE-3]]:11: note: 'std' namespace opened here
+template <>
+struct is_error_code_enum<bool> : std::true_type {};
+// function template, builtin type
+// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: modification of 'std' namespace
+// CHECK-MESSAGES: :[[@LINE-8]]:11: note: 'std' namespace opened here
 template <>
-struct is_error_code_enum<MyError2> : std::true_type {};
+void swap<bool>(bool &, bool &);
+// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: modification of 'std' namespace
+// CHECK-MESSAGES: :[[@LINE-12]]:11: note: 'std' namespace opened here
+template <>
+void swap<ConstBoolPtr>(ConstBoolPtr &, ConstBoolPtr &);
+} // namespace std
 
-int foobar;
+namespace std {
+// class template, std type
+// CHECK-MESSAGES: :[[@LINE+3]]:8: warning: modification of 'std' namespace
+// CHECK-MESSAGES: :[[@LINE-3]]:11: note: 'std' namespace opened here
+template <>
+struct is_error_code_enum<std::io_errc> : std::true_type {};
+// function template, std type
+// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: modification of 'std' namespace
+// CHECK-MESSAGES: :[[@LINE-8]]:11: note: 'std' namespace opened here
+template <>
+void swap<std::io_errc>(std::io_errc &, std::io_errc &);
+} // namespace std
+
+// parameter pack, has program-defined type
+namespace std {
+// no-warning: there is one program-defined type.
+template <>
+class tuple<int, MyError, std::io_errc> {};
+} // namespace std
+
+// parameter pack, only builtin or std type
+namespace std {
+// Forbid variadic specializations over only `std::` or builtin types.
+// CHECK-MESSAGES: :[[@LINE+3]]:7: warning: modification of 'std' namespace
+// CHECK-MESSAGES: :[[@LINE-3]]:11: note: 'std' namespace opened here
+template <>
+class tuple<int, const std::io_errc, float> {};
+} // namespace std
+
+namespace std {
+// Test nested standard declarations.
+// CHECK-MESSAGES: :[[@LINE+3]]:8: warning: modification of 'std' namespace
+// CHECK-MESSAGES: :[[@LINE-3]]:11: note: 'std' namespace opened here
+template <>
+struct is_error_code_enum<std::Outer::Inner> : std::true_type {};
+} // namespace std
+
+namespace std {
+// Test nested namespace.
+// CHECK-MESSAGES: :[[@LINE+3]]:8: warning: modification of 'std' namespace
+// CHECK-MESSAGES: :[[@LINE-3]]:11: note: 'std' namespace opened here
+template <>
+struct is_error_code_enum<std::detail::X> : std::true_type {};
+} // namespace std
+
+// Test member function template specializations.
+namespace std {
+// CHECK-MESSAGES: :[[@LINE+3]]:18: warning: modification of 'std' namespace
+// CHECK_MESSAGES: :[[@LINE-2]]:11: note: 'std' namespace opened here
+template <>
+bool less<void>::operator()<int &&, float &&>(int &&, float &&) const {
+  return true;
+}
+// CHECK-MESSAGES: :[[@LINE+3]]:18: warning: modification of 'std' namespace
+// CHECK_MESSAGES: :[[@LINE-8]]:11: note: 'std' namespace opened here
+template <>
+bool less<void>::operator()<MyError &&, MyError &&>(MyError &&, MyError &&) const {
+  return true;
 }
+} // namespace std
+
+// Test member class template specializations.
+namespace std {
+// CHECK-MESSAGES: :[[@LINE+3]]:20: warning: modification of 'std' namespace
+// CHECK_MESSAGES: :[[@LINE-2]]:11: note: 'std' namespace opened here
+template <>
+struct less<void>::X<bool> {};
+// CHECK-MESSAGES: :[[@LINE+3]]:20: warning: modification of 'std' namespace
+// CHECK_MESSAGES: :[[@LINE-6]]:11: note: 'std' namespace opened here
+template <>
+struct less<void>::X<MyError> {};
+// CHECK-MESSAGES: :[[@LINE+3]]:20: warning: modification of 'std' namespace
+// CHECK_MESSAGES: :[[@LINE-10]]:11: note: 'std' namespace opened here
+template <typename T>
+struct less<void>::X<MyError, T> {};
+} // namespace std
+
+// We did not open the 'std' namespace, but still specialized the member
+// function of 'std::less'.
+// CHECK-MESSAGES: :[[@LINE+3]]:23: warning: modification of 'std' namespace
+// no-note: There is no opening of 'std' namespace, hence no note emitted.
+template <>
+bool std::less<void>::operator()<int &&, int &&>(int &&, int &&) const {
+  return true;
+}
+
+namespace SpaceA {
+namespace SpaceB {
+class MapKey {
+  int Type = 0;
+
+public:
+  MapKey() = default;
+  int getType() const { return Type; }
+};
+} // namespace SpaceB
+} // namespace SpaceA
+
+// no-warning: Specializing for 'std::hash' for a program-defined type.
+template <>
+struct std::hash<::SpaceA::SpaceB::MapKey> {
+  // no-warning
+  unsigned long operator()(const ::SpaceA::SpaceB::MapKey &K) const {
+    return K.getType();
+  }
+  // no-warning
+  bool operator()(const ::SpaceA::SpaceB::MapKey &K1,
+                  const ::SpaceA::SpaceB::MapKey &K2) const {
+    return K1.getType() < K2.getType();
+  }
+};
+
+using myint = int;
 
-using namespace std;
+// The type alias declaration is the same as typedef, does not introduce a
+// program-defined type.
+// CHECK-MESSAGES: :[[@LINE+2]]:13: warning: modification of 'std' namespace
+template <>
+struct std::hash<myint> {
+  // no-warning: The warning was already reported for the struct itself.
+  unsigned long operator()(const myint &K) const {
+    return K;
+  }
+  // no-warning: The warning was already reported for the struct itself.
+  bool operator()(const myint &K1,
+                  const myint &K2) const {
+    return K1 < K2;
+  }
+};
 
-int x;
+// CHECK-MESSAGES: :[[@LINE+2]]:15: warning: modification of 'std' namespace
+template <>
+struct ::std::hash<long> {
+  unsigned long operator()(const long &K) const {
+    return K;
+  }
+};
 
+namespace ranges {
+namespace detail {
+struct 
diff max_t {};
+using LongT = long;
+} // namespace detail
+} // namespace ranges
+
+namespace std {
+// no-warning: specialization with an user-defined type
+template <>
+struct numeric_limits<::ranges::detail::
diff max_t> {
+  static constexpr bool is_signed = true;
+  static constexpr bool is_integer = true;
+  static constexpr ::ranges::detail::
diff max_t max() noexcept {
+    return {};
+  }
+};
+inline constexpr bool numeric_limits<::ranges::detail::
diff max_t>::is_signed;
+inline constexpr bool numeric_limits<::ranges::detail::
diff max_t>::is_integer;
+} // namespace std
+
+namespace std {
+// specialization with type alias to non-program-defined-type
+// CHECK-MESSAGES: :[[@LINE+3]]:8: warning: modification of 'std' namespace
+// CHECK_MESSAGES: :[[@LINE-3]]:11: note: 'std' namespace opened here
+template <>
+struct numeric_limits<::ranges::detail::LongT> {
+  static constexpr bool is_signed = true;
+  static constexpr bool is_integer = true;
+  static constexpr ::ranges::detail::LongT max() noexcept {
+    return 1;
+  }
+};
+inline constexpr bool numeric_limits<::ranges::detail::LongT>::is_signed;
+inline constexpr bool numeric_limits<::ranges::detail::LongT>::is_integer;
+} // namespace std


        


More information about the cfe-commits mailing list