Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions cxxheaderparser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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,
)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions cxxheaderparser/parserstate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions tests/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")]),
Expand Down Expand Up @@ -173,6 +174,7 @@ def test_misc_extern_inline() -> None:
name="handle",
)
],
extern='"C++"',
inline=True,
has_body=True,
)
Expand Down
3 changes: 1 addition & 2 deletions tests/test_var.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"',
)
]
)
Expand Down
Loading