Skip to content

Commit 02b221c

Browse files
authored
Merge pull request #140 from kdkavanagh/main
Add support for explicit-expressions. Fixes #139
2 parents a867a2b + 2fb949b commit 02b221c

4 files changed

Lines changed: 121 additions & 3 deletions

File tree

cxxheaderparser/parser.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2268,6 +2268,8 @@ def _parse_function(
22682268

22692269
if (is_class_block or multiple_name_segments) and not is_typedef:
22702270
props.update(dict.fromkeys(mods.meths.keys(), True))
2271+
if mods.explicit_value is not None:
2272+
props["explicit"] = mods.explicit_value
22712273

22722274
if attributes is None:
22732275
attributes = []
@@ -2562,6 +2564,7 @@ def _parse_type(
25622564
vars: typing.Dict[str, LexToken] = {} # only found on variables
25632565
both: typing.Dict[str, LexToken] = {} # found on either
25642566
meths: typing.Dict[str, LexToken] = {} # only found on methods
2567+
explicit_value: typing.Optional[Value] = None
25652568

25662569
get_token = self.lex.token
25672570

@@ -2601,6 +2604,13 @@ def _parse_type(
26012604
both[tok_type] = tok
26022605
elif tok_type in self._type_kwd_meth:
26032606
meths[tok_type] = tok
2607+
if tok_type == "explicit":
2608+
# C++20: explicit(<bool-constant-expression>)
2609+
otok = self.lex.token_if("(")
2610+
if otok:
2611+
explicit_value = self._create_value(
2612+
self._consume_balanced_tokens(otok)[1:-1]
2613+
)
26042614
elif tok_type == "mutable":
26052615
vars["mutable"] = tok
26062616
elif tok_type == "volatile":
@@ -2625,7 +2635,7 @@ def _parse_type(
26252635
self.lex.return_token(tok)
26262636

26272637
# Always return the modifiers
2628-
mods = ParsedTypeModifiers(vars, both, meths)
2638+
mods = ParsedTypeModifiers(vars, both, meths, explicit_value)
26292639
return parsed_type, mods
26302640

26312641
def _parse_decl(

cxxheaderparser/parserstate.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,17 @@
55

66
from .errors import CxxParseError
77
from .lexer import LexToken, Location
8-
from .types import ClassDecl, NamespaceDecl
8+
from .types import ClassDecl, NamespaceDecl, Value
99

1010

1111
class ParsedTypeModifiers(typing.NamedTuple):
1212
vars: typing.Dict[str, LexToken] # only found on variables
1313
both: typing.Dict[str, LexToken] # found on either variables or functions
1414
meths: typing.Dict[str, LexToken] # only found on methods
15+
#: For C++20 ``explicit(<expr>)``: the constant expression inside the
16+
#: parens (omitting the parens themselves). ``None`` if absent or if
17+
#: ``explicit`` was used as a bare keyword.
18+
explicit_value: typing.Optional[Value] = None
1519

1620
def validate(self, *, var_ok: bool, meth_ok: bool, msg: str) -> None:
1721
# Almost there! Do any checks the caller asked for

cxxheaderparser/types.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -822,7 +822,11 @@ class Method(Function):
822822
ref_qualifier: typing.Optional[str] = None
823823

824824
constructor: bool = False
825-
explicit: bool = False
825+
826+
#: True if the method was declared ``explicit``. For C++20
827+
#: ``explicit(<expr>)``, this holds the expression as a Value (omitting
828+
#: the outer parentheses).
829+
explicit: typing.Union[bool, Value] = False
826830
default: bool = False
827831

828832
destructor: bool = False

tests/test_class.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2308,6 +2308,106 @@ class Lizzard {
23082308
)
23092309

23102310

2311+
def test_class_fn_explicit_bool_constructor() -> None:
2312+
# C++20: explicit(<bool-constant-expression>)
2313+
content = """
2314+
struct Clazz {
2315+
explicit(false) Clazz();
2316+
explicit(sizeof(int) == 4) Clazz(int a);
2317+
template<class U>
2318+
explicit(!std::is_convertible_v<U, Clazz>)
2319+
Clazz(U const& u);
2320+
};
2321+
"""
2322+
data = parse_string(content, cleandoc=True)
2323+
2324+
assert data == ParsedData(
2325+
namespace=NamespaceScope(
2326+
classes=[
2327+
ClassScope(
2328+
class_decl=ClassDecl(
2329+
typename=PQName(
2330+
segments=[NameSpecifier(name="Clazz")], classkey="struct"
2331+
)
2332+
),
2333+
methods=[
2334+
Method(
2335+
return_type=None,
2336+
name=PQName(segments=[NameSpecifier(name="Clazz")]),
2337+
parameters=[],
2338+
access="public",
2339+
constructor=True,
2340+
explicit=Value(tokens=[Token(value="false")]),
2341+
),
2342+
Method(
2343+
return_type=None,
2344+
name=PQName(segments=[NameSpecifier(name="Clazz")]),
2345+
parameters=[
2346+
Parameter(
2347+
type=Type(
2348+
typename=PQName(
2349+
segments=[FundamentalSpecifier(name="int")]
2350+
)
2351+
),
2352+
name="a",
2353+
)
2354+
],
2355+
access="public",
2356+
constructor=True,
2357+
explicit=Value(
2358+
tokens=[
2359+
Token(value="sizeof"),
2360+
Token(value="("),
2361+
Token(value="int"),
2362+
Token(value=")"),
2363+
Token(value="="),
2364+
Token(value="="),
2365+
Token(value="4"),
2366+
]
2367+
),
2368+
),
2369+
Method(
2370+
return_type=None,
2371+
name=PQName(segments=[NameSpecifier(name="Clazz")]),
2372+
parameters=[
2373+
Parameter(
2374+
type=Reference(
2375+
ref_to=Type(
2376+
typename=PQName(
2377+
segments=[NameSpecifier(name="U")]
2378+
),
2379+
const=True,
2380+
)
2381+
),
2382+
name="u",
2383+
)
2384+
],
2385+
access="public",
2386+
constructor=True,
2387+
template=TemplateDecl(
2388+
params=[TemplateTypeParam(typekey="class", name="U")]
2389+
),
2390+
explicit=Value(
2391+
tokens=[
2392+
Token(value="!"),
2393+
Token(value="std"),
2394+
Token(value="::"),
2395+
Token(value="is_convertible_v"),
2396+
Token(value="<"),
2397+
Token(value="U"),
2398+
Token(value=","),
2399+
Token(value="Clazz"),
2400+
Token(value=">"),
2401+
]
2402+
),
2403+
),
2404+
],
2405+
)
2406+
]
2407+
)
2408+
)
2409+
2410+
23112411
def test_class_fn_default_constructor() -> None:
23122412
content = """
23132413
class DefaultConstDest {

0 commit comments

Comments
 (0)