Skip to content

Unraw raw identifiers in the service and proxy macros - #295

Merged
zeenix merged 3 commits into
mainfrom
claude/github-issue-292-293-raw-idents
Jul 17, 2026
Merged

Unraw raw identifiers in the service and proxy macros#295
zeenix merged 3 commits into
mainfrom
claude/github-issue-292-293-raw-idents

Conversation

@zeenix

@zeenix zeenix commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Raw identifiers, continued from #294 — plus one lint fix found alongside them.

r# is Rust syntax for using a keyword as an identifier; it is never part of the name. #294 taught
the Type / CustomType / introspect::ReplyError derives that. These three commits finish the
job for the two macros #294 didn't touch, and fix an unrelated-but-adjacent lint bug found while
writing its tests.

  • zlink-macros: Allow non-camel-case variants in ReplyError's helper (derive(ReplyError): a lowercase variant fails -D warnings and the user's own #[allow] can't silence it #293) — the wire
    ReplyError derive nests a helper enum inside Deserialize::deserialize to clone the variants
    across for serde. Nested items don't inherit attributes, so the user's own
    #[allow(non_camel_case_types)] could never reach it: a lowercase variant failed -D warnings
    with the diagnostic telling the user to add the attribute they already had. Nothing to do with
    raw idents.
  • zlink-macros: Unraw idents in the service macro (#[zlink::service] mishandles raw identifiers: the IDL disagrees with the wire #292) — the macro built the IDL parameter
    name by hand while leaving the wire name to serde, and serde unraws. The two disagreed, and the
    IDL was the wrong half: Varlink can't express r#type at all (# starts a comment), so what we
    emitted wasn't parseable by our own parser. A raw method name additionally panicked in
    format_ident! as R#type.
  • zlink-macros: Unraw idents in the proxy macro — no issue of its own; found while verifying
    #[zlink::service] mishandles raw identifiers: the IDL disagrees with the wire #292, whose text wrongly claimed proxy was unaffected. Parameters were indeed fine (serde unraws
    them), but a raw method name panicked with "R#typeParams" is not a valid identifier across 8
    sites, one of which would have built the ident chain_r#type.

Both halves of #292 had to land together: unrawing only the static's ident would have made
r#type compile while the IDL still described the field as r#type.

An explicit #[zlink(rename = "...")] is still used verbatim throughout — that's a user-supplied
string, not an ident.

Each commit passes standalone (469 → 471 → 473), so the branch bisects. Every fix is
mutation-verified: reverting it fails a test with the error the issue reports.

Fixes #292.
Fixes #293.

zeenix added 3 commits July 17, 2026 12:29
derive(ReplyError) generates a Deserialize impl that clones the user's enum
variants into a helper enum, __ZlinkDeserHelper, nested inside the
deserialize function body so serde can derive on it directly. Being a
separate item in that scope, it doesn't inherit the user's own
#[allow(non_camel_case_types)] on their enum, so a lowercase variant name
(e.g. `lowercase { .. }`) fails `-D warnings` unconditionally. Worse, the
diagnostic points at the user's source and tells them to add the very
attribute they already have, with no way to silence it.

Add the allow directly to the generated helper, since it's the one place
that can actually see it.

Checked the other generated helper items in zlink-macros for the same
nested-in-a-function-body pattern (proxy chain/method wrapper structs and
enums, the Serialize impl's ParametersSerializer). None of them clone
user idents into a position that can trip non_camel_case_types: their
generated type names are either fixed or already normalized to
PascalCase, and the only cloned user idents end up as struct fields,
which that lint doesn't cover.

This only lifts the lint. A lowercase name is still not a valid Varlink error
name -- `error_def` parses it with `type_name`, which requires an uppercase
first letter -- so the realistic raw variants that motivated this (`r#fn`,
`r#match`) stay unusable, now for that reason instead. Making the derives
reject names our own parser cannot read is a separate concern.

Fixes #293.

Assisted-by: Claude Sonnet 4.5
Assisted-by: Claude Opus 4.8 (1M context)
The service macro builds the IDL parameter name by hand but leaves the wire
name to serde, and serde unraws idents. The two therefore disagreed for a raw
parameter: the IDL advertised `r#type` while the method only ever deserialized
`type`, so a client trusting our own interface description got `missing field
type`. It compiled silently and nothing caught it.

The IDL is the wrong half here. Varlink cannot express `r#type` at all -- `#`
starts a comment -- so the description we emitted was not even parseable, as
the new test shows by round-tripping it through our own parser.

A raw method name had a second failure: the name reached `format_ident!` as
`R#type` and panicked with no span and no hint.

Both now resolve through `naming::unraw`, keeping naming decisions in the one
module that owns them so the IDL and the wire cannot drift apart.

The `_`-prefix check guarding those names had the same gap: it judged the raw
string, so `r#_foo` slipped past it and then reached the IDL unraw'd as the
`_foo` the check exists to reject. It now judges the string `wire_name` does.

Fixes #292.

Assisted-by: Claude Opus 4.8 (1M context)
A raw method name (`async fn r#type`) made the proxy macro panic with
`"R#typeParams" is not a valid identifier`: the name reached `format_ident!`
still carrying its `r#`. Same class as the service macro fix, but with a
milder consequence -- it never compiled, so no wrong bytes could reach the
wire.

Parameters were already fine here: the proxy has no IDL side and inherits
serde's unrawing, so `r#move` has always serialized as `move`.

Only the string-derived names change: the wire method name, the generated
`TypeParams`/`TypeWrapper` idents, and the `chain_type` ident. The generated
fn deliberately keeps the raw ident, since it still has to match the trait it
implements.

Assisted-by: Claude Opus 4.8 (1M context)
@codspeed-hq

codspeed-hq Bot commented Jul 17, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 4 untouched benchmarks


Comparing claude/github-issue-292-293-raw-idents (f0ba7d7) with main (a32c849)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (17da222) during the generation of this report, so a32c849 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@zeenix
zeenix merged commit 795188e into main Jul 17, 2026
18 checks passed
@zeenix
zeenix deleted the claude/github-issue-292-293-raw-idents branch July 17, 2026 10:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant