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
4 changes: 3 additions & 1 deletion SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -1089,7 +1089,9 @@ Rules:

Clause 2's registry is the SAME class-name → reactive-member-name map (`classRegistry`) the builder threads through the `.value` cross-class rewrite (§5.1) — populated same-file by a fast member-scan and cross-file by walking imports (the resolver pass documented under §3.6's env-field rule), extended to also seed from every type created at a `Provider(...)` / `.environment<T>()` call site in the file (not just `@SolidEnvironment` field types), so a controller that's provided but never consumed via `@SolidEnvironment` still gets recognized. It is further extended to seed from the declared type name of every class's instance field and constructor parameter in the file — the plain constructor-injection DI shape (`CustomersRepository({required AuthRepository authRepository})`, `final AuthRepository _authRepository;`), which has no `@SolidEnvironment` field and creates nothing at a `Provider(...)` / `.environment<T>()` call site, so neither of the other two rules ever seeds it (issue #104). A `super.` constructor parameter seeds the same way: an explicit-typed one (`Foo(AuthRepository super.repo)`) reads its own annotation like any other parameter, and a bare one (`Foo(super.repo)`) is resolved — on a resolved unit, from the analyzer's own resolution of the forwarded parameter; on an unresolved unit (the pure-consumer probe), by locating the superclass declaration and matching the targeted super-constructor's corresponding parameter, recursing up a chain of further bare `super.` relays where needed — or, for a `this.x` field-formal target, the base class's same-named instance field (§2; issue #108). The superclass-location search is same-package-only and alias-aware (§2), and never runs at all once the resolved path has already answered the question — both matter in practice because this same code path fires for `{super.key}` on every widget, and without them would otherwise chase `StatefulWidget`/`State` into `package:flutter` on every unresolved probe. Once the wanted name is known, finding ITS OWN declaration gets one extra hop beyond the import walk this paragraph describes — through the file that hosts the superclass's own imports — never a general transitive walk (§2). A type name enters the registry ONLY when its declaration carries at least one `@SolidState` field or getter; a candidate name that matches nothing found is harmless.

Two rules narrow the cross-file import walk to mirror Dart's own name resolution, closing collision windows the annotation-blind seeding above would otherwise open: a simple name that the CURRENT file itself declares as a class/enum/mixin/etc. is dropped from the wanted set before the import walk starts — a local top-level declaration always shadows a same-name import (no error, no ambiguity), so cross-file attribution for that name is provably wrong and the import walk never even looks for it; and each import's `show`/`hide` combinators are honored — an import that hides the wanted name, or carries a `show` list that doesn't include it, cannot be credited as that name's source and is skipped for that name (its other names are unaffected). Matching is otherwise by SIMPLE class name, not library identity — two distinct types sharing a name across two DIFFERENT imported libraries, neither of them local to the current file and neither excluded by a combinator, could in principle still collide; a residual risk of the name-based registry design, not resolved by the two rules above. The bare-`super.x` seeder's own ambiguity fallback (§2; issue #108) widens this collision surface a little further when it fires: unable to pin down a single matching parameter, it seeds EVERY field's and constructor parameter's type name from the located superclass, including names that would otherwise never have been proposed as candidates for the current file at all.
One rule still narrows the cross-file import walk to mirror Dart's own name resolution: each import's `show`/`hide` combinators are honored — an import that hides the wanted name, or carries a `show` list that doesn't include it, cannot be credited as that name's source and is skipped for that name (its other names are unaffected). A simple name the CURRENT file itself declares as a class/enum/mixin/etc. no longer drops that name from the wanted set before the walk starts (issue #110) — the walk still looks for a same-named foreign class, and a match is recorded QUALIFIED by its origin library (`classRegistryOrigins`, `name -> originUri -> fields`) rather than folded into the flat, name-only `classRegistry`; a name with a local declaration, or with two-plus DISTINCT cross-file origins, is flagged in `classRegistryShadowedNames` and the flat `classRegistry` entry for it is withheld.

That qualification is what narrows the residual name-based collision risk — but only for the §5.1 `.value` cross-class rewrite, and only where real type resolution reaches the receiver. `value_rewriter.dart` resolves a flagged name's cross-class read by comparing the receiver's resolved `staticType` (tier 1 — see §5.1) against the flagged name's recorded origins; two distinct types sharing a simple name across two DIFFERENT imported libraries now resolve to their OWN class's fields precisely, each through its own constructor-injected receiver, rather than colliding. The caveat is NOT eliminated, only narrowed to the contexts tier 1 cannot reach: an AST-only receiver (tiers 2-4 — a same-named-but-untyped shape, or any context with no resolver at all) can never supply a library URI, so a flagged name simply never rewrites there — conservative, not wrong, but still a residual gap relative to a fully library-qualified design. It is also NOT narrowed at all for THIS section's own clause 2 — the dispose auto-injection registry lookup (`_classRegistry.containsKey(typeName)`) is a plain name-based presence check with no per-origin awareness, and the main lowering path (above) has no resolver to consult in the first place, so two distinct types sharing a name could still cause dispose auto-injection to reason about the wrong one. The bare-`super.x` seeder's own ambiguity fallback (§2; issue #108) widens the underlying collision SURFACE a little further when it fires: unable to pin down a single matching parameter, it seeds EVERY field's and constructor parameter's type name from the located superclass, including names that would otherwise never have been proposed as candidates for the current file at all — the qualification above still applies to each of those seeded names individually.

The injected `provider.dispose()` always compiles for Solid-lowered types because Section 10 attaches `implements Disposable` and a synthesized `dispose()` to every annotated class. For non-Solid types, the user declares `void dispose()` (or a known disposable base) on the source class for clause 1 to fire; otherwise nothing is injected (clause 3) unless the type is cross-file and unproven (clause 4), and the user may always opt in or out by writing `dispose:` explicitly.

Expand Down
2 changes: 1 addition & 1 deletion packages/solid_annotations/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## 3.0.0-dev.2

- **DOCS**: Update `WidgetEnvironment.environment()` doc comment — `solid_generator` now only auto-injects `dispose:` when the created type is recognized as needing it (has a provable `dispose()`, or is `@Solid*`-annotated — same-file or cross-file); omitting `dispose:` on a type provably without one injects nothing instead of a call that previously failed source-layer typechecking with a compile-time `undefined_method` error on `.dispose()`.
- **DOCS**: Update `WidgetEnvironment.environment()` doc comment for `solid_generator`'s type-aware `dispose:` auto-injection.

## 3.0.0-dev.1

Expand Down
Loading
Loading