Skip to content

Commit af9a080

Browse files
committed
Correctly parse free conversion operators
- Fixes #94 - Fixes #112
1 parent 0bc2f51 commit af9a080

2 files changed

Lines changed: 90 additions & 4 deletions

File tree

cxxheaderparser/parser.py

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1925,10 +1925,23 @@ def _parse_pqname(
19251925
break
19261926

19271927
# If no more segments, we're done
1928-
if not self.lex.token_if("DBL_COLON"):
1928+
tok = self.lex.token_if("DBL_COLON")
1929+
if not tok:
1930+
break
1931+
1932+
next_tok = self._next_token_must_be(
1933+
"NAME", "operator", "template", "decltype"
1934+
)
1935+
if next_tok.type == "operator" and not fn_ok:
1936+
# Qualified conversion operators (for example,
1937+
# ``Foo::operator int()``) do not have a leading return type.
1938+
# When parsing what might be a type, stop before the operator
1939+
# so declaration parsing can consume the qualified operator as
1940+
# the function name and parse the conversion type separately.
1941+
self.lex.return_tokens([tok, next_tok])
19291942
break
19301943

1931-
tok = self._next_token_must_be("NAME", "operator", "template", "decltype")
1944+
tok = next_tok
19321945

19331946
pqname = PQName(segments, classkey, has_typename)
19341947

@@ -2898,6 +2911,7 @@ def _parse_operator_conversion(
28982911
is_typedef: bool,
28992912
is_friend: bool,
29002913
attributes: typing.List[Attribute],
2914+
pqname: typing.Optional[PQName] = None,
29012915
) -> None:
29022916
tok = self._next_token_must_be("operator")
29032917

@@ -2918,8 +2932,11 @@ def _parse_operator_conversion(
29182932
self._next_token_must_be("(")
29192933

29202934
# make our own pqname/op here
2921-
segments: typing.List[PQNameSegment] = [NameSpecifier("operator")]
2922-
pqname = PQName(segments)
2935+
if pqname is None:
2936+
segments: typing.List[PQNameSegment] = [NameSpecifier("operator")]
2937+
pqname = PQName(segments)
2938+
else:
2939+
pqname = PQName([*pqname.segments, NameSpecifier("operator")])
29232940
op = "conversion"
29242941

29252942
if self._parse_function(
@@ -3024,6 +3041,25 @@ def _parse_declarations(
30243041
)
30253042
return
30263043

3044+
qtok = self.lex.token_if("DBL_COLON")
3045+
if qtok:
3046+
otok = self.lex.token_if("operator")
3047+
if otok:
3048+
self.lex.return_tokens([qtok, otok])
3049+
self._next_token_must_be("DBL_COLON")
3050+
self._parse_operator_conversion(
3051+
mods,
3052+
location,
3053+
doxygen,
3054+
template,
3055+
is_typedef,
3056+
is_friend,
3057+
attributes,
3058+
parsed_type.typename,
3059+
)
3060+
return
3061+
self.lex.return_token(qtok)
3062+
30273063
# Ok, dealing with a variable or function/method
30283064
while True:
30293065
if self._parse_decl(

tests/test_operators.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
PQName,
1212
Parameter,
1313
Reference,
14+
TemplateArgument,
15+
TemplateDecl,
16+
TemplateSpecialization,
17+
TemplateTypeParam,
1418
Type,
1519
)
1620
from cxxheaderparser.simple import (
@@ -694,6 +698,52 @@ def test_conversion_operators_decorated() -> None:
694698
)
695699

696700

701+
def test_template_conversion_operator_impl() -> None:
702+
content = """
703+
template <class T> ON_SimpleArray<T>::operator T *() {
704+
return (m_count > 0) ? m_a : 0;
705+
}
706+
"""
707+
data = parse_string(content, cleandoc=True)
708+
709+
assert data == ParsedData(
710+
namespace=NamespaceScope(
711+
method_impls=[
712+
Method(
713+
return_type=Pointer(
714+
ptr_to=Type(typename=PQName(segments=[NameSpecifier(name="T")]))
715+
),
716+
name=PQName(
717+
segments=[
718+
NameSpecifier(
719+
name="ON_SimpleArray",
720+
specialization=TemplateSpecialization(
721+
args=[
722+
TemplateArgument(
723+
arg=Type(
724+
typename=PQName(
725+
segments=[NameSpecifier(name="T")]
726+
)
727+
)
728+
)
729+
]
730+
),
731+
),
732+
NameSpecifier(name="operator"),
733+
]
734+
),
735+
parameters=[],
736+
has_body=True,
737+
template=TemplateDecl(
738+
params=[TemplateTypeParam(typekey="class", name="T")]
739+
),
740+
operator="conversion",
741+
)
742+
]
743+
)
744+
)
745+
746+
697747
def test_free_operator() -> None:
698748
content = """
699749
std::ostream& operator<<(std::ostream& os, const MyDate& dt);

0 commit comments

Comments
 (0)