From b04ee7462abb20923ef59af0c65f12c2ec352050 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Fri, 26 Jun 2026 01:35:43 -0400 Subject: [PATCH] Store external linkage --- cxxheaderparser/parser.py | 21 ++++++++++++++++----- cxxheaderparser/parserstate.py | 7 +++++++ tests/test_misc.py | 2 ++ tests/test_var.py | 3 +-- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/cxxheaderparser/parser.py b/cxxheaderparser/parser.py index 564bf5c..c5f0fbe 100644 --- a/cxxheaderparser/parser.py +++ b/cxxheaderparser/parser.py @@ -1572,6 +1572,15 @@ def _discard_ctor_initializer(self) -> None: # Variable parsing # + def _make_extern_value( + self, mods: ParsedTypeModifiers, include_block_linkage: bool = True + ) -> typing.Union[bool, str]: + if mods.extern_linkage is not None: + return mods.extern_linkage + if include_block_linkage and self.state.extern_linkage is not None: + return self.state.extern_linkage + return mods.extern is not None + def _parse_bitfield(self) -> typing.Union[int, Value]: # Convert to integer for backwards compat tok = self.lex.token_if("INT_CONST_DEC") @@ -1687,7 +1696,7 @@ def _parse_field( template=template, attributes=attributes, constexpr=mods.constexpr is not None, - extern=mods.extern is not None, + extern=self._make_extern_value(mods), static=mods.static is not None, inline=mods.inline is not None, ) @@ -2312,6 +2321,7 @@ def _parse_function( state = self.state state.location = location is_class_block = isinstance(state, ClassBlockState) + extern = self._make_extern_value(mods, include_block_linkage=not is_class_block) params, vararg, at_params = self._parse_parameters( True, deduce_this_ok=is_class_block @@ -2352,7 +2362,7 @@ def _parse_function( operator=op, access=self._current_access, constexpr=mods.constexpr is not None, - extern=mods.extern is not None, + extern=extern, static=mods.static is not None, inline=mods.inline is not None, msvc_convention=msvc_convention_value, @@ -2411,7 +2421,7 @@ def _parse_function( template=template, operator=op, constexpr=mods.constexpr is not None, - extern=mods.extern is not None, + extern=extern, static=mods.static is not None, inline=mods.inline is not None, msvc_convention=msvc_convention_value, @@ -2717,8 +2727,9 @@ def _parse_type( elif tok_type == "constexpr": mods.constexpr = tok elif tok_type == "extern": - # TODO: store linkage - self.lex.token_if("STRING_LITERAL") + linkage_tok = self.lex.token_if("STRING_LITERAL") + if linkage_tok: + mods.extern_linkage = linkage_tok.value mods.extern = tok elif tok_type in ("__inline", "__forceinline", "inline"): mods.inline = tok diff --git a/cxxheaderparser/parserstate.py b/cxxheaderparser/parserstate.py index bb5a326..90c7254 100644 --- a/cxxheaderparser/parserstate.py +++ b/cxxheaderparser/parserstate.py @@ -30,6 +30,8 @@ class ParsedTypeModifiers: explicit_value: typing.Optional[Value] = None #: ``friend`` if encountered while parsing declaration specifiers. friend: typing.Optional[LexToken] = None + #: Linkage from an ``extern "..."`` declaration specifier, if present. + extern_linkage: typing.Optional[str] = None def validate( self, *, var_ok: bool, meth_ok: bool, msg: str, friend_ok: bool = False @@ -72,12 +74,16 @@ class BaseState(typing.Generic[T, PT]): #: Approximate location that the parsed element was found at location: Location + #: Effective extern linkage for this state, if any + extern_linkage: typing.Optional[str] + #: internal detail used by parser _prior_visitor: "CxxVisitor" def __init__(self, parent: typing.Optional["State"], location: Location) -> None: self.parent = parent self.location = location + self.extern_linkage = parent.extern_linkage if parent else None def _finish(self, visitor: "CxxVisitor") -> None: pass @@ -94,6 +100,7 @@ def __init__( ) -> None: super().__init__(parent, location) self.linkage = linkage + self.extern_linkage = linkage def _finish(self, visitor: "CxxVisitor") -> None: visitor.on_extern_block_end(self) diff --git a/tests/test_misc.py b/tests/test_misc.py index f0bad01..42d72e9 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -132,6 +132,7 @@ def test_extern_c() -> None: type=Type( typename=PQName(segments=[FundamentalSpecifier(name="int")]) ), + extern='"C"', ), Variable( name=PQName(segments=[NameSpecifier(name="y")]), @@ -173,6 +174,7 @@ def test_misc_extern_inline() -> None: name="handle", ) ], + extern='"C++"', inline=True, has_body=True, ) diff --git a/tests/test_var.py b/tests/test_var.py index 1db1425..ad6c10c 100644 --- a/tests/test_var.py +++ b/tests/test_var.py @@ -658,8 +658,7 @@ def test_var_extern_c() -> None: type=Type( typename=PQName(segments=[FundamentalSpecifier(name="int")]) ), - # TODO: store linkage - extern=True, + extern='"C"', ) ] )