[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