[clang] [BoundsSafety][NFC] Move LateParsedAttribute outside Parser class; move LateParsedAttrList to DeclSpec.h (PR #192145)

Yeoul Na via cfe-commits cfe-commits at lists.llvm.org
Sat Apr 18 09:30:57 PDT 2026


https://github.com/rapidsna updated https://github.com/llvm/llvm-project/pull/192145

>From 844fc9b65126aa307d679e76a876368a15d65cb9 Mon Sep 17 00:00:00 2001
From: Yeoul Na <yeoul_na at apple.com>
Date: Tue, 14 Apr 2026 12:26:18 -0700
Subject: [PATCH 1/2] [BoundsSafety][NFC] Move LateParsedAttribute outside
 Parser class; move LateParsedAttrList to DeclSpec.h

Preparatory refactoring for llvm/llvm-project#179612, which introduces
late parsing of bounds-safety attributes as type attributes. The new
approach needs LateParsedAttribute accessible from DeclSpec.h (to store
late attr pointers in DeclaratorChunk, Declarator, and DeclSpec), which
cannot depend on Parser.h.

- Move LateParsedDeclaration and LateParsedAttribute to namespace level in Parser.h
- Move LateParsedAttrList to DeclSpec.h with a forward declaration of LateParsedAttribute

Other LateParsedDeclaration subclasses (LateParsedClass, LateParsedPragma,
LateParsedMemberInitializer, etc.) remain inside Parser as they are only
created and consumed within Parser and don't need to cross the Parser/Sema
boundary.
---
 clang/include/clang/Parse/Parser.h        | 107 +++++++++-------------
 clang/include/clang/Sema/DeclSpec.h       |  21 +++++
 clang/lib/Parse/ParseCXXInlineMethods.cpp |  14 +--
 3 files changed, 72 insertions(+), 70 deletions(-)

diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index c077671cb2407..412fe5f13d844 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -164,6 +164,48 @@ enum class CXX11AttributeKind {
   InvalidAttributeSpecifier
 };
 
+/// [class.mem]p1: "... the class is regarded as complete within
+/// - function bodies
+/// - default arguments
+/// - exception-specifications (TODO: C++0x)
+/// - and brace-or-equal-initializers for non-static data members
+/// (including such things in nested classes)."
+/// LateParsedDeclarations build the tree of those elements so they can
+/// be parsed after parsing the top-level class.
+class LateParsedDeclaration {
+public:
+  virtual ~LateParsedDeclaration();
+
+  virtual void ParseLexedMethodDeclarations();
+  virtual void ParseLexedMemberInitializers();
+  virtual void ParseLexedMethodDefs();
+  virtual void ParseLexedAttributes();
+  virtual void ParseLexedPragmas();
+};
+
+/// Contains the lexed tokens of an attribute with arguments that
+/// may reference member variables and so need to be parsed at the
+/// end of the class declaration after parsing all other member
+/// member declarations.
+/// FIXME: Perhaps we should change the name of LateParsedDeclaration to
+/// LateParsedTokens.
+struct LateParsedAttribute : public LateParsedDeclaration {
+  Parser *Self;
+  CachedTokens Toks;
+  IdentifierInfo &AttrName;
+  IdentifierInfo *MacroII = nullptr;
+  SourceLocation AttrNameLoc;
+  SmallVector<Decl *, 2> Decls;
+
+  explicit LateParsedAttribute(Parser *P, IdentifierInfo &Name,
+                               SourceLocation Loc)
+      : Self(P), AttrName(Name), AttrNameLoc(Loc) {}
+
+  void ParseLexedAttributes() override;
+
+  void addDecl(Decl *D) { Decls.push_back(D); }
+};
+
 /// Parser - This implements a parser for the C family of languages.  After
 /// parsing units of the grammar, productions are invoked to handle whatever has
 /// been read.
@@ -950,7 +992,6 @@ class Parser : public CodeCompletionHandler {
   void SkipFunctionBody();
 
   struct ParsedTemplateInfo;
-  class LateParsedAttrList;
 
   /// ParseFunctionDefinition - We parsed and verified that the specified
   /// Declarator is well formed.  If this is a K&R-style function, read the
@@ -1118,26 +1159,9 @@ class Parser : public CodeCompletionHandler {
   ///@{
 
 private:
-  struct ParsingClass;
+  friend struct LateParsedAttribute;
 
-  /// [class.mem]p1: "... the class is regarded as complete within
-  /// - function bodies
-  /// - default arguments
-  /// - exception-specifications (TODO: C++0x)
-  /// - and brace-or-equal-initializers for non-static data members
-  /// (including such things in nested classes)."
-  /// LateParsedDeclarations build the tree of those elements so they can
-  /// be parsed after parsing the top-level class.
-  class LateParsedDeclaration {
-  public:
-    virtual ~LateParsedDeclaration();
-
-    virtual void ParseLexedMethodDeclarations();
-    virtual void ParseLexedMemberInitializers();
-    virtual void ParseLexedMethodDefs();
-    virtual void ParseLexedAttributes();
-    virtual void ParseLexedPragmas();
-  };
+  struct ParsingClass;
 
   /// Inner node of the LateParsedDeclaration tree that parses
   /// all its members recursively.
@@ -1161,29 +1185,6 @@ class Parser : public CodeCompletionHandler {
     ParsingClass *Class;
   };
 
-  /// Contains the lexed tokens of an attribute with arguments that
-  /// may reference member variables and so need to be parsed at the
-  /// end of the class declaration after parsing all other member
-  /// member declarations.
-  /// FIXME: Perhaps we should change the name of LateParsedDeclaration to
-  /// LateParsedTokens.
-  struct LateParsedAttribute : public LateParsedDeclaration {
-    Parser *Self;
-    CachedTokens Toks;
-    IdentifierInfo &AttrName;
-    IdentifierInfo *MacroII = nullptr;
-    SourceLocation AttrNameLoc;
-    SmallVector<Decl *, 2> Decls;
-
-    explicit LateParsedAttribute(Parser *P, IdentifierInfo &Name,
-                                 SourceLocation Loc)
-        : Self(P), AttrName(Name), AttrNameLoc(Loc) {}
-
-    void ParseLexedAttributes() override;
-
-    void addDecl(Decl *D) { Decls.push_back(D); }
-  };
-
   /// Contains the lexed tokens of a pragma with arguments that
   /// may reference member variables and so need to be parsed at the
   /// end of the class declaration after parsing all other member
@@ -1204,26 +1205,6 @@ class Parser : public CodeCompletionHandler {
     void ParseLexedPragmas() override;
   };
 
-  // A list of late-parsed attributes.  Used by ParseGNUAttributes.
-  class LateParsedAttrList : public SmallVector<LateParsedAttribute *, 2> {
-  public:
-    LateParsedAttrList(bool PSoon = false,
-                       bool LateAttrParseExperimentalExtOnly = false)
-        : ParseSoon(PSoon),
-          LateAttrParseExperimentalExtOnly(LateAttrParseExperimentalExtOnly) {}
-
-    bool parseSoon() { return ParseSoon; }
-    /// returns true iff the attribute to be parsed should only be late parsed
-    /// if it is annotated with `LateAttrParseExperimentalExt`
-    bool lateAttrParseExperimentalExtOnly() {
-      return LateAttrParseExperimentalExtOnly;
-    }
-
-  private:
-    bool ParseSoon; // Are we planning to parse these shortly after creation?
-    bool LateAttrParseExperimentalExtOnly;
-  };
-
   /// Contains the lexed tokens of a member function definition
   /// which needs to be parsed at the end of the class declaration
   /// after parsing all other member declarations.
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 6e5421c7072c7..aff63ec56ddb2 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -50,6 +50,7 @@ namespace clang {
   class Declarator;
   class OverflowBehaviorType;
   struct TemplateIdAnnotation;
+  struct LateParsedAttribute;
 
 /// Represents a C++ nested-name-specifier or a global scope specifier.
 ///
@@ -1249,6 +1250,26 @@ class UnqualifiedId {
 /// A set of tokens that has been cached for later parsing.
 typedef SmallVector<Token, 4> CachedTokens;
 
+// A list of late-parsed attributes.  Used by ParseGNUAttributes.
+class LateParsedAttrList : public SmallVector<LateParsedAttribute *, 2> {
+public:
+  LateParsedAttrList(bool PSoon = false,
+                     bool LateAttrParseExperimentalExtOnly = false)
+      : ParseSoon(PSoon),
+        LateAttrParseExperimentalExtOnly(LateAttrParseExperimentalExtOnly) {}
+
+  bool parseSoon() { return ParseSoon; }
+  /// returns true iff the attribute to be parsed should only be late parsed
+  /// if it is annotated with `LateAttrParseExperimentalExt`
+  bool lateAttrParseExperimentalExtOnly() {
+    return LateAttrParseExperimentalExtOnly;
+  }
+
+private:
+  bool ParseSoon; // Are we planning to parse these shortly after creation?
+  bool LateAttrParseExperimentalExtOnly;
+};
+
 /// One instance of this struct is used for each type in a
 /// declarator that is parsed.
 ///
diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp
index bc18881e89110..bea7d9e55a77d 100644
--- a/clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -268,12 +268,12 @@ void Parser::ParseCXXNonStaticMemberInitializer(Decl *VarD) {
   Toks.push_back(Eof);
 }
 
-Parser::LateParsedDeclaration::~LateParsedDeclaration() {}
-void Parser::LateParsedDeclaration::ParseLexedMethodDeclarations() {}
-void Parser::LateParsedDeclaration::ParseLexedMemberInitializers() {}
-void Parser::LateParsedDeclaration::ParseLexedMethodDefs() {}
-void Parser::LateParsedDeclaration::ParseLexedAttributes() {}
-void Parser::LateParsedDeclaration::ParseLexedPragmas() {}
+LateParsedDeclaration::~LateParsedDeclaration() {}
+void LateParsedDeclaration::ParseLexedMethodDeclarations() {}
+void LateParsedDeclaration::ParseLexedMemberInitializers() {}
+void LateParsedDeclaration::ParseLexedMethodDefs() {}
+void LateParsedDeclaration::ParseLexedAttributes() {}
+void LateParsedDeclaration::ParseLexedPragmas() {}
 
 Parser::LateParsedClass::LateParsedClass(Parser *P, ParsingClass *C)
   : Self(P), Class(C) {}
@@ -314,7 +314,7 @@ void Parser::LateParsedMemberInitializer::ParseLexedMemberInitializers() {
   Self->ParseLexedMemberInitializer(*this);
 }
 
-void Parser::LateParsedAttribute::ParseLexedAttributes() {
+void LateParsedAttribute::ParseLexedAttributes() {
   Self->ParseLexedAttribute(*this, true, false);
 }
 

>From 066959fad453beb6d87d42320ce0b7cd7df146da Mon Sep 17 00:00:00 2001
From: Yeoul Na <yeoul_na at apple.com>
Date: Tue, 14 Apr 2026 15:19:07 -0700
Subject: [PATCH 2/2] clang-format; diffs are purely formatting change

---
 clang/include/clang/Sema/DeclSpec.h | 292 ++++++++++++++--------------
 1 file changed, 146 insertions(+), 146 deletions(-)

diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index aff63ec56ddb2..61706bc8f4229 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -52,165 +52,165 @@ namespace clang {
   struct TemplateIdAnnotation;
   struct LateParsedAttribute;
 
-/// Represents a C++ nested-name-specifier or a global scope specifier.
-///
-/// These can be in 3 states:
-///   1) Not present, identified by isEmpty()
-///   2) Present, identified by isNotEmpty()
-///      2.a) Valid, identified by isValid()
-///      2.b) Invalid, identified by isInvalid().
-///
-/// isSet() is deprecated because it mostly corresponded to "valid" but was
-/// often used as if it meant "present".
-///
-/// The actual scope is described by getScopeRep().
-///
-/// If the kind of getScopeRep() is TypeSpec then TemplateParamLists may be empty
-/// or contain the template parameter lists attached to the current declaration.
-/// Consider the following example:
-/// template <class T> void SomeType<T>::some_method() {}
-/// If CXXScopeSpec refers to SomeType<T> then TemplateParamLists will contain
-/// a single element referring to template <class T>.
-
-class CXXScopeSpec {
-  SourceRange Range;
-  NestedNameSpecifierLocBuilder Builder;
-  ArrayRef<TemplateParameterList *> TemplateParamLists;
-
-public:
-  SourceRange getRange() const { return Range; }
-  void setRange(SourceRange R) { Range = R; }
-  void setBeginLoc(SourceLocation Loc) { Range.setBegin(Loc); }
-  void setEndLoc(SourceLocation Loc) { Range.setEnd(Loc); }
-  SourceLocation getBeginLoc() const { return Range.getBegin(); }
-  SourceLocation getEndLoc() const { return Range.getEnd(); }
-
-  void setTemplateParamLists(ArrayRef<TemplateParameterList *> L) {
-    TemplateParamLists = L;
-  }
-  ArrayRef<TemplateParameterList *> getTemplateParamLists() const {
-    return TemplateParamLists;
-  }
-
-  /// Retrieve the representation of the nested-name-specifier.
-  NestedNameSpecifier getScopeRep() const {
-    return Builder.getRepresentation();
-  }
-
-  /// Make a nested-name-specifier of the form 'type::'.
+  /// Represents a C++ nested-name-specifier or a global scope specifier.
   ///
-  /// \param Context The AST context in which this nested-name-specifier
-  /// resides.
+  /// These can be in 3 states:
+  ///   1) Not present, identified by isEmpty()
+  ///   2) Present, identified by isNotEmpty()
+  ///      2.a) Valid, identified by isValid()
+  ///      2.b) Invalid, identified by isInvalid().
   ///
-  /// \param TemplateKWLoc The location of the 'template' keyword, if present.
+  /// isSet() is deprecated because it mostly corresponded to "valid" but was
+  /// often used as if it meant "present".
   ///
-  /// \param TL The TypeLoc that describes the type preceding the '::'.
+  /// The actual scope is described by getScopeRep().
   ///
-  /// \param ColonColonLoc The location of the trailing '::'.
-  void Make(ASTContext &Context, TypeLoc TL, SourceLocation ColonColonLoc);
+  /// If the kind of getScopeRep() is TypeSpec then TemplateParamLists may be
+  /// empty or contain the template parameter lists attached to the current
+  /// declaration. Consider the following example: template <class T> void
+  /// SomeType<T>::some_method() {} If CXXScopeSpec refers to SomeType<T> then
+  /// TemplateParamLists will contain a single element referring to template
+  /// <class T>.
+
+  class CXXScopeSpec {
+    SourceRange Range;
+    NestedNameSpecifierLocBuilder Builder;
+    ArrayRef<TemplateParameterList *> TemplateParamLists;
+
+  public:
+    SourceRange getRange() const { return Range; }
+    void setRange(SourceRange R) { Range = R; }
+    void setBeginLoc(SourceLocation Loc) { Range.setBegin(Loc); }
+    void setEndLoc(SourceLocation Loc) { Range.setEnd(Loc); }
+    SourceLocation getBeginLoc() const { return Range.getBegin(); }
+    SourceLocation getEndLoc() const { return Range.getEnd(); }
+
+    void setTemplateParamLists(ArrayRef<TemplateParameterList *> L) {
+      TemplateParamLists = L;
+    }
+    ArrayRef<TemplateParameterList *> getTemplateParamLists() const {
+      return TemplateParamLists;
+    }
 
-  /// Extend the current nested-name-specifier by another
-  /// nested-name-specifier component of the form 'namespace::'.
-  ///
-  /// \param Context The AST context in which this nested-name-specifier
-  /// resides.
-  ///
-  /// \param Namespace The namespace or the namespace alias.
-  ///
-  /// \param NamespaceLoc The location of the namespace name or the namespace
-  /// alias.
-  ///
-  /// \param ColonColonLoc The location of the trailing '::'.
-  void Extend(ASTContext &Context, NamespaceBaseDecl *Namespace,
-              SourceLocation NamespaceLoc, SourceLocation ColonColonLoc);
+    /// Retrieve the representation of the nested-name-specifier.
+    NestedNameSpecifier getScopeRep() const {
+      return Builder.getRepresentation();
+    }
 
-  /// Turn this (empty) nested-name-specifier into the global
-  /// nested-name-specifier '::'.
-  void MakeGlobal(ASTContext &Context, SourceLocation ColonColonLoc);
+    /// Make a nested-name-specifier of the form 'type::'.
+    ///
+    /// \param Context The AST context in which this nested-name-specifier
+    /// resides.
+    ///
+    /// \param TemplateKWLoc The location of the 'template' keyword, if present.
+    ///
+    /// \param TL The TypeLoc that describes the type preceding the '::'.
+    ///
+    /// \param ColonColonLoc The location of the trailing '::'.
+    void Make(ASTContext &Context, TypeLoc TL, SourceLocation ColonColonLoc);
 
-  /// Turns this (empty) nested-name-specifier into '__super'
-  /// nested-name-specifier.
-  ///
-  /// \param Context The AST context in which this nested-name-specifier
-  /// resides.
-  ///
-  /// \param RD The declaration of the class in which nested-name-specifier
-  /// appeared.
-  ///
-  /// \param SuperLoc The location of the '__super' keyword.
-  /// name.
-  ///
-  /// \param ColonColonLoc The location of the trailing '::'.
-  void MakeMicrosoftSuper(ASTContext &Context, CXXRecordDecl *RD,
-                          SourceLocation SuperLoc,
-                          SourceLocation ColonColonLoc);
+    /// Extend the current nested-name-specifier by another
+    /// nested-name-specifier component of the form 'namespace::'.
+    ///
+    /// \param Context The AST context in which this nested-name-specifier
+    /// resides.
+    ///
+    /// \param Namespace The namespace or the namespace alias.
+    ///
+    /// \param NamespaceLoc The location of the namespace name or the namespace
+    /// alias.
+    ///
+    /// \param ColonColonLoc The location of the trailing '::'.
+    void Extend(ASTContext &Context, NamespaceBaseDecl *Namespace,
+                SourceLocation NamespaceLoc, SourceLocation ColonColonLoc);
 
-  /// Make a new nested-name-specifier from incomplete source-location
-  /// information.
-  ///
-  /// FIXME: This routine should be used very, very rarely, in cases where we
-  /// need to synthesize a nested-name-specifier. Most code should instead use
-  /// \c Adopt() with a proper \c NestedNameSpecifierLoc.
-  void MakeTrivial(ASTContext &Context, NestedNameSpecifier Qualifier,
-                   SourceRange R);
-
-  /// Adopt an existing nested-name-specifier (with source-range
-  /// information).
-  void Adopt(NestedNameSpecifierLoc Other);
-
-  /// Retrieve a nested-name-specifier with location information, copied
-  /// into the given AST context.
-  ///
-  /// \param Context The context into which this nested-name-specifier will be
-  /// copied.
-  NestedNameSpecifierLoc getWithLocInContext(ASTContext &Context) const;
+    /// Turn this (empty) nested-name-specifier into the global
+    /// nested-name-specifier '::'.
+    void MakeGlobal(ASTContext &Context, SourceLocation ColonColonLoc);
 
-  /// Retrieve the location of the name in the last qualifier
-  /// in this nested name specifier.
-  ///
-  /// For example, the location of \c bar
-  /// in
-  /// \verbatim
-  ///   \::foo::bar<0>::
-  ///           ^~~
-  /// \endverbatim
-  SourceLocation getLastQualifierNameLoc() const;
-
-  /// No scope specifier.
-  bool isEmpty() const { return Range.isInvalid() && !getScopeRep(); }
-  /// A scope specifier is present, but may be valid or invalid.
-  bool isNotEmpty() const { return !isEmpty(); }
-
-  /// An error occurred during parsing of the scope specifier.
-  bool isInvalid() const { return Range.isValid() && !getScopeRep(); }
-  /// A scope specifier is present, and it refers to a real scope.
-  bool isValid() const { return bool(getScopeRep()); }
-
-  /// Indicate that this nested-name-specifier is invalid.
-  void SetInvalid(SourceRange R) {
-    assert(R.isValid() && "Must have a valid source range");
-    if (Range.getBegin().isInvalid())
-      Range.setBegin(R.getBegin());
-    Range.setEnd(R.getEnd());
-    Builder.Clear();
-  }
+    /// Turns this (empty) nested-name-specifier into '__super'
+    /// nested-name-specifier.
+    ///
+    /// \param Context The AST context in which this nested-name-specifier
+    /// resides.
+    ///
+    /// \param RD The declaration of the class in which nested-name-specifier
+    /// appeared.
+    ///
+    /// \param SuperLoc The location of the '__super' keyword.
+    /// name.
+    ///
+    /// \param ColonColonLoc The location of the trailing '::'.
+    void MakeMicrosoftSuper(ASTContext &Context, CXXRecordDecl *RD,
+                            SourceLocation SuperLoc,
+                            SourceLocation ColonColonLoc);
 
-  /// Deprecated.  Some call sites intend isNotEmpty() while others intend
-  /// isValid().
-  bool isSet() const { return bool(getScopeRep()); }
+    /// Make a new nested-name-specifier from incomplete source-location
+    /// information.
+    ///
+    /// FIXME: This routine should be used very, very rarely, in cases where we
+    /// need to synthesize a nested-name-specifier. Most code should instead use
+    /// \c Adopt() with a proper \c NestedNameSpecifierLoc.
+    void MakeTrivial(ASTContext &Context, NestedNameSpecifier Qualifier,
+                     SourceRange R);
+
+    /// Adopt an existing nested-name-specifier (with source-range
+    /// information).
+    void Adopt(NestedNameSpecifierLoc Other);
+
+    /// Retrieve a nested-name-specifier with location information, copied
+    /// into the given AST context.
+    ///
+    /// \param Context The context into which this nested-name-specifier will be
+    /// copied.
+    NestedNameSpecifierLoc getWithLocInContext(ASTContext &Context) const;
 
-  void clear() {
-    Range = SourceRange();
-    Builder.Clear();
-  }
+    /// Retrieve the location of the name in the last qualifier
+    /// in this nested name specifier.
+    ///
+    /// For example, the location of \c bar
+    /// in
+    /// \verbatim
+    ///   \::foo::bar<0>::
+    ///           ^~~
+    /// \endverbatim
+    SourceLocation getLastQualifierNameLoc() const;
+
+    /// No scope specifier.
+    bool isEmpty() const { return Range.isInvalid() && !getScopeRep(); }
+    /// A scope specifier is present, but may be valid or invalid.
+    bool isNotEmpty() const { return !isEmpty(); }
+
+    /// An error occurred during parsing of the scope specifier.
+    bool isInvalid() const { return Range.isValid() && !getScopeRep(); }
+    /// A scope specifier is present, and it refers to a real scope.
+    bool isValid() const { return bool(getScopeRep()); }
+
+    /// Indicate that this nested-name-specifier is invalid.
+    void SetInvalid(SourceRange R) {
+      assert(R.isValid() && "Must have a valid source range");
+      if (Range.getBegin().isInvalid())
+        Range.setBegin(R.getBegin());
+      Range.setEnd(R.getEnd());
+      Builder.Clear();
+    }
 
-  /// Retrieve the data associated with the source-location information.
-  char *location_data() const { return Builder.getBuffer().first; }
+    /// Deprecated.  Some call sites intend isNotEmpty() while others intend
+    /// isValid().
+    bool isSet() const { return bool(getScopeRep()); }
 
-  /// Retrieve the size of the data associated with source-location
-  /// information.
-  unsigned location_size() const { return Builder.getBuffer().second; }
-};
+    void clear() {
+      Range = SourceRange();
+      Builder.Clear();
+    }
+
+    /// Retrieve the data associated with the source-location information.
+    char *location_data() const { return Builder.getBuffer().first; }
+
+    /// Retrieve the size of the data associated with source-location
+    /// information.
+    unsigned location_size() const { return Builder.getBuffer().second; }
+  };
 
 /// Captures information about "declaration specifiers".
 ///



More information about the cfe-commits mailing list