Skip to content

Commit e2d512b

Browse files
authored
Merge pull request #148 from robotpy/most-vexing-parse
Fix parenthesized variable initialization
2 parents 3b84e9e + 4b9299b commit e2d512b

2 files changed

Lines changed: 89 additions & 20 deletions

File tree

cxxheaderparser/parser.py

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,7 +1606,7 @@ def _parse_field(
16061606

16071607
else:
16081608
# default value initializer
1609-
tok = self.lex.token_if("{")
1609+
tok = self.lex.token_if("{", "(")
16101610
if tok:
16111611
if is_typedef:
16121612
raise self._parse_error(tok)
@@ -2537,6 +2537,22 @@ def _parse_cv_ptr_or_fn(
25372537

25382538
_parse_type_ptr_ref_paren = {"*", "&", "DBL_AMP", "("}
25392539

2540+
_parameter_start_tokens = (
2541+
_pqname_start_tokens
2542+
| _attribute_start_tokens
2543+
| _type_kwd_both
2544+
| _type_kwd_meth
2545+
| {
2546+
"volatile",
2547+
"mutable",
2548+
"__inline",
2549+
"__forceinline",
2550+
"this",
2551+
"ELLIPSIS",
2552+
")",
2553+
}
2554+
)
2555+
25402556
def _parse_type(
25412557
self,
25422558
tok: typing.Optional[LexToken],
@@ -2735,29 +2751,34 @@ def _parse_decl(
27352751

27362752
# TODO: "type fn(x);" is ambiguous here. Because this is a header
27372753
# parser, we assume it's a function, not a variable declaration
2738-
# calling a constructor
2754+
# calling a constructor, unless the token after ( cannot start a
2755+
# parameter declaration.
27392756

2740-
# if ( then it's a function/method
2741-
if self.lex.token_if("("):
2757+
# if ( then it's usually a function/method
2758+
tok = self.lex.token_if("(")
2759+
if tok:
27422760
if not pqname:
27432761
raise self._parse_error(None)
27442762

2745-
return self._parse_function(
2746-
mods,
2747-
dtype,
2748-
pqname,
2749-
op,
2750-
template,
2751-
doxygen,
2752-
location,
2753-
constructor,
2754-
destructor,
2755-
is_friend,
2756-
is_typedef,
2757-
msvc_convention,
2758-
is_guide,
2759-
attributes,
2760-
)
2763+
if not self.lex.token_peek_if(*self._parameter_start_tokens):
2764+
self.lex.return_token(tok)
2765+
else:
2766+
return self._parse_function(
2767+
mods,
2768+
dtype,
2769+
pqname,
2770+
op,
2771+
template,
2772+
doxygen,
2773+
location,
2774+
constructor,
2775+
destructor,
2776+
is_friend,
2777+
is_typedef,
2778+
msvc_convention,
2779+
is_guide,
2780+
attributes,
2781+
)
27612782
elif msvc_convention:
27622783
raise self._parse_error(msvc_convention)
27632784

tests/test_var.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,54 @@ def test_var_ns_3() -> None:
601601
)
602602

603603

604+
def test_var_direct_initializer_parens() -> None:
605+
content = """
606+
const string FOO("foo");
607+
int BAR(static_cast<int>(1));
608+
"""
609+
data = parse_string(content, cleandoc=True)
610+
611+
assert data == ParsedData(
612+
namespace=NamespaceScope(
613+
variables=[
614+
Variable(
615+
name=PQName(segments=[NameSpecifier(name="FOO")]),
616+
type=Type(
617+
typename=PQName(segments=[NameSpecifier(name="string")]),
618+
const=True,
619+
),
620+
value=Value(
621+
tokens=[
622+
Token(value="("),
623+
Token(value='"foo"'),
624+
Token(value=")"),
625+
]
626+
),
627+
),
628+
Variable(
629+
name=PQName(segments=[NameSpecifier(name="BAR")]),
630+
type=Type(
631+
typename=PQName(segments=[FundamentalSpecifier(name="int")])
632+
),
633+
value=Value(
634+
tokens=[
635+
Token(value="("),
636+
Token(value="static_cast"),
637+
Token(value="<"),
638+
Token(value="int"),
639+
Token(value=">"),
640+
Token(value="("),
641+
Token(value="1"),
642+
Token(value=")"),
643+
Token(value=")"),
644+
]
645+
),
646+
),
647+
]
648+
)
649+
)
650+
651+
604652
def test_var_static_struct() -> None:
605653
content = """
606654
constexpr static struct SS {} s;

0 commit comments

Comments
 (0)