llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: None (fscheidl)
<details>
<summary>Changes</summary>
### Bug fixes to existing APIs:
**clang_Cursor_getNumTemplateArguments**:
- Add `CXCursor_CXXMethod` to accepted cursor kinds, queries on method
specializations would incorrectly return -1 before
- Expand parameter packs into a flat count
Previously for the following this would incorrectly report 2 (T + the pack)
instead of 3
```
template<typename T, typename... Ts> struct Foo {};
Foo<int, float, double> x;
```
**clang_Cursor_getTemplateArgument** (static helper used by all
getTemplateArgument* APIs):
- Add `CXCursor_CXXMethod` to accepted cursor kinds (would also fail before)
- Allow indexing into parameter packs, so that index 2 for the specialization
above returns `double` instead of
failing with out of bounds
### New APIs:
- **clang_Cursor_getNumTemplateParameters**: get the parameter count on a
class/function template
- **clang_Cursor_getTemplateParameter**: get the I-th template parameter as a
cursor
- **clang_Cursor_isTemplateParameterPack**: check if a template parameter is a
parameter pack
Currently the only way of checking whether a cursor represents a parameter pack
seems to be to inspect the spelling.
- **clang_Cursor_getTemplateArgumentIntegralType**: get the type of a non-type
template parameter argument.
The existing `clang_Cursor_getTemplateArgumentValue` and
`clang_Cursor_getTemplateArgumentUnsignedValue` return the value but provide no
easy way to query the type of the non-type template parameter, making it hard
to determine which of the two to use (possible to infer based on the return
value of each one, but it's quite cumbersome)
---
Patch is 22.46 KiB, truncated to 20.00 KiB below, full version:
https://github.com/llvm/llvm-project/pull/183504.diff
8 Files Affected:
- (modified) clang/bindings/python/clang/cindex.py (+26)
- (modified) clang/bindings/python/tests/cindex/test_cursor.py (+135)
- (modified) clang/docs/ReleaseNotes.rst (+6)
- (modified) clang/include/clang-c/Index.h (+56-2)
- (added) clang/test/Index/template-parameters.cpp (+14)
- (modified) clang/tools/c-index-test/c-index-test.c (+5-4)
- (modified) clang/tools/libclang/CXCursor.cpp (+107-28)
- (modified) clang/tools/libclang/libclang.map (+8)
``````````diff
diff --git a/clang/bindings/python/clang/cindex.py
b/clang/bindings/python/clang/cindex.py
index 1896a0a9c1c34..9207fd0727063 100644
--- a/clang/bindings/python/clang/cindex.py
+++ b/clang/bindings/python/clang/cindex.py
@@ -2272,6 +2272,28 @@ def get_template_argument_unsigned_value(self, num: int)
-> int:
"""Returns the value of the indicated arg as an unsigned 64b
integer."""
return conf.lib.clang_Cursor_getTemplateArgumentUnsignedValue(self,
num) # type: ignore [no-any-return]
+ @cursor_null_guard
+ def get_template_argument_integral_type(self, num: int) -> Type:
+ """Returns the type of an integral template argument at the given
index."""
+ return Type.from_result(
+ conf.lib.clang_Cursor_getTemplateArgumentIntegralType(self, num),
self
+ )
+
+ @cursor_null_guard
+ def get_num_template_parameters(self) -> int:
+ """Returns the number of template parameters, or -1 if not a
template."""
+ return conf.lib.clang_Cursor_getNumTemplateParameters(self) # type:
ignore [no-any-return]
+
+ @cursor_null_guard
+ def get_template_parameter(self, num: int) -> Cursor:
+ """Returns the template parameter at the given index."""
+ return conf.lib.clang_Cursor_getTemplateParameter(self, num) # type:
ignore [no-any-return]
+
+ @cursor_null_guard
+ def is_template_parameter_pack(self) -> bool:
+ """Returns True if the cursor is a template parameter pack."""
+ return bool(conf.lib.clang_Cursor_isTemplateParameterPack(self))
+
@cursor_null_guard
def get_children(self) -> Iterator[Cursor]:
"""Return an iterator for accessing the children of this cursor."""
@@ -4427,6 +4449,10 @@ def set_property(self, property, value):
("clang_Cursor_getTemplateArgumentType", [Cursor, c_uint], Type),
("clang_Cursor_getTemplateArgumentValue", [Cursor, c_uint], c_longlong),
("clang_Cursor_getTemplateArgumentUnsignedValue", [Cursor, c_uint],
c_ulonglong),
+ ("clang_Cursor_getTemplateArgumentIntegralType", [Cursor, c_uint], Type),
+ ("clang_Cursor_getNumTemplateParameters", [Cursor], c_int),
+ ("clang_Cursor_getTemplateParameter", [Cursor, c_uint], Cursor),
+ ("clang_Cursor_isTemplateParameterPack", [Cursor], c_uint),
("clang_getCursorBinaryOperatorKind", [Cursor], c_int),
("clang_Cursor_getBriefCommentText", [Cursor], _CXString),
("clang_Cursor_getRawCommentText", [Cursor], _CXString),
diff --git a/clang/bindings/python/tests/cindex/test_cursor.py
b/clang/bindings/python/tests/cindex/test_cursor.py
index 76680e576b307..4adfc244f2582 100644
--- a/clang/bindings/python/tests/cindex/test_cursor.py
+++ b/clang/bindings/python/tests/cindex/test_cursor.py
@@ -916,6 +916,141 @@ def test_get_template_argument_unsigned_value(self):
self.assertEqual(foos[1].get_template_argument_unsigned_value(0),
2**32 - 7)
self.assertEqual(foos[1].get_template_argument_unsigned_value(2), True)
+ def test_get_template_argument_integral_type(self):
+ tu = get_tu(TEMPLATE_ARG_TEST, lang="cpp")
+ foos = get_cursors(tu, "foo")
+
+ self.assertEqual(
+ foos[1].get_template_argument_integral_type(0).kind, TypeKind.INT
+ )
+ self.assertEqual(
+ foos[1].get_template_argument_integral_type(1).kind,
+ TypeKind.INVALID,
+ )
+ self.assertEqual(
+ foos[1].get_template_argument_integral_type(2).kind, TypeKind.BOOL
+ )
+
+ def test_get_template_argument_integral_type_pack(self):
+ source = """
+ template<int... Ns> struct Foo {};
+ template class Foo<1, 2, 3>;
+ """
+ tu = get_tu(source, lang="cpp")
+ foo = get_cursor(tu, "Foo")
+ # Find the specialization.
+ spec = None
+ for c in tu.cursor.walk_preorder():
+ if c.kind == CursorKind.STRUCT_DECL and c.spelling == "Foo":
+ if c.get_num_template_arguments() >= 0:
+ spec = c
+ break
+ self.assertIsNotNone(spec)
+ self.assertEqual(spec.get_num_template_arguments(), 3)
+ self.assertEqual(
+ spec.get_template_argument_integral_type(0).kind, TypeKind.INT
+ )
+ self.assertEqual(
+ spec.get_template_argument_integral_type(1).kind, TypeKind.INT
+ )
+ self.assertEqual(
+ spec.get_template_argument_integral_type(2).kind, TypeKind.INT
+ )
+
+ def test_get_num_template_parameters(self):
+ source = "template<typename T, int N> void foo(); template<typename...
Ts> void bar(); void baz();"
+ tu = get_tu(source, lang="cpp")
+ foo = get_cursor(tu, "foo")
+ bar = get_cursor(tu, "bar")
+ baz = get_cursor(tu, "baz")
+ self.assertIsNotNone(foo)
+ self.assertIsNotNone(bar)
+ self.assertIsNotNone(baz)
+
+ self.assertEqual(foo.get_num_template_parameters(), 2)
+ self.assertEqual(bar.get_num_template_parameters(), 1)
+ self.assertEqual(baz.get_num_template_parameters(), -1)
+
+ def test_get_template_parameter(self):
+ source = "template<typename T, int N> void foo();"
+ tu = get_tu(source, lang="cpp")
+ foo = get_cursor(tu, "foo")
+ self.assertIsNotNone(foo)
+
+ t_param = foo.get_template_parameter(0)
+ n_param = foo.get_template_parameter(1)
+ self.assertEqual(t_param.kind, CursorKind.TEMPLATE_TYPE_PARAMETER)
+ self.assertEqual(t_param.spelling, "T")
+ self.assertEqual(n_param.kind, CursorKind.TEMPLATE_NON_TYPE_PARAMETER)
+ self.assertEqual(n_param.spelling, "N")
+ oob = conf.lib.clang_Cursor_getTemplateParameter(foo, 5)
+ self.assertTrue(oob.is_null())
+
+ def test_is_template_parameter_pack(self):
+ # Type parameter pack
+ source = "template<typename T, typename... Ts> void foo();"
+ tu = get_tu(source, lang="cpp")
+ foo = get_cursor(tu, "foo")
+ self.assertIsNotNone(foo)
+
self.assertFalse(foo.get_template_parameter(0).is_template_parameter_pack())
+
self.assertTrue(foo.get_template_parameter(1).is_template_parameter_pack())
+
+ # Non-type parameter pack
+ source = "template<int... Ns> void bar();"
+ tu = get_tu(source, lang="cpp")
+ bar = get_cursor(tu, "bar")
+ self.assertIsNotNone(bar)
+
self.assertTrue(bar.get_template_parameter(0).is_template_parameter_pack())
+
+ # Template template parameter pack
+ source = "template<template<typename> class... Ts> void baz();"
+ tu = get_tu(source, lang="cpp")
+ baz = get_cursor(tu, "baz")
+ self.assertIsNotNone(baz)
+
self.assertTrue(baz.get_template_parameter(0).is_template_parameter_pack())
+
+ def test_get_template_argument_variadic(self):
+ source = """
+ template<typename T, typename... Ts> struct Foo {};
+ Foo<int, float, double> x;
+ Foo<int> y;
+ """
+ tu = get_tu(source, lang="cpp")
+
+ x = get_cursor(tu, "x")
+ self.assertIsNotNone(x)
+ x_type = x.type.get_declaration()
+ self.assertEqual(x_type.get_num_template_arguments(), 3)
+ self.assertEqual(x_type.get_template_argument_type(0).kind,
TypeKind.INT)
+ self.assertEqual(x_type.get_template_argument_type(1).kind,
TypeKind.FLOAT)
+ self.assertEqual(x_type.get_template_argument_type(2).kind,
TypeKind.DOUBLE)
+ self.assertEqual(x_type.get_template_argument_type(3).kind,
TypeKind.INVALID)
+
+ # Empty pack: only T=int, pack contributes 0.
+ y = get_cursor(tu, "y")
+ self.assertIsNotNone(y)
+ y_type = y.type.get_declaration()
+ self.assertEqual(y_type.get_num_template_arguments(), 1)
+
+ def test_get_num_template_arguments_method(self):
+ source = """
+ struct S {
+ template<typename U> void method(U) {}
+ };
+ void use() { S().method(42); }
+ """
+ tu = get_tu(source, lang="cpp")
+ # Find a call to S::method<int> and get the referenced specialization.
+ method_spec = None
+ for c in tu.cursor.walk_preorder():
+ if c.kind == CursorKind.CALL_EXPR and c.spelling == "method":
+ method_spec = c.referenced
+ break
+ self.assertIsNotNone(method_spec)
+ self.assertEqual(method_spec.kind, CursorKind.CXX_METHOD)
+ self.assertEqual(method_spec.get_num_template_arguments(), 1)
+ self.assertEqual(method_spec.get_template_argument_type(0).kind,
TypeKind.INT)
+
def test_referenced(self):
tu = get_tu("void foo(); void bar() { foo(); }")
foo = get_cursor(tu, "foo")
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index cb1010aee1edd..e6286ab41fde8 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -430,6 +430,10 @@ libclang
- Visit constraints of `auto` type to properly visit concept usages (#GH166580)
- Visit switch initializer statements
(https://bugs.kde.org/show_bug.cgi?id=415537#c2)
- Fix crash in clang_getBinaryOperatorKindSpelling and
clang_getUnaryOperatorKindSpelling
+- Added ``clang_Cursor_getNumTemplateParameters``,
``clang_Cursor_getTemplateParameter``,
+ ``clang_Cursor_isTemplateParameterPack``, and
``clang_Cursor_getTemplateArgumentIntegralType``.
+- Fixed ``clang_Cursor_getNumTemplateArguments`` and related APIs to work with
+ ``CXCursor_CXXMethod`` cursors and to correctly allow indexing into
parameter pack arguments.
Code Completion
---------------
@@ -468,6 +472,8 @@ Python Binding Changes
``CodeCompletionResults.results`` should be changed to directly use
``CodeCompletionResults``: it nows supports ``__len__`` and ``__getitem__``,
so it can be used the same as ``CodeCompletionResults.results``.
+- Added ``Cursor.get_num_template_parameters``,
``Cursor.get_template_parameter``,
+ ``Cursor.is_template_parameter_pack``, and
``Cursor.get_template_argument_integral_type``.
OpenMP Support
--------------
diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h
index 203634c80d82a..8d04932f57fcf 100644
--- a/clang/include/clang-c/Index.h
+++ b/clang/include/clang-c/Index.h
@@ -3267,7 +3267,29 @@ CINDEX_LINKAGE CXType
clang_Cursor_getTemplateArgumentType(CXCursor C,
unsigned I);
/**
- * Retrieve the value of an Integral TemplateArgument (of a function
+ * Retrieve the type of an Integral TemplateArgument at a given index.
+ *
+ * For example, for:
+ * template <typename T, int N>
+ * void foo() {}
+ *
+ * template <>
+ * void foo<float, 42>();
+ *
+ * If called with I = 1, the type "int" will be returned (the type of the
+ * integral argument 42). An invalid type is returned if the argument at
+ * index I is not integral, or if the index is out of range.
+ *
+ * \param C a cursor representing a template specialization.
+ * \param I the zero-based index of the template argument.
+ *
+ * \returns the type of the integral template argument, or an invalid type.
+ */
+CINDEX_LINKAGE CXType clang_Cursor_getTemplateArgumentIntegralType(CXCursor C,
+ unsigned I);
+
+/**
+ * Retrieve the value of an Integral TemplateArgument (of a function or class
* decl representing a template specialization) as a signed long long.
*
* It is undefined to call this function on a CXCursor that does not represent
a
@@ -3288,7 +3310,7 @@ CINDEX_LINKAGE long long
clang_Cursor_getTemplateArgumentValue(CXCursor C,
unsigned I);
/**
- * Retrieve the value of an Integral TemplateArgument (of a function
+ * Retrieve the value of an Integral TemplateArgument (of a function or class
* decl representing a template specialization) as an unsigned long long.
*
* It is undefined to call this function on a CXCursor that does not represent
a
@@ -3308,6 +3330,38 @@ CINDEX_LINKAGE long long
clang_Cursor_getTemplateArgumentValue(CXCursor C,
CINDEX_LINKAGE unsigned long long
clang_Cursor_getTemplateArgumentUnsignedValue(CXCursor C, unsigned I);
+/**
+ * Retrieve the number of template parameters on a template declaration.
+ *
+ * \param C a cursor representing a class template or function template.
+ *
+ * \returns the number of template parameters, or -1 if the cursor does not
+ * represent a template.
+ */
+CINDEX_LINKAGE int clang_Cursor_getNumTemplateParameters(CXCursor C);
+
+/**
+ * Retrieve a template parameter at the given index.
+ *
+ * \param C a cursor representing a class template or function template.
+ * \param I the zero-based index of the template parameter.
+ *
+ * \returns a cursor for the template parameter, or a null cursor if the
+ * index is out of range or the cursor is not a template.
+ */
+CINDEX_LINKAGE CXCursor clang_Cursor_getTemplateParameter(CXCursor C,
+ unsigned I);
+
+/**
+ * Determine whether a template parameter is a parameter pack.
+ *
+ * \param C a cursor representing a template type parameter, non-type
+ * template parameter, or template template parameter.
+ *
+ * \returns non-zero if the template parameter is a parameter pack.
+ */
+CINDEX_LINKAGE unsigned clang_Cursor_isTemplateParameterPack(CXCursor C);
+
/**
* Determine whether two CXTypes represent the same type.
*
diff --git a/clang/test/Index/template-parameters.cpp
b/clang/test/Index/template-parameters.cpp
new file mode 100644
index 0000000000000..b78e850de59e2
--- /dev/null
+++ b/clang/test/Index/template-parameters.cpp
@@ -0,0 +1,14 @@
+// Test template argument pack expansion.
+// RUN: c-index-test -test-load-source all -fno-delayed-template-parsing %s |
FileCheck %s
+
+template<typename T, typename... Ts>
+struct Variadic {};
+
+template class Variadic<int, float, double>;
+template class Variadic<int>;
+
+// Pack with 3 args: should report 3 (not 2).
+// CHECK: StructDecl=Variadic:7:16 (Definition) [Specialization of
Variadic:5:8] [Template arg 0: kind: 1, type: int] [Template arg 1: kind: 1,
type: float] [Template arg 2: kind: 1, type: double]
+
+// Empty pack: should report 1 (just T, pack contributes 0).
+// CHECK: StructDecl=Variadic:8:16 (Definition) [Specialization of
Variadic:5:8] [Template arg 0: kind: 1, type: int]
diff --git a/clang/tools/c-index-test/c-index-test.c
b/clang/tools/c-index-test/c-index-test.c
index cb3245756a394..e6e78c8d5ab37 100644
--- a/clang/tools/c-index-test/c-index-test.c
+++ b/clang/tools/c-index-test/c-index-test.c
@@ -1054,10 +1054,11 @@ static void PrintCursor(CXCursor Cursor, const char
*CommentSchemaFile) {
clang_getCString(Name), line, column);
clang_disposeString(Name);
- if (Cursor.kind == CXCursor_FunctionDecl
- || Cursor.kind == CXCursor_StructDecl
- || Cursor.kind == CXCursor_ClassDecl
- || Cursor.kind == CXCursor_ClassTemplatePartialSpecialization) {
+ if (Cursor.kind == CXCursor_FunctionDecl ||
+ Cursor.kind == CXCursor_CXXMethod ||
+ Cursor.kind == CXCursor_StructDecl ||
+ Cursor.kind == CXCursor_ClassDecl ||
+ Cursor.kind == CXCursor_ClassTemplatePartialSpecialization) {
/* Collect the template parameter kinds from the base template. */
int NumTemplateArgs = clang_Cursor_getNumTemplateArguments(Cursor);
int I;
diff --git a/clang/tools/libclang/CXCursor.cpp
b/clang/tools/libclang/CXCursor.cpp
index 17f485e5c78a5..1f39fdc66a5cf 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -1438,29 +1438,40 @@ CXCursor clang_Cursor_getArgument(CXCursor C, unsigned
i) {
int clang_Cursor_getNumTemplateArguments(CXCursor C) {
CXCursorKind kind = clang_getCursorKind(C);
- if (kind != CXCursor_FunctionDecl && kind != CXCursor_StructDecl &&
- kind != CXCursor_ClassDecl &&
+ if (kind != CXCursor_FunctionDecl && kind != CXCursor_CXXMethod &&
+ kind != CXCursor_StructDecl && kind != CXCursor_ClassDecl &&
kind != CXCursor_ClassTemplatePartialSpecialization) {
return -1;
}
- if (const auto *FD =
- llvm::dyn_cast_if_present<clang::FunctionDecl>(getCursorDecl(C))) {
+ const TemplateArgumentList *TAL = nullptr;
+
+ if (const auto *FD = dyn_cast_if_present<FunctionDecl>(getCursorDecl(C))) {
const FunctionTemplateSpecializationInfo *SpecInfo =
FD->getTemplateSpecializationInfo();
if (!SpecInfo) {
return -1;
}
- return SpecInfo->TemplateArguments->size();
+ TAL = SpecInfo->TemplateArguments;
}
- if (const auto *SD =
- llvm::dyn_cast_if_present<clang::ClassTemplateSpecializationDecl>(
- getCursorDecl(C))) {
- return SD->getTemplateArgs().size();
+ if (!TAL) {
+ if (const auto *SD = dyn_cast_if_present<ClassTemplateSpecializationDecl>(
+ getCursorDecl(C))) {
+ TAL = &SD->getTemplateArgs();
+ }
}
- return -1;
+ if (!TAL)
+ return -1;
+
+ unsigned ArgCount = TAL->size();
+ for (unsigned i = 0; i < TAL->size(); i++) {
+ const TemplateArgument &Arg = TAL->get(i);
+ if (Arg.getKind() == TemplateArgument::Pack)
+ ArgCount += Arg.pack_size() - 1;
+ }
+ return ArgCount;
}
enum CXGetTemplateArgumentStatus {
@@ -1485,14 +1496,15 @@ enum CXGetTemplateArgumentStatus {
static int clang_Cursor_getTemplateArgument(CXCursor C, unsigned I,
TemplateArgument *TA) {
CXCursorKind kind = clang_getCursorKind(C);
- if (kind != CXCursor_FunctionDecl && kind != CXCursor_StructDecl &&
- kind != CXCursor_ClassDecl &&
+ if (kind != CXCursor_FunctionDecl && kind != CXCursor_CXXMethod &&
+ kind != CXCursor_StructDecl && kind != CXCursor_ClassDecl &&
kind != CXCursor_ClassTemplatePartialSpecialization) {
return -1;
}
- if (const auto *FD =
- llvm::dyn_cast_if_present<clang::FunctionDecl>(getCursorDecl(C))) {
+ const TemplateArgumentList *TAL = nullptr;
+
+ if (const auto *FD = dyn_cast_if_present<FunctionDecl>(getCursorDecl(C))) {
const FunctionTemplateSpecializationInfo *SpecInfo =
FD->getTemplateSpecializationInfo();
@@ -1500,26 +1512,38 @@ static int clang_Cursor_getTemplateArgument(CXCursor C,
unsigned I,
return CXGetTemplateArgumentStatus_NullTemplSpecInfo;
}
- if (I >= SpecInfo->TemplateArguments->size()) {
- return CXGetTemplateArgumentStatus_InvalidIndex;
- }
-
- *TA = SpecInfo->TemplateArguments->get(I);
- return 0;
+ TAL = SpecInfo->TemplateArguments;
}
- if (const auto *SD =
- llvm::dyn_cast_if_present<clang::ClassTemplateSpecializationDecl>(
- getCursorDecl(C))) {
- if (I >= SD->getTemplateArgs().size()) {
- return CXGetTemplateArgumentStatus_InvalidIndex;
+ if (!TAL) {
+ if (const auto *SD = dyn_cast_if_present<ClassTemplateSpecializationDecl>(
+ getCursorDecl(C))) {
+ TAL = &SD->getTemplateArgs();
}
+ }
- *TA = SD->getTemplateArgs()[I];
- return 0;
+ if (!TAL)
+ return CXGetTemplateArgumentStatus_BadDeclCast;
+
+ unsigned current = 0;
+ for (unsigned i = 0; i < TAL->size(); i++) {
+ const auto &TACand = TAL->get(i);
+ if (TACand.getKind() == TemplateArgument::Pack) {
+ if (I < current + TACand.pack_size()) {
+ *TA = TACand.pack_elements()[I - current];
+ return 0;
+ }
+ current += TACand.pack_size();
+ continue;
+ }
+ if (current == I) {
+ *TA = TACand;
+ return 0;
+ }
+ current++;
}
- return CXGetTemplateArgumentStatus_BadDeclCast;
+ return CXGetTemplateArgumentStatus_InvalidIndex;
}
enum CXTemplateArgumentKind clang_Cursor_getTemplateArgumentKind(CXCursor C,
@@ -1603,6 +1627,61 @@ unsigned long long
clang_Cursor_getTemplateArgumentUnsignedValue(CXCursor C,
return TA.getAsIntegral().getZExtValue();
}
+CXType clang_Cursor_getTemplateArgumentIntegralType(CXCursor C, unsigned I) {
+ TemplateArgument TA;
+ if (clang_Cursor_getTemplateArgument(C, I, &TA))
+ return cxtype::MakeCXType(QualType(), getCursorTU(C));
+
+ if (TA.getKind() != TemplateArgument::Integral)
+ return cxtype::MakeCXType(QualType(), getCursorTU(C));
+
+ return cxtype::MakeCXType(TA.getIntegralT...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/183504
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits