[cfe-commits] r141861 - in /cfe/trunk: include/clang-c/Index.h include/clang/Basic/DiagnosticSemaKinds.td include/clang/Parse/Parser.h include/clang/Sema/Sema.h lib/Parse/ParseCXXInlineMethods.cpp lib/Parse/ParseDeclCXX.cpp lib/Parse/ParseTemplate.cpp lib/Sema/SemaDeclAttr.cpp lib/Sema/SemaDeclCXX.cpp test/Index/annotate-attribute.cpp test/Parser/access-spec-attrs.cpp tools/libclang/CIndex.cpp tools/libclang/CXCursor.cpp

Erik Verbruggen erikjv at me.com
Thu Oct 13 02:41:32 PDT 2011


Author: erikjv
Date: Thu Oct 13 04:41:32 2011
New Revision: 141861

URL: http://llvm.org/viewvc/llvm-project?rev=141861&view=rev
Log:
Allow for annotate attributes after access specifiers. When such
attributes are found, propagate them to subsequent declarations.


Added:
    cfe/trunk/test/Index/annotate-attribute.cpp
    cfe/trunk/test/Parser/access-spec-attrs.cpp
Modified:
    cfe/trunk/include/clang-c/Index.h
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Parse/Parser.h
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp
    cfe/trunk/lib/Parse/ParseDeclCXX.cpp
    cfe/trunk/lib/Parse/ParseTemplate.cpp
    cfe/trunk/lib/Sema/SemaDeclAttr.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/tools/libclang/CIndex.cpp
    cfe/trunk/tools/libclang/CXCursor.cpp

Modified: cfe/trunk/include/clang-c/Index.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang-c/Index.h?rev=141861&r1=141860&r2=141861&view=diff
==============================================================================
--- cfe/trunk/include/clang-c/Index.h (original)
+++ cfe/trunk/include/clang-c/Index.h Thu Oct 13 04:41:32 2011
@@ -1830,7 +1830,8 @@
   CXCursor_IBOutletCollectionAttr        = 403,
   CXCursor_CXXFinalAttr                  = 404,
   CXCursor_CXXOverrideAttr               = 405,
-  CXCursor_LastAttr                      = CXCursor_CXXOverrideAttr,
+  CXCursor_AnnotateAttr                  = 406,
+  CXCursor_LastAttr                      = CXCursor_AnnotateAttr,
      
   /* Preprocessing */
   CXCursor_PreprocessingDirective        = 500,

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=141861&r1=141860&r2=141861&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Thu Oct 13 04:41:32 2011
@@ -1365,6 +1365,8 @@
   "the type %0 already has retainment attributes set on it">;
 def err_attribute_not_string : Error<
   "argument to %0 attribute was not a string literal">;
+def err_only_annotate_after_access_spec : Error<
+  "access specifier can only have annotation attributes">;
 def err_attribute_section_invalid_for_target : Error<
   "argument to 'section' attribute is not valid for this target: %0">;
 def err_attribute_section_local_variable : Error<

Modified: cfe/trunk/include/clang/Parse/Parser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=141861&r1=141860&r2=141861&view=diff
==============================================================================
--- cfe/trunk/include/clang/Parse/Parser.h (original)
+++ cfe/trunk/include/clang/Parse/Parser.h Thu Oct 13 04:41:32 2011
@@ -1125,7 +1125,8 @@
   void DeallocateParsedClasses(ParsingClass *Class);
   void PopParsingClass(Sema::ParsingClassState);
 
-  Decl *ParseCXXInlineMethodDef(AccessSpecifier AS, ParsingDeclarator &D,
+  Decl *ParseCXXInlineMethodDef(AccessSpecifier AS, AttributeList *AccessAttrs,
+                                ParsingDeclarator &D,
                                 const ParsedTemplateInfo &TemplateInfo,
                                 const VirtSpecifiers& VS, ExprResult& Init);
   void ParseCXXNonStaticMemberInitializer(Decl *VarD);
@@ -1961,7 +1962,7 @@
                                    Decl *TagDecl);
   ExprResult ParseCXXMemberInitializer(bool IsFunction,
                                        SourceLocation &EqualLoc);
