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
51 changes: 46 additions & 5 deletions cxxheaderparser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -1925,10 +1925,23 @@ def _parse_pqname(
break

# If no more segments, we're done
if not self.lex.token_if("DBL_COLON"):
tok = self.lex.token_if("DBL_COLON")
if not tok:
break

next_tok = self._next_token_must_be(
"NAME", "operator", "template", "decltype"
)
if next_tok.type == "operator" and not fn_ok:
# Qualified conversion operators (for example,
# ``Foo::operator int()``) do not have a leading return type.
# When parsing what might be a type, stop before the operator
# so declaration parsing can consume the qualified operator as
# the function name and parse the conversion type separately.
self.lex.return_tokens([tok, next_tok])
break

tok = self._next_token_must_be("NAME", "operator", "template", "decltype")
tok = next_tok

pqname = PQName(segments, classkey, has_typename)

Expand Down Expand Up @@ -2898,6 +2911,7 @@ def _parse_operator_conversion(
is_typedef: bool,
is_friend: bool,
attributes: typing.List[Attribute],
parsed_type: typing.Optional[Type] = None,
) -> None:
tok = self._next_token_must_be("operator")

Expand All @@ -2917,9 +2931,17 @@ def _parse_operator_conversion(
# then this must be a method
self._next_token_must_be("(")

# make our own pqname/op here
segments: typing.List[PQNameSegment] = [NameSpecifier("operator")]
pqname = PQName(segments)
# if parsed_type is present, then create the pqname from it and apply
# any modifiers also
if parsed_type:
ctype.const = ctype.const or parsed_type.const
ctype.volatile = ctype.volatile or parsed_type.volatile
pqname = PQName([*parsed_type.typename.segments, NameSpecifier("operator")])
else:
# make our own pqname/op here
segments: typing.List[PQNameSegment] = [NameSpecifier("operator")]
pqname = PQName(segments)

op = "conversion"

if self._parse_function(
Expand Down Expand Up @@ -3024,6 +3046,25 @@ def _parse_declarations(
)
return

qtok = self.lex.token_if("DBL_COLON")
if qtok:
otok = self.lex.token_if("operator")
if otok:
self.lex.return_tokens([qtok, otok])
self._next_token_must_be("DBL_COLON")
self._parse_operator_conversion(
mods,
location,
doxygen,
template,
is_typedef,
is_friend,
attributes,
parsed_type,
)
return
self.lex.return_token(qtok)

# Ok, dealing with a variable or function/method
while True:
if self._parse_decl(
Expand Down
209 changes: 209 additions & 0 deletions tests/test_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
PQName,
Parameter,
Reference,
TemplateArgument,
TemplateDecl,
TemplateSpecialization,
TemplateTypeParam,
Type,
)
from cxxheaderparser.simple import (
Expand Down Expand Up @@ -694,6 +698,211 @@ def test_conversion_operators_decorated() -> None:
)


def test_qualified_conversion_operator_impls() -> None:
content = """
foo::operator bool() const { return bar; }
foo::operator bar() {;}

Foo::operator Type1() { return SomeMethod(); }
const Foo::operator Type2() const { return SomeMethod(); }
volatile Foo::operator Type3() const { return SomeMethod(); }

Foo::operator Foo::Type4() { return SomeMethod(); }
const Foo::operator Foo::Type5() const { return SomeMethod(); }
volatile Foo::operator Foo::Type6() const { return SomeMethod(); }
"""
data = parse_string(content, cleandoc=True)

assert data == ParsedData(
namespace=NamespaceScope(
method_impls=[
Method(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="bool")])
),
name=PQName(
segments=[
NameSpecifier(name="foo"),
NameSpecifier(name="operator"),
]
),
parameters=[],
has_body=True,
operator="conversion",
const=True,
),
Method(
return_type=Type(
typename=PQName(segments=[NameSpecifier(name="bar")])
),
name=PQName(
segments=[
NameSpecifier(name="foo"),
NameSpecifier(name="operator"),
]
),
parameters=[],
has_body=True,
operator="conversion",
),
Method(
return_type=Type(
typename=PQName(segments=[NameSpecifier(name="Type1")])
),
name=PQName(
segments=[
NameSpecifier(name="Foo"),
NameSpecifier(name="operator"),
]
),
parameters=[],
has_body=True,
operator="conversion",
),
Method(
return_type=Type(
typename=PQName(segments=[NameSpecifier(name="Type2")]),
const=True,
),
name=PQName(
segments=[
NameSpecifier(name="Foo"),
NameSpecifier(name="operator"),
]
),
parameters=[],
has_body=True,
operator="conversion",
const=True,
),
Method(
return_type=Type(
typename=PQName(segments=[NameSpecifier(name="Type3")]),
volatile=True,
),
name=PQName(
segments=[
NameSpecifier(name="Foo"),
NameSpecifier(name="operator"),
]
),
parameters=[],
has_body=True,
operator="conversion",
const=True,
),
Method(
return_type=Type(
typename=PQName(
segments=[
NameSpecifier(name="Foo"),
NameSpecifier(name="Type4"),
]
)
),
name=PQName(
segments=[
NameSpecifier(name="Foo"),
NameSpecifier(name="operator"),
]
),
parameters=[],
has_body=True,
operator="conversion",
),
Method(
return_type=Type(
typename=PQName(
segments=[
NameSpecifier(name="Foo"),
NameSpecifier(name="Type5"),
]
),
const=True,
),
name=PQName(
segments=[
NameSpecifier(name="Foo"),
NameSpecifier(name="operator"),
]
),
parameters=[],
has_body=True,
operator="conversion",
const=True,
),
Method(
return_type=Type(
typename=PQName(
segments=[
NameSpecifier(name="Foo"),
NameSpecifier(name="Type6"),
]
),
volatile=True,
),
name=PQName(
segments=[
NameSpecifier(name="Foo"),
NameSpecifier(name="operator"),
]
),
parameters=[],
has_body=True,
operator="conversion",
const=True,
),
]
)
)


def test_template_conversion_operator_impl() -> None:
content = """
template <class T> ON_SimpleArray<T>::operator T *() {
return (m_count > 0) ? m_a : 0;
}
"""
data = parse_string(content, cleandoc=True)

assert data == ParsedData(
namespace=NamespaceScope(
method_impls=[
Method(
return_type=Pointer(
ptr_to=Type(typename=PQName(segments=[NameSpecifier(name="T")]))
),
name=PQName(
segments=[
NameSpecifier(
name="ON_SimpleArray",
specialization=TemplateSpecialization(
args=[
TemplateArgument(
arg=Type(
typename=PQName(
segments=[NameSpecifier(name="T")]
)
)
)
]
),
),
NameSpecifier(name="operator"),
]
),
parameters=[],
has_body=True,
template=TemplateDecl(
params=[TemplateTypeParam(typekey="class", name="T")]
),
operator="conversion",
)
]
)
)


def test_free_operator() -> None:
content = """
std::ostream& operator<<(std::ostream& os, const MyDate& dt);
Expand Down
Loading