Repository navigation
fix(dart): a bare call to a parameter or local links nothing - #2379
Merged
Merged
Conversation
A receiver-less Dart call, or a function passed by name, whose name a parameter, local, loop or catch variable, or pattern variable around it binds now links nothing, instead of a same-named top-level symbol in another file or a member the class inherits; a local function declared there still links. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # CHANGELOG.md
# Conflicts: # CHANGELOG.md
# Conflicts: # CHANGELOG.md
This was referenced Oct 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A receiver-less Dart call, or a function passed by name, whose name a parameter, a local variable, a
for/catchvariable or a pattern variable around it binds now links nothing. Before, it linked to whatever same-named symbol the name matcher found: a top-level function or constant in a file the caller doesn't import, or a member the class inherits. A local function declared around the call still links, andthis.create()/x.create()are untouched.New
src/resolution/dart-local-scope.ts, applied atReferenceResolver.resolveOnenext togateTypeParameter, so every strategy's result obeys it. Per file it reads the source, with comments, string text and type arguments blanked, and records:{ … },=> …, a constructor's initializer list) of functions, methods, closures andcatchclauses, bound over the body;forhead declares, over the loop;final x = …,Foo? x;,void Function() x = …,final (a, b) = …,case Foo(:final x)), from the declaration to the block's end;Fields and top-level variables are not locals.
Why
On rrousselGit/riverpod @4ba1be2, 48
create(ref, arg)calls linked at exact-match 0.9 tofinal create = Mutation<…>()inwebsite/docs/concepts2/mutations/generic.dart, which none of them import. They came from$FunctionalFamilyOverride::overrideWith's function-typed parameter,_Cache::call,ExpandoUtils::upsert, the generatedoverrideWiths insync.g.dart, and the test matrix's provider factories.Two candidate fixes, evaluated
createedgesemit→Bloc::emit; riverpod'slisten,error,onError,fromJson)I prototyped import visibility offline: a pubspec package map,
part/part of, theexportclosure withshow/hide, and prefixed imports skipped..g.dartparts whose call to a function in their own library resolved to a namesake in another library, for exampleasync.g.dart'sfamily(…)→annotated.dart.package:functions (fakeAsync,runApp,unawaited) linked to project stubs.A veto would drop the part-file edges instead of fixing them. Fixing them needs a pre-ranking filter and a Dart library model that codegraph doesn't have, so that is left for a follow-up.
A/B (native kernel loaded, nodes unchanged)
Measured against main at 5a11cd7, after #2376 and #2377. The removed set is identical, edge for edge, to the one measured on 6421644 before those landed.
I traced every removed edge back to the binding that covers it. All are correct.
final listener = Listener<int>()mocks (332) andfinal provider = …/final family = ….family(…)(348), and parameters such as thecreates.listen,error, a parameter namedset→ an enum member, …).emit(…)calls that went toBloc::emit(130) andReplayBlocMixin::emit(2). Each is theEmitterparameter ofon<E>((event, emit) => …)orvoid _onX(E e, Emitter<S> emit).hydrate'sonError→BlocBase::onError,testBloc'sbuild→ a brick template, and functions passed by name.Cost: the scanner reads all of riverpod's Dart (6.4 MB) in about 0.8 s of CPU. Interleaved index runs stayed within noise: CPU 54.2 / 54.5 / 50.3 s on main against 58.5 / 53.9 / 54.3 s here.
Tests
__tests__/dart-local-call-scope.test.tsruns on kernel and wasm. It fails on main in both and passes here. It pins the riverpod and bloc shapes, plus controls that must keep linking: an inheritedemitcalled bare,this.create(), sibling closure and loop scopes, a nearer local function,final Box? box, and a parameter namedset.CODEGRAPH_KERNEL_EXPECT=1: 6,152 passed, 0 failed, 80 skipped.tscis clean.🤖 Generated with Claude Code