-  void ParseCXXClassMemberDeclaration(AccessSpecifier AS,
+  void ParseCXXClassMemberDeclaration(AccessSpecifier AS, AttributeList *Attr,
                 const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(),
                                  ParsingDeclRAIIObject *DiagsFromTParams = 0);
   void ParseConstructorInitializer(Decl *ConstructorDecl);
@@ -1997,17 +1998,20 @@
 
   // C++ 14.1: Template Parameters [temp.param]
   Decl *ParseDeclarationStartingWithTemplate(unsigned Context,
-                                                 SourceLocation &DeclEnd,
-                                                 AccessSpecifier AS = AS_none);
+                                             SourceLocation &DeclEnd,
+                                             AccessSpecifier AS = AS_none,
+                                             AttributeList *AccessAttrs = 0);
   Decl *ParseTemplateDeclarationOrSpecialization(unsigned Context,
-                                                     SourceLocation &DeclEnd,
-                                                     AccessSpecifier AS);
+                                                 SourceLocation &DeclEnd,
+                                                 AccessSpecifier AS,
+                                                 AttributeList *AccessAttrs);
   Decl *ParseSingleDeclarationAfterTemplate(
                                        unsigned Context,
                                        const ParsedTemplateInfo &TemplateInfo,
                                        ParsingDeclRAIIObject &DiagsFromParams,
                                        SourceLocation &DeclEnd,
-                                       AccessSpecifier AS=AS_none);
+                                       AccessSpecifier AS=AS_none,
+                                       AttributeList *AccessAttrs = 0);
   bool ParseTemplateParameters(unsigned Depth,
                                SmallVectorImpl<Decl*> &TemplateParams,
                                SourceLocation &LAngleLoc,

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=141861&r1=141860&r2=141861&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Thu Oct 13 04:41:32 2011
@@ -1838,6 +1838,8 @@
                            bool NonInheritable = true, bool Inheritable = true);
   void ProcessDeclAttributeList(Scope *S, Decl *D, const AttributeList *AL,
                            bool NonInheritable = true, bool Inheritable = true);
+  bool ProcessAccessDeclAttributeList(AccessSpecDecl *ASDecl,
+                                      const AttributeList *AttrList);
 
   void checkUnusedDeclAttributes(Declarator &D);
 
@@ -3414,9 +3416,10 @@
   bool isCurrentClassName(const IdentifierInfo &II, Scope *S,
                           const CXXScopeSpec *SS = 0);
 
-  Decl *ActOnAccessSpecifier(AccessSpecifier Access,
-                             SourceLocation ASLoc,
-                             SourceLocation ColonLoc);
+  bool ActOnAccessSpecifier(AccessSpecifier Access,
+                            SourceLocation ASLoc,
+                            SourceLocation ColonLoc,
+                            AttributeList *Attrs = 0);
 
   Decl *ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS,
                                  Declarator &D,

Modified: cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp?rev=141861&r1=141860&r2=141861&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp (original)
+++ cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp Thu Oct 13 04:41:32 2011
@@ -21,7 +21,9 @@
 /// ParseCXXInlineMethodDef - We parsed and verified that the specified
 /// Declarator is a well formed C++ inline method definition. Now lex its body
 /// and store its tokens for parsing after the C++ class is complete.
-Decl *Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, ParsingDeclarator &D,
+Decl *Parser::ParseCXXInlineMethodDef(AccessSpecifier AS,
+                                      AttributeList *AccessAttrs,
+                                      ParsingDeclarator &D,
                                 const ParsedTemplateInfo &TemplateInfo,
                                 const VirtSpecifiers& VS, ExprResult& Init) {
   assert(D.isFunctionDeclarator() && "This isn't a function declarator!");
@@ -43,6 +45,8 @@
                                            move(TemplateParams), 0, 
                                            VS, /*HasInit=*/false);
     if (FnD) {
+      Actions.ProcessDeclAttributeList(getCurScope(), FnD, AccessAttrs,
+                                       false, true);
       bool TypeSpecContainsAuto
         = D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto;
       if (Init.get())

Modified: cfe/trunk/lib/Parse/ParseDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDeclCXX.cpp?rev=141861&r1=141860&r2=141861&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Thu Oct 13 04:41:32 2011
@@ -1577,6 +1577,7 @@
 ///         '=' constant-expression
 ///
 void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
+                                            AttributeList *AccessAttrs,
                                        const ParsedTemplateInfo &TemplateInfo,
                                        ParsingDeclRAIIObject *TemplateDiags) {
   if (Tok.is(tok::at)) {
@@ -1643,7 +1644,7 @@
            "Nested template improperly parsed?");
     SourceLocation DeclEnd;
     ParseDeclarationStartingWithTemplate(Declarator::MemberContext, DeclEnd,
-                                         AS);
+                                         AS, AccessAttrs);
     return;
   }
 
