Skip to content

Reduce __-prefixed identifiers in generated code where hygiene doesn't require them #1010

@carllerche

Description

@carllerche

Problem

The model macros prefix many generated identifiers with __. Some of these
leak into the user-facing surface — rustdoc signatures and compiler error
messages — where they add noise and make the API harder to read.

The clearest example is the generated select method
(crates/toasty-macros/src/model/expand/query.rs:56):

#vis fn select<__E, __T>(
    self,
    projection: __E,
) -> #toasty::stmt::Query<#toasty::List<__T>>
where
    __E: #toasty::IntoExpr<__T>,
    __T: #toasty::Load,

A user who mistypes a projection sees an error mentioning __E and __T, and
the same names show up in rustdoc. Plain E / U would read better. Other
user-visible generic params with the same treatment: __Origin and __T in
the Model trait associated types and method signatures, and the __T on the
generated query struct.

The __ prefix is a hygiene guard: quote! emits call-site spans, so a
generated generic param could in principle collide with a user type spliced
into the same signature (e.g. Path<__Origin, #model_ident> breaks if the
model is named Origin). But many uses carry no such risk — select's
signature splices only toasty paths, never a user ident, so __E/__T guard
against nothing. Those are pure noise.

Proposed solution

Audit every __-prefixed identifier in generated code and split them into two
buckets:

  1. No collision possible — drop the prefix in favor of a readable name.
    select<__E, __T>select<E, U> is the motivating case.
  2. Could collide with a spliced user ident — keep a guard, but prefer the
    least intrusive option. Where the ident is purely internal (macro-local
    let bindings, #[doc(hidden)] helpers like __macro_fields_root), the
    prefix is fine and invisible. Where it appears in a user-visible signature
    (__Origin in trait/Model method signatures), consider whether def-site /
    mixed-site hygiene lets us use a readable name without the collision risk,
    rather than carrying __ into rustdoc.

Scope of the audit (from a sweep of crates/toasty-macros/src/):

  • User-facing generic params: __E, __T in select; __Origin, __T in
    Model trait assoc types / methods and the query struct.
  • Internal-only (likely keep): macro-local vars (__update, __scope,
    __scope_fields, __fields), the __macro_fields_root hidden method,
    type-check aliases (__RelationTarget, __via_typed/__via_untyped,
    _check<__T>), and the __toasty_ collision-avoidance prefix in
    model/expand/util.rs.

Alternatives considered

  • Leave as-is. The names work; they're just ugly. The cost is paid by every
    user who reads a generated signature or hits a related compile error.
  • Blanket-strip all __ prefixes. Risks reintroducing the splice
    collisions the prefix was added to prevent. The point of the audit is to
    separate the safe cases from the load-bearing ones.

Scope

Medium — touches the generated public API surface (rustdoc + error messages),
but no behavior change.

Additional context

Identified while reading the macro source; no functional bug, purely a
readability/ergonomics cleanup of generated code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions