r234762 - [libclang] Add functions to get information about fields.
Argyrios Kyrtzidis
akyrtzi at gmail.com
Mon Apr 13 09:55:05 PDT 2015
Author: akirtzidis
Date: Mon Apr 13 11:55:04 2015
New Revision: 234762
URL: http://llvm.org/viewvc/llvm-project?rev=234762&view=rev
Log:
[libclang] Add functions to get information about fields.
Patch by Loïc Jaquemet!
Modified:
cfe/trunk/bindings/python/clang/cindex.py
cfe/trunk/bindings/python/tests/cindex/test_type.py
cfe/trunk/include/clang-c/Index.h
cfe/trunk/test/Index/print-type-size.cpp
cfe/trunk/test/Index/print-type.cpp
cfe/trunk/tools/c-index-test/c-index-test.c
cfe/trunk/tools/libclang/CXType.cpp
cfe/trunk/tools/libclang/libclang.exports
Modified: cfe/trunk/bindings/python/clang/cindex.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/bindings/python/clang/cindex.py?rev=234762&r1=234761&r2=234762&view=diff
==============================================================================
--- cfe/trunk/bindings/python/clang/cindex.py (original)
+++ cfe/trunk/bindings/python/clang/cindex.py Mon Apr 13 11:55:04 2015
@@ -1476,6 +1476,18 @@ class Cursor(Structure):
"""
return TokenGroup.get_tokens(self._tu, self.extent)
+ def get_field_offsetof(self):
+ """Returns the offsetof the FIELD_DECL pointed by this Cursor."""
+ return conf.lib.clang_Cursor_getOffsetOfField(self)
+
+ def is_anonymous(self):
+ """
+ Check if the record is anonymous.
+ """
+ if self.kind == CursorKind.FIELD_DECL:
+ return self.type.get_declaration().is_anonymous()
+ return conf.lib.clang_Cursor_isAnonymous(self)
+
def is_bitfield(self):
"""
Check if the field is a bitfield.
@@ -1884,6 +1896,21 @@ class Type(Structure):
return RefQualifierKind.from_id(
conf.lib.clang_Type_getCXXRefQualifier(self))
+ def get_fields(self):
+ """Return an iterator for accessing the fields of this type."""
+
+ def visitor(field, children):
+ assert field != conf.lib.clang_getNullCursor()
+
+ # Create reference to TU so it isn't GC'd before Cursor.
+ field._tu = self._tu
+ fields.append(field)
+ return 1 # continue
+ fields = []
+ conf.lib.clang_Type_visitFields(self,
+ callbacks['fields_visit'](visitor), fields)
+ return iter(fields)
+
@property
def spelling(self):
"""Retrieve the spelling of this Type."""
@@ -2780,6 +2807,7 @@ class Token(Structure):
callbacks['translation_unit_includes'] = CFUNCTYPE(None, c_object_p,
POINTER(SourceLocation), c_uint, py_object)
callbacks['cursor_visit'] = CFUNCTYPE(c_int, Cursor, Cursor, py_object)
+callbacks['fields_visit'] = CFUNCTYPE(c_int, Cursor, py_object)
# Functions strictly alphabetical order.
functionList = [
@@ -3367,6 +3395,10 @@ functionList = [
[Cursor, c_uint],
c_ulonglong),
+ ("clang_Cursor_isAnonymous",
+ [Cursor],
+ bool),
+
("clang_Cursor_isBitField",
[Cursor],
bool),
@@ -3381,6 +3413,10 @@ functionList = [
_CXString,
_CXString.from_result),
+ ("clang_Cursor_getOffsetOfField",
+ [Cursor],
+ c_longlong),
+
("clang_Type_getAlignOf",
[Type],
c_longlong),
@@ -3401,6 +3437,10 @@ functionList = [
("clang_Type_getCXXRefQualifier",
[Type],
c_uint),
+
+ ("clang_Type_visitFields",
+ [Type, callbacks['fields_visit'], py_object],
+ c_uint),
]
class LibclangError(Exception):
Modified: cfe/trunk/bindings/python/tests/cindex/test_type.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/bindings/python/tests/cindex/test_type.py?rev=234762&r1=234761&r2=234762&view=diff
==============================================================================
--- cfe/trunk/bindings/python/tests/cindex/test_type.py (original)
+++ cfe/trunk/bindings/python/tests/cindex/test_type.py Mon Apr 13 11:55:04 2015
@@ -363,6 +363,7 @@ def test_offset():
"""Ensure Cursor.get_record_field_offset works in anonymous records"""
source="""
struct Test {
+ struct {int a;} typeanon;
struct {
int bariton;
union {
@@ -371,15 +372,23 @@ struct Test {
};
int bar;
};"""
- tries=[(['-target','i386-linux-gnu'],(4,16,0,32,64)),
- (['-target','nvptx64-unknown-unknown'],(8,24,0,32,64)),
- (['-target','i386-pc-win32'],(8,16,0,32,64)),
- (['-target','msp430-none-none'],(2,14,0,32,64))]
+ tries=[(['-target','i386-linux-gnu'],(4,16,0,32,64,96)),
+ (['-target','nvptx64-unknown-unknown'],(8,24,0,32,64,96)),
+ (['-target','i386-pc-win32'],(8,16,0,32,64,96)),
+ (['-target','msp430-none-none'],(2,14,0,32,64,96))]
for flags, values in tries:
- align,total,bariton,foo,bar = values
+ align,total,f1,bariton,foo,bar = values
tu = get_tu(source)
teststruct = get_cursor(tu, 'Test')
- fields = list(teststruct.get_children())
+ children = list(teststruct.get_children())
+ fields = list(teststruct.type.get_fields())
+ assert children[0].kind == CursorKind.STRUCT_DECL
+ assert children[0].spelling != "typeanon"
+ assert children[1].spelling == "typeanon"
+ assert fields[0].kind == CursorKind.FIELD_DECL
+ assert fields[1].kind == CursorKind.FIELD_DECL
+ assert fields[1].is_anonymous()
+ assert teststruct.type.get_offset("typeanon") == f1
assert teststruct.type.get_offset("bariton") == bariton
assert teststruct.type.get_offset("foo") == foo
assert teststruct.type.get_offset("bar") == bar
Modified: cfe/trunk/include/clang-c/Index.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang-c/Index.h?rev=234762&r1=234761&r2=234762&view=diff
==============================================================================
--- cfe/trunk/include/clang-c/Index.h (original)
+++ cfe/trunk/include/clang-c/Index.h Mon Apr 13 11:55:04 2015
@@ -32,7 +32,7 @@
* compatible, thus CINDEX_VERSION_MAJOR is expected to remain stable.
*/
#define CINDEX_VERSION_MAJOR 0
-#define CINDEX_VERSION_MINOR 29
+#define CINDEX_VERSION_MINOR 30
#define CINDEX_VERSION_ENCODE(major, minor) ( \
((major) * 10000) \
@@ -3281,6 +3281,28 @@ CINDEX_LINKAGE long long clang_Type_getS
*/
CINDEX_LINKAGE long long clang_Type_getOffsetOf(CXType T, const char *S);
+/**
+ * \brief Return the offset of the field represented by the Cursor.
+ *
+ * If the cursor is not a field declaration, -1 is returned.
+ * If the cursor semantic parent is not a record field declaration,
+ * CXTypeLayoutError_Invalid is returned.
+ * If the field's type declaration is an incomplete type,
+ * CXTypeLayoutError_Incomplete is returned.
+ * If the field's type declaration is a dependent type,
+ * CXTypeLayoutError_Dependent is returned.
+ * If the field's name S is not found,
+ * CXTypeLayoutError_InvalidFieldName is returned.
+ */
+CINDEX_LINKAGE long long clang_Cursor_getOffsetOfField(CXCursor C);
+
+/**
+ * \brief Determine whether the given cursor represents an anonymous record
+ * declaration.
+ */
+CINDEX_LINKAGE unsigned clang_Cursor_isAnonymous(CXCursor C);
+
+
enum CXRefQualifierKind {
/** \brief No ref-qualifier was provided. */
CXRefQualifier_None = 0,
@@ -5669,6 +5691,44 @@ CINDEX_LINKAGE
CXSourceLocation clang_indexLoc_getCXSourceLocation(CXIdxLoc loc);
/**
+ * \brief Visitor invoked for each field found by a traversal.
+ *
+ * This visitor function will be invoked for each field found by
+ * \c clang_Type_visitFields. Its first argument is the cursor being
+ * visited, its second argument is the client data provided to
+ * \c clang_Type_visitFields.
+ *
+ * The visitor should return one of the \c CXVisitorResult values
+ * to direct \c clang_Type_visitFields.
+ */
+typedef enum CXVisitorResult (*CXFieldVisitor)(CXCursor C,
+ CXClientData client_data);
+
+/**
+ * \brief Visit the fields of a particular type.
+ *
+ * This function visits all the direct fields of the given cursor,
+ * invoking the given \p visitor function with the cursors of each
+ * visited field. The traversal may be ended prematurely, if
+ * the visitor returns \c CXFieldVisit_Break.
+ *
+ * \param T the record type whose field may be visited.
+ *
+ * \param visitor the visitor function that will be invoked for each
+ * field of \p T.
+ *
+ * \param client_data pointer data supplied by the client, which will
+ * be passed to the visitor each time it is invoked.
+ *
+ * \returns a non-zero value if the traversal was terminated
+ * prematurely by the visitor returning \c CXFieldVisit_Break.
+ */
+CINDEX_LINKAGE unsigned clang_Type_visitFields(CXType T,
+ CXFieldVisitor visitor,
+ CXClientData client_data);
+
+
+/**
* @}
*/
Modified: cfe/trunk/test/Index/print-type-size.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Index/print-type-size.cpp?rev=234762&r1=234761&r2=234762&view=diff
==============================================================================
--- cfe/trunk/test/Index/print-type-size.cpp (original)
+++ cfe/trunk/test/Index/print-type-size.cpp Mon Apr 13 11:55:04 2015
@@ -65,12 +65,12 @@ struct Test2 {
int foo;
};
struct {
-//CHECK64: FieldDecl=bar:[[@LINE+1]]:11 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=32]
+//CHECK64: FieldDecl=bar:[[@LINE+1]]:11 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=32/0]
int bar;
};
struct {
struct {
-//CHECK64: FieldDecl=foobar:[[@LINE+1]]:15 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=64]
+//CHECK64: FieldDecl=foobar:[[@LINE+1]]:15 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=64/0]
int foobar;
};
};
@@ -160,6 +160,7 @@ struct s4a {
struct {
struct {
struct {
+//CHECK64: FieldDecl=s4_e1:[[@LINE+1]]:17 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-1/0]
int s4_e1;
};
};
Modified: cfe/trunk/test/Index/print-type.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Index/print-type.cpp?rev=234762&r1=234761&r2=234762&view=diff
==============================================================================
--- cfe/trunk/test/Index/print-type.cpp (original)
+++ cfe/trunk/test/Index/print-type.cpp Mon Apr 13 11:55:04 2015
@@ -44,6 +44,7 @@ void foo(int i, int incomplete_array[])
struct Blob {
int i;
+ int j;
};
int Blob::*member_pointer;
@@ -58,7 +59,7 @@ int Blob::*member_pointer;
// CHECK: NonTypeTemplateParameter=U:8:32 (Definition) [type=unsigned int] [typekind=UInt] [isPOD=1]
// CHECK: TemplateTemplateParameter=W:8:60 (Definition) [type=] [typekind=Invalid] [isPOD=0]
// CHECK: Namespace=inner:14:11 (Definition) [type=] [typekind=Invalid] [isPOD=0]
-// CHECK: StructDecl=Bar:16:8 (Definition) [type=outer::inner::Bar] [typekind=Record] [isPOD=0]
+// CHECK: StructDecl=Bar:16:8 (Definition) [type=outer::inner::Bar] [typekind=Record] [isPOD=0] [nbFields=3]
// CHECK: CXXConstructor=Bar:17:3 (Definition) [type=void (outer::Foo<bool> *){{.*}}] [typekind=FunctionProto] [canonicaltype=void (outer::Foo<bool> *){{.*}}] [canonicaltypekind=FunctionProto] [resulttype=void] [resulttypekind=Void] [args= [outer::Foo<bool> *] [Pointer]] [isPOD=0]
// CHECK: ParmDecl=foo:17:25 (Definition) [type=outer::Foo<bool> *] [typekind=Pointer] [canonicaltype=outer::Foo<bool> *] [canonicaltypekind=Pointer] [isPOD=1] [pointeetype=outer::Foo<bool>] [pointeekind=Unexposed]
// CHECK: NamespaceRef=outer:1:11 [type=] [typekind=Invalid] [isPOD=0]
@@ -115,6 +116,6 @@ int Blob::*member_pointer;
// CHECK: DeclStmt= [type=] [typekind=Invalid] [isPOD=0]
// CHECK: VarDecl=variable_array:43:47 (Definition) [type=int [i]] [typekind=VariableArray] [isPOD=1]
// CHECK: DeclRefExpr=i:43:14 [type=int] [typekind=Int] [isPOD=1]
-// CHECK: StructDecl=Blob:45:8 (Definition) [type=Blob] [typekind=Record] [isPOD=1]
+// CHECK: StructDecl=Blob:45:8 (Definition) [type=Blob] [typekind=Record] [isPOD=1] [nbFields=2]
// CHECK: FieldDecl=i:46:7 (Definition) [type=int] [typekind=Int] [isPOD=1]
-// CHECK: VarDecl=member_pointer:48:12 (Definition) [type=int Blob::*] [typekind=MemberPointer] [isPOD=1]
+// CHECK: VarDecl=member_pointer:49:12 (Definition) [type=int Blob::*] [typekind=MemberPointer] [isPOD=1]
Modified: cfe/trunk/tools/c-index-test/c-index-test.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/c-index-test/c-index-test.c?rev=234762&r1=234761&r2=234762&view=diff
==============================================================================
--- cfe/trunk/tools/c-index-test/c-index-test.c (original)
+++ cfe/trunk/tools/c-index-test/c-index-test.c Mon Apr 13 11:55:04 2015
@@ -1250,6 +1250,12 @@ static void PrintTypeAndTypeKind(CXType
clang_disposeString(TypeKindSpelling);
}
+static enum CXVisitorResult FieldVisitor(CXCursor C,
+ CXClientData client_data) {
+ (*(int *) client_data)+=1;
+ return CXVisit_Continue;
+}
+
static enum CXChildVisitResult PrintType(CXCursor cursor, CXCursor p,
CXClientData d) {
if (!clang_isInvalid(clang_getCursorKind(cursor))) {
@@ -1320,6 +1326,22 @@ static enum CXChildVisitResult PrintType
PrintTypeAndTypeKind(PT, " [pointeetype=%s] [pointeekind=%s]");
}
}
+ /* Print the number of fields if they exist. */
+ {
+ int numFields = 0;
+ if (clang_Type_visitFields(T, FieldVisitor, &numFields)){
+ if (numFields != 0) {
+ printf(" [nbFields=%d]", numFields);
+ }
+ /* Print if it is an anonymous record. */
+ {
+ unsigned isAnon = clang_Cursor_isAnonymous(cursor);
+ if (isAnon != 0) {
+ printf(" [isAnon=%d]", isAnon);
+ }
+ }
+ }
+ }
printf("\n");
}
@@ -1353,28 +1375,29 @@ static enum CXChildVisitResult PrintType
{
CXString FieldSpelling = clang_getCursorSpelling(cursor);
const char *FieldName = clang_getCString(FieldSpelling);
- /* recurse to get the root anonymous record parent */
- CXCursor Parent, Root;
+ /* recurse to get the first parent record that is not anonymous. */
+ CXCursor Parent, Record;
+ unsigned RecordIsAnonymous = 0;
if (clang_getCursorKind(cursor) == CXCursor_FieldDecl) {
- CXString RootParentSpelling;
- const char *RootParentName = 0;
- Parent = p;
+ Record = Parent = p;
do {
- if (RootParentName != 0)
- clang_disposeString(RootParentSpelling);
-
- Root = Parent;
- RootParentSpelling = clang_getCursorSpelling(Root);
- RootParentName = clang_getCString(RootParentSpelling);
- Parent = clang_getCursorSemanticParent(Root);
- } while (clang_getCursorType(Parent).kind == CXType_Record &&
- !strcmp(RootParentName, ""));
- clang_disposeString(RootParentSpelling);
- /* if RootParentName is "", record is anonymous. */
+ Record = Parent;
+ Parent = clang_getCursorSemanticParent(Record);
+ RecordIsAnonymous = clang_Cursor_isAnonymous(Record);
+ /* Recurse as long as the parent is a CXType_Record and the Record
+ is anonymous */
+ } while ( clang_getCursorType(Parent).kind == CXType_Record &&
+ RecordIsAnonymous > 0);
{
- long long Offset = clang_Type_getOffsetOf(clang_getCursorType(Root),
+ long long Offset = clang_Type_getOffsetOf(clang_getCursorType(Record),
FieldName);
- printf(" [offsetof=%lld]", Offset);
+ long long Offset2 = clang_Cursor_getOffsetOfField(cursor);
+ if (Offset == Offset2){
+ printf(" [offsetof=%lld]", Offset);
+ } else {
+ /* Offsets will be different in anonymous records. */
+ printf(" [offsetof=%lld/%lld]", Offset, Offset2);
+ }
}
}
clang_disposeString(FieldSpelling);
Modified: cfe/trunk/tools/libclang/CXType.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/libclang/CXType.cpp?rev=234762&r1=234761&r2=234762&view=diff
==============================================================================
--- cfe/trunk/tools/libclang/CXType.cpp (original)
+++ cfe/trunk/tools/libclang/CXType.cpp Mon Apr 13 11:55:04 2015
@@ -775,13 +775,12 @@ static long long visitRecordForValidatio
return 0;
}
-long long clang_Type_getOffsetOf(CXType PT, const char *S) {
- // check that PT is not incomplete/dependent
- CXCursor PC = clang_getTypeDeclaration(PT);
+static long long validateFieldParentType(CXCursor PC, CXType PT){
if (clang_isInvalid(PC.kind))
return CXTypeLayoutError_Invalid;
const RecordDecl *RD =
dyn_cast_or_null<RecordDecl>(cxcursor::getCursorDecl(PC));
+ // validate parent declaration
if (!RD || RD->isInvalidDecl())
return CXTypeLayoutError_Invalid;
RD = RD->getDefinition();
@@ -789,6 +788,7 @@ long long clang_Type_getOffsetOf(CXType
return CXTypeLayoutError_Incomplete;
if (RD->isInvalidDecl())
return CXTypeLayoutError_Invalid;
+ // validate parent type
QualType RT = GetQualType(PT);
if (RT->isIncompleteType())
return CXTypeLayoutError_Incomplete;
@@ -798,12 +798,25 @@ long long clang_Type_getOffsetOf(CXType
long long Error = visitRecordForValidation(RD);
if (Error < 0)
return Error;
+ return 0;
+}
+
+long long clang_Type_getOffsetOf(CXType PT, const char *S) {
+ // check that PT is not incomplete/dependent
+ CXCursor PC = clang_getTypeDeclaration(PT);
+ long long Error = validateFieldParentType(PC,PT);
+ if (Error < 0)
+ return Error;
if (!S)
return CXTypeLayoutError_InvalidFieldName;
// lookup field
ASTContext &Ctx = cxtu::getASTUnit(GetTU(PT))->getASTContext();
IdentifierInfo *II = &Ctx.Idents.get(S);
DeclarationName FieldName(II);
+ const RecordDecl *RD =
+ dyn_cast_or_null<RecordDecl>(cxcursor::getCursorDecl(PC));
+ // verified in validateFieldParentType
+ RD = RD->getDefinition();
RecordDecl::lookup_result Res = RD->lookup(FieldName);
// If a field of the parent record is incomplete, lookup will fail.
// and we would return InvalidFieldName instead of Incomplete.
@@ -819,6 +832,25 @@ long long clang_Type_getOffsetOf(CXType
return CXTypeLayoutError_InvalidFieldName;
}
+long long clang_Cursor_getOffsetOfField(CXCursor C) {
+ if (clang_isDeclaration(C.kind)) {
+ // we need to validate the parent type
+ CXCursor PC = clang_getCursorSemanticParent(C);
+ CXType PT = clang_getCursorType(PC);
+ long long Error = validateFieldParentType(PC,PT);
+ if (Error < 0)
+ return Error;
+ // proceed with the offset calculation
+ const Decl *D = cxcursor::getCursorDecl(C);
+ ASTContext &Ctx = cxcursor::getCursorContext(C);
+ if (const FieldDecl *FD = dyn_cast_or_null<FieldDecl>(D))
+ return Ctx.getFieldOffset(FD);
+ if (const IndirectFieldDecl *IFD = dyn_cast_or_null<IndirectFieldDecl>(D))
+ return Ctx.getFieldOffset(IFD);
+ }
+ return -1;
+}
+
enum CXRefQualifierKind clang_Type_getCXXRefQualifier(CXType T) {
QualType QT = GetQualType(T);
if (QT.isNull())
@@ -908,4 +940,41 @@ CXType clang_Type_getTemplateArgumentAsT
return MakeCXType(A.getAsType(), GetTU(CT));
}
+unsigned clang_Type_visitFields(CXType PT,
+ CXFieldVisitor visitor,
+ CXClientData client_data){
+ CXCursor PC = clang_getTypeDeclaration(PT);
+ if (clang_isInvalid(PC.kind))
+ return false;
+ const RecordDecl *RD =
+ dyn_cast_or_null<RecordDecl>(cxcursor::getCursorDecl(PC));
+ if (!RD || RD->isInvalidDecl())
+ return false;
+ RD = RD->getDefinition();
+ if (!RD || RD->isInvalidDecl())
+ return false;
+
+ for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end();
+ I != E; ++I){
+ const FieldDecl *FD = dyn_cast_or_null<FieldDecl>((*I));
+ // Callback to the client.
+ switch (visitor(cxcursor::MakeCXCursor(FD, GetTU(PT)), client_data)){
+ case CXVisit_Break:
+ return true;
+ case CXVisit_Continue:
+ break;
+ }
+ }
+ return true;
+}
+
+unsigned clang_Cursor_isAnonymous(CXCursor C){
+ if (!clang_isDeclaration(C.kind))
+ return 0;
+ const Decl *D = cxcursor::getCursorDecl(C);
+ if (const RecordDecl *FD = dyn_cast_or_null<RecordDecl>(D))
+ return FD->isAnonymousStructOrUnion();
+ return 0;
+}
+
} // end: extern "C"
Modified: cfe/trunk/tools/libclang/libclang.exports
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/libclang/libclang.exports?rev=234762&r1=234761&r2=234762&view=diff
==============================================================================
--- cfe/trunk/tools/libclang/libclang.exports (original)
+++ cfe/trunk/tools/libclang/libclang.exports Mon Apr 13 11:55:04 2015
@@ -21,9 +21,11 @@ clang_Cursor_getNumArguments
clang_Cursor_getObjCDeclQualifiers
clang_Cursor_getObjCPropertyAttributes
clang_Cursor_getObjCSelectorIndex
+clang_Cursor_getOffsetOfField
clang_Cursor_getSpellingNameRange
clang_Cursor_getTranslationUnit
clang_Cursor_getReceiverType
+clang_Cursor_isAnonymous
clang_Cursor_isBitField
clang_Cursor_isDynamicCall
clang_Cursor_isNull
@@ -77,6 +79,7 @@ clang_Type_getOffsetOf
clang_Type_getNumTemplateArguments
clang_Type_getTemplateArgumentAsType
clang_Type_getCXXRefQualifier
+clang_Type_visitFields
clang_VerbatimBlockLineComment_getText
clang_VerbatimLineComment_getText
clang_HTMLTagComment_getAsString
More information about the cfe-commits
mailing list