@@ -1652,7 +1653,8 @@
     // __extension__ silences extension warnings in the subexpression.
     ExtensionRAIIObject O(Diags);  // Use RAII to do this.
     ConsumeToken();
-    return ParseCXXClassMemberDeclaration(AS, TemplateInfo, TemplateDiags);
+    return ParseCXXClassMemberDeclaration(AS, AccessAttrs,
+                                          TemplateInfo, TemplateDiags);
   }
 
   // Don't parse FOO:BAR as if it were a typo for FOO::BAR, in this context it
@@ -1783,7 +1785,8 @@
       }
 
       Decl *FunDecl =
-        ParseCXXInlineMethodDef(AS, DeclaratorInfo, TemplateInfo, VS, Init);
+        ParseCXXInlineMethodDef(AS, AccessAttrs, DeclaratorInfo, TemplateInfo,
+                                VS, Init);
 
       for (unsigned i = 0, ni = LateParsedAttrs.size(); i < ni; ++i) {
         LateParsedAttrs[i]->setDecl(FunDecl);
@@ -1867,6 +1870,9 @@
                                                   move(TemplateParams),
                                                   BitfieldSize.release(),
                                                   VS, HasDeferredInitializer);
+      if (AccessAttrs)
+        Actions.ProcessDeclAttributeList(getCurScope(), ThisDecl, AccessAttrs,
+                                         false, true);
     }
     
     // Set the Decl for any late parsed attributes
@@ -2109,6 +2115,7 @@
     CurAS = AS_private;
   else
     CurAS = AS_public;
+  ParsedAttributes AccessAttrs(AttrFactory);
 
   if (TagDecl) {
     // While we still have something to read, read the member-declarations.
@@ -2137,9 +2144,17 @@
         SourceLocation ASLoc = Tok.getLocation();
         unsigned TokLength = Tok.getLength();
         ConsumeToken();
+        AccessAttrs.clear();
+        MaybeParseGNUAttributes(AccessAttrs);
+
         SourceLocation EndLoc;
         if (Tok.is(tok::colon)) {
           EndLoc = Tok.getLocation();
+          if (Actions.ActOnAccessSpecifier(AS, ASLoc, EndLoc,
+                                           AccessAttrs.getList())) {
+            // found another attribute than only annotations
+            AccessAttrs.clear();
+          }
           ConsumeToken();
         } else if (Tok.is(tok::semi)) {
           EndLoc = Tok.getLocation();
@@ -2158,7 +2173,7 @@
       // FIXME: Make sure we don't have a template here.
 
       // Parse all the comma separated declarators.
-      ParseCXXClassMemberDeclaration(CurAS);
+      ParseCXXClassMemberDeclaration(CurAS, AccessAttrs.getList());
     }
 
     T.consumeClose();
@@ -2779,7 +2794,7 @@
     }
 
     // Parse all the comma separated declarators.
-    ParseCXXClassMemberDeclaration(CurAS);
+    ParseCXXClassMemberDeclaration(CurAS, 0);
   }
 
   if (Tok.isNot(tok::r_brace)) {

Modified: cfe/trunk/lib/Parse/ParseTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseTemplate.cpp?rev=141861&r1=141860&r2=141861&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseTemplate.cpp (original)
+++ cfe/trunk/lib/Parse/ParseTemplate.cpp Thu Oct 13 04:41:32 2011
@@ -26,14 +26,16 @@
 Decl *
 Parser::ParseDeclarationStartingWithTemplate(unsigned Context,
                                              SourceLocation &DeclEnd,
-                                             AccessSpecifier AS) {
+                                             AccessSpecifier AS,
+                                             AttributeList *AccessAttrs) {
   ObjCDeclContextSwitch ObjCDC(*this);
   
   if (Tok.is(tok::kw_template) && NextToken().isNot(tok::less)) {
     return ParseExplicitInstantiation(SourceLocation(), ConsumeToken(),
                                            DeclEnd);
   }
-  return ParseTemplateDeclarationOrSpecialization(Context, DeclEnd, AS);
+  return ParseTemplateDeclarationOrSpecialization(Context, DeclEnd, AS,
+                                                  AccessAttrs);
 }
 
 /// \brief RAII class that manages the template parameter depth.
@@ -77,7 +79,8 @@
 Decl *
 Parser::ParseTemplateDeclarationOrSpecialization(unsigned Context,
                                                  SourceLocation &DeclEnd,
-                                                 AccessSpecifier AS) {
+                                                 AccessSpecifier AS,
+                                                 AttributeList *AccessAttrs) {
   assert((Tok.is(tok::kw_export) || Tok.is(tok::kw_template)) &&
          "Token does not start a template declaration.");
 
@@ -161,7 +164,7 @@
                                                              isSpecialization,
                                                          LastParamListWasEmpty),
                                              ParsingTemplateParams,
-                                             DeclEnd, AS);
+                                             DeclEnd, AS, AccessAttrs);
 }
 
 /// \brief Parse a single declaration that declares a template,
