Repository navigation
fix(dart): a bare call or type name reaches only what its library can see - #2386
Merged
Merged
Conversation
… see A receiver-less Dart call, constructor call or type name now links only to a top-level declaration its library can see: one in the library itself (the file and its parts) or one exported by a library it imports without a prefix, through export chains and show/hide. A type written through an import prefix (`p.Report`) means what that import exports. It is a candidate filter ahead of the ranking in matchByExactName and matchFuzzy, so a visible namesake wins, and a library's own declaration shadows an imported one. The library model (pubspec packages, package: and relative URIs, part / part of, conditional imports) is src/resolution/dart-libraries.ts. A bare call also never reaches another type's enum constant or static constant, a generic `extension<T> on X` counts as unnamed, and an unnamed extension applies in every part of its library. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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 Dart name written without a receiver (a call, a constructor call, a type in a declaration, cast, generic argument,
extendsorimplements) now resolves only to a top-level declaration the caller's library can see:parts, private_names included;exportchains andshow/hide.A type written through an import prefix (
p.Report) means only what the importsas pexport. A library's own declaration shadows an imported one.dart:libraries and packages that aren't in the repository (flutter, test, mockito, …) export nothing of the project.It is a candidate filter ahead of the ranking in
matchByExactNameandmatchFuzzy, likeisJavaTypeVisible/isPhpClassVisible, so a visible namesake wins instead of the edge being dropped.The library model is the new
src/resolution/dart-libraries.ts:pubspec.yaml'sname:maps to its package root (the nearest pubspec above an indexed.dartfile, never above the project root), sopackage:<name>/<path>is<root>/lib/<path>. When several packages share a name, the importing file's own package wins, then the one enclosing it, else all of them count;part/part ofby URI or by library name, nested parts;import 'stub.dart' if (dart.library.io) 'io.dart') count as the union;showlist with comments in it is read whole.Where the library can't be established — a file with no pubspec above it (a mason
__brick__template), apart ofnaming no library it can find — nothing is filtered.Triage of the first cut added four related fixes:
static const. Main's param gate (fix(dart): a bare call to a parameter or local links nothing #2379) drops calls to parameters; this covers the rest, e.g. bloc's brick hook calling mason'sLogger.info(…)linked to bloc_lint'sLinterRuleState.info.matchFuzzyskips unnamed-extension nodes the way exact matching already does. Thinning a crowd had left fuzzy a lone same-fileextension on Colorto takeColortype refs.extension<T> on X, is now seen as unnamed. riverpod'sextension<PointerT …, ProviderT …> on Map<…>was takingMaptype references.override.providerOverrideandlistenable.debugListenedProvider(extensions in other parts offramework.dart) and riverpod_analyzer_utils'DartObject.toDependencyListnow link.Resolution only; extraction and the kernel are unchanged.
Why
Measured on rrousselGit/riverpod @4ba1be2 and felangel/bloc @b9be1e2, with main at 249d9a8 and the native kernel loaded. Edges into a library the caller cannot see, judged independently by the earlier offline prototype of the library model:
instantiates)extends/implementsbloc's remainder is examples/flutter_login using its own
authentication_repositorypackage while examples/flutter_firebase_login has another of that name: both count, and proximity picks flutter_login's (itspath:dependency).Validation
Edges per call site (source node, line, column, ref name):
Every changed site was triaged. Disagreements with the prototype were its blind spots: comments inside a
showlist (it reads them as export-all), import prefixes, duplicate package names..g.dart/.freezed.dartparts now reach their own library's declarations:test/integration/async.g.dart'sreturn family(…)was annotated.dart'sfamily, async.dart declares one; also the*.fix.g.dartlint fixtures;errorsOf/Counterthrough../src/utils.dart,CounterBlocthroughblocs/blocs.dart→counter/counter.dart, flutter_todos'MockTodosRepositorythroughhelpers/helpers.dart;weather_repository.Weather,open_meteo_api.Weather, andextends Notifiernow reach riverpod's ownNotifier.fakeAsync(package:fake_async → a vendored copy),runAppandBuilder(material_ui),unawaited(dart:async), Flutter'sPage,Position/Range(lsp_server_ce), vm_service'sClass,lsp.Diagnostic,firebase_auth.User,vm.Event,Map,Object,File;dispose()inside a class mixing in package:listen'sChangeNotifier;flutter_riverpod.dart'sshowlist has noStateNotifierProvider/StateProvider, and some locales import files that only exist in the English docs.MyAppthrough../provider_observer_logger.dart;ontype (_DataRecord,Mutation,_ProviderTransformerMixin) now reaching that type, not the extension node;Cost: about 4% more indexing CPU on riverpod (process CPU median 43.5 s → 45.4 s over 5 interleaved runs, wall 14.0 s → 14.6 s); bloc within noise.
Tests
__tests__/dart-library-visibility.test.ts(14 cases): a generated part's call and type → its own library; a non-imported namesake → nothing; the imported library over an unimported copy; export chains withshow,hide, a conditional export and an import prefix;part ofby library name; own library over import; enum and static constants; unnamed and generic unnamed extensions; an unnamed extension across parts; files outside any package; the directive reader. On main 9 of them fail.Not covered
importsedges are still matched by file basename (riverpod: 223 externalpackage:URIs link to a project file, e.g.package:flutter/foundation.dart→ riverpod'sfoundation.dart).http.get(…)withimport 'package:http/http.dart' as http) still guesses by name (25 on riverpod).🤖 Generated with Claude Code