feat/stubgen: add preliminary $Cxx.name support

This commit is contained in:
2026-07-09 20:07:37 +08:00
parent efaae42603
commit e1dbd2dca9
5 changed files with 29 additions and 9 deletions
+2 -2
View File
@@ -7,7 +7,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from .utils import NodeDict
from .utils import NodeDict, get_cxx_name
if TYPE_CHECKING:
from .emitter import Emitter
@@ -30,7 +30,7 @@ def generate_enum(
literals: list[str] = []
for e in enumerants:
literals.append(f'"{e["name"]}"')
literals.append(f'"{get_cxx_name(e) or e["name"]}"')
emitter.add_typing_import("Literal")
line = f"Literal[{', '.join(literals)}]"
+4 -3
View File
@@ -16,7 +16,7 @@ from .gen_const import generate_const
from .gen_enum import generate_enum
from .gen_generic import generate_type_vars
from .type_resolver import resolve_type
from .utils import NodeDict, type_id_to_int
from .utils import NodeDict, get_cxx_name, type_id_to_int
if TYPE_CHECKING:
from .emitter import Emitter
@@ -44,7 +44,7 @@ def generate_struct(
# ── collect fields ─────────────────────────────────────────────────
for field in struct_body.get("fields", []):
field_name: str = field["name"]
field_name: str = get_cxx_name(field) or field["name"]
disc_value: int = field.get("discriminantValue", _NO_DISCRIMINANT)
if "slot" in field:
@@ -112,7 +112,8 @@ def generate_struct(
generate_const(emitter, nt, registry)
for field_name, type_str in non_union_fields:
emitter.add_field(field_name, type_str)
default = "None" if type_str == "None" else None
emitter.add_field(field_name, type_str, default=default)
if discriminant_count > 0 and union_fields:
_generate_union_methods(emitter, union_fields)
+3 -3
View File
@@ -7,7 +7,7 @@ building a ``TypeRegistry`` that maps type IDs to ``TypeInfo``.
from __future__ import annotations
from .models import TypeInfo, TypeRegistry
from .utils import NodeDict, get_display_name, get_node_kind, type_id_to_int
from .utils import NodeDict, get_cxx_name, get_display_name, get_node_kind, type_id_to_int
_GENERATABLE_KINDS = frozenset({"struct", "enum", "interface", "const"})
@@ -35,7 +35,7 @@ def build_type_registry(
continue
type_id = type_id_to_int(node["id"])
name = get_display_name(node)
name = get_cxx_name(node) or get_display_name(node)
scope_id = node.get("scopeId", "0")
parent_id = type_id_to_int(scope_id) if scope_id != "0" else None
@@ -117,7 +117,7 @@ def _build_scoped_name(
break
if get_node_kind(node) != "file":
parts.append(get_display_name(node))
parts.append(get_cxx_name(node) or get_display_name(node))
scope_id_str = node.get("scopeId", "0")
scope_id = int(scope_id_str) if scope_id_str != "0" else 0
+19
View File
@@ -38,3 +38,22 @@ def get_node_kind(node: NodeDict) -> str:
def type_id_to_int(type_id: str) -> int:
"""Convert a type ID string (from dict) to an integer."""
return int(type_id)
# Annotation IDs from c++.capnp
_CXX_NAME_ANNOTATION_ID = 17466269397259751886 # 0xf2466592b7084e81
def get_cxx_name(node: NodeDict) -> str | None:
"""Return the ``$Cxx.name`` annotation value if present on a node."""
try:
for annot in node.get("annotations", []):
if annot.get("id") == _CXX_NAME_ANNOTATION_ID:
val = annot.get("value")
if isinstance(val, dict):
text = val.get("text", "")
if isinstance(text, str):
return text
except Exception:
pass
return None
+1 -1
View File
@@ -15,7 +15,7 @@ _HERE = Path(__file__).parent
if str(_HERE) not in sys.path:
sys.path.insert(0, str(_HERE))
import capnp # pyright: ignore[reportMissingTypeStubs, reportUnusedImport] # activates import hook
import capnp # pyright: ignore[reportMissingTypeStubs] # activates import hook
import test_generics_capnp
import test_interface_capnp
import test_nested_capnp