@@ -190,13 +193,15 @@
                                        const ParsedTemplateInfo &TemplateInfo,
                                        ParsingDeclRAIIObject &DiagsFromTParams,
                                        SourceLocation &DeclEnd,
-                                       AccessSpecifier AS) {
+                                       AccessSpecifier AS,
+                                       AttributeList *AccessAttrs) {
   assert(TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate &&
          "Template information required");
 
   if (Context == Declarator::MemberContext) {
     // We are parsing a member template.
-    ParseCXXClassMemberDeclaration(AS, TemplateInfo, &DiagsFromTParams);
+    ParseCXXClassMemberDeclaration(AS, AccessAttrs, TemplateInfo,
+                                   &DiagsFromTParams);
     return 0;
   }
 

Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=141861&r1=141860&r2=141861&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Thu Oct 13 04:41:32 2011
@@ -3743,6 +3743,22 @@
   }
 }
 
+// Annotation attributes are the only attributes allowed after an access
+// specifier.
+bool Sema::ProcessAccessDeclAttributeList(AccessSpecDecl *ASDecl,
+                                          const AttributeList *AttrList) {
+  for (const AttributeList* l = AttrList; l; l = l->getNext()) {
+    if (l->getKind() == AttributeList::AT_annotate) {
+      handleAnnotateAttr(*this, ASDecl, *l);
+    } else {
+      Diag(l->getLoc(), diag::err_only_annotate_after_access_spec);
+      return true;
+    }
+  }
+
+  return false;
+}
+
 /// checkUnusedDeclAttributes - Check a list of attributes to see if it
 /// contains any decl attributes that we should warn about.
 static void checkUnusedDeclAttributes(Sema &S, const AttributeList *A) {

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=141861&r1=141860&r2=141861&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Thu Oct 13 04:41:32 2011
@@ -1376,14 +1376,15 @@
 //===----------------------------------------------------------------------===//
 
 /// ActOnAccessSpecifier - Parsed an access specifier followed by a colon.
-Decl *Sema::ActOnAccessSpecifier(AccessSpecifier Access,
-                                 SourceLocation ASLoc,
-                                 SourceLocation ColonLoc) {
+bool Sema::ActOnAccessSpecifier(AccessSpecifier Access,
+                                SourceLocation ASLoc,
+                                SourceLocation ColonLoc,
+                                AttributeList *Attrs) {
   assert(Access != AS_none && "Invalid kind for syntactic access specifier!");
   AccessSpecDecl *ASDecl = AccessSpecDecl::Create(Context, Access, CurContext,
                                                   ASLoc, ColonLoc);
   CurContext->addHiddenDecl(ASDecl);
-  return ASDecl;
+  return ProcessAccessDeclAttributeList(ASDecl, Attrs);
 }
 
 /// CheckOverrideControl - Check C++0x override control semantics.

Added: cfe/trunk/test/Index/annotate-attribute.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Index/annotate-attribute.cpp?rev=141861&view=auto
==============================================================================
--- cfe/trunk/test/Index/annotate-attribute.cpp (added)
+++ cfe/trunk/test/Index/annotate-attribute.cpp Thu Oct 13 04:41:32 2011
@@ -0,0 +1,33 @@
+// RUN: c-index-test -test-load-source all %s | FileCheck %s
+
+class Test {
+public:
+  __attribute__((annotate("spiffy_method"))) void aMethod();
+
+public __attribute__((annotate("works"))):
+  void anotherMethod(); // annotation attribute should be propagated.
+
+private __attribute__((annotate("investigations"))):
+  //propagated annotation should have changed from "works" to "investigations"
+  void inlineMethod() {}
+
+protected:
+  // attribute propagation should have stopped here
+  void methodWithoutAttribute();
+};
+
+// CHECK: ClassDecl=Test:3:7 (Definition) Extent=[3:1 - 17:2]
+// CHECK: CXXAccessSpecifier=:4:1 (Definition) Extent=[4:1 - 4:8]
+// CHECK: CXXMethod=aMethod:5:51 Extent=[5:3 - 5:60]
+// CHECK: attribute(annotate)=spiffy_method Extent=[5:18 - 5:43]
+// CHECK: CXXAccessSpecifier=:7:1 (Definition) Extent=[7:1 - 7:43]
+// CHECK: attribute(annotate)=works Extent=[7:23 - 7:40]
+// CHECK: CXXMethod=anotherMethod:8:8 Extent=[8:3 - 8:23]
+// CHECK: attribute(annotate)=works Extent=[7:23 - 7:40]
+// CHECK: CXXAccessSpecifier=:10:1 (Definition) Extent=[10:1 - 10:53]
+// CHECK: attribute(annotate)=investigations Extent=[10:24 - 10:50]
+// CHECK: CXXMethod=inlineMethod:12:8 (Definition) Extent=[12:3 - 12:25]
+// CHECK: attribute(annotate)=investigations Extent=[10:24 - 10:50]
+// CHECK: CompoundStmt= Extent=[12:23 - 12:25]
+// CHECK: CXXAccessSpecifier=:14:1 (Definition) Extent=[14:1 - 14:11]
+// CHECK: CXXMethod=methodWithoutAttribute:16:8 Extent=[16:3 - 16:32]

Added: cfe/trunk/test/Parser/access-spec-attrs.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/access-spec-attrs.cpp?rev=141861&view=auto
==============================================================================
--- cfe/trunk/test/Parser/access-spec-attrs.cpp (added)
+++ cfe/trunk/test/Parser/access-spec-attrs.cpp Thu Oct 13 04:41:32 2011
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 %s -fsyntax-only -verify
+
+struct X {
+public __attribute__((unavailable)): // expected-error {{access specifier can only have annotation attributes}}
+  void foo();
+private __attribute__((annotate("foobar"))):
+    void bar();
+};
+
+void f(X x) {
+  x.foo();
+}

Modified: cfe/trunk/tools/libclang/CIndex.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/libclang/CIndex.cpp?rev=141861&r1=141860&r2=141861&view=diff
==============================================================================
--- cfe/trunk/tools/libclang/CIndex.cpp (original)
+++ cfe/trunk/tools/libclang/CIndex.cpp Thu Oct 13 04:41:32 2011
@@ -3200,6 +3200,11 @@
   if (clang_isDeclaration(C.kind))
     return getDeclSpelling(getCursorDecl(C));
 
+  if (C.kind == CXCursor_AnnotateAttr) {
+    AnnotateAttr *AA = cast<AnnotateAttr>(cxcursor::getCursorAttr(C));
+    return createCXString(AA->getAnnotation());
+  }
+
   return createCXString("");
 }
 
@@ -3521,6 +3526,8 @@
       return createCXString("attribute(final)");
   case CXCursor_CXXOverrideAttr:
       return createCXString("attribute(override)");
+  case CXCursor_AnnotateAttr:
+    return createCXString("attribute(annotate)");
   case CXCursor_PreprocessingDirective:
     return createCXString("preprocessing directive");
   case CXCursor_MacroDefinition:

Modified: cfe/trunk/tools/libclang/CXCursor.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/libclang/CXCursor.cpp?rev=141861&r1=141860&r2=141861&view=diff
==============================================================================
--- cfe/trunk/tools/libclang/CXCursor.cpp (original)
+++ cfe/trunk/tools/libclang/CXCursor.cpp Thu Oct 13 04:41:32 2011
@@ -45,6 +45,7 @@
     case attr::IBOutletCollection: return CXCursor_IBOutletCollectionAttr;
     case attr::Final: return CXCursor_CXXFinalAttr;
     case attr::Override: return CXCursor_CXXOverrideAttr;
+    case attr::Annotate: return CXCursor_AnnotateAttr;
   }
 
   return CXCursor_UnexposedAttr;





More information about the cfe-commits mailing list