Skip to content

fix(dart): an import or export links the file its URI names, not a namesake - #2388

Merged
colbymchenry merged 1 commit into
mainfrom
claude/intelligent-northcutt-841a04
Oct 6, 2026
Merged

colbymchenry merged 1 commit into
mainfrom
claude/intelligent-northcutt-841a04

Conversation

@colbymchenry

Copy link
Copy Markdown
Owner

Summary

A Dart import / export now links the file its URI names, or nothing:

  • package:<name>/<path> → <root>/lib/<path> of the project package whose pubspec.yaml says name: <name> (the nearest pubspec above an indexed .dart file, never above the project root; the importing file's own package first when names repeat).
  • Any other URI → the path from the importing file.
  • dart:, a package from outside the repo, a path out of the project, or a file the index doesn't hold → no edge. The ref stays unresolved, i.e. an outside import.

Before, matchByFilePath matched the URI's last segment against file basenames, falling back to a unique same-named file. Otherwise matchByQualifiedName bound the ref to the importing file's own import node, whose qualified name is the URI.

How:

When packages share a name and neither the importer's own package nor an enclosing one settles it, the importing package's path: dependency of that name picks it. That is read from pubspec_overrides.yaml, then pubspec.yaml, in block or flow style; a git dependency's inner path: doesn't count. bloc's two example apps each keep an authentication_repository. Library visibility reads the same answer.

Validation

riverpod @4ba1be2 and bloc @b9be1e2, native kernel loaded, main @99246848 vs this branch. Every Dart imports edge is classified against an independent oracle, a separate pubspec/URI resolver in the probe script.

Dart imports edges riverpod before riverpod after bloc before bloc after
to the file the URI names 1,381 2,180 602 1,012
outside package → a project file by basename 223 0 9 0
relative URI → another tree's file (the named one isn't in the repo) 2 0 0 0
to the importing file's own import node 2,044 0 1,042 0
left unresolved 161 1,631 102 743

All edges, paired by call site:

  • riverpod (85,482 → 84,012 edges): 1,470 removed, all imports; 799 moved from the own import node to the named file; 1,381 kept, with file-path at 0.7/0.85 becoming import at 0.9. Nothing added, and no other edge kind changed.
  • bloc (18,354 → 17,713 edges): 641 removed, all imports; 410 moved to the named file, 23 of them told apart by path:; 602 kept. Nothing added. 37 references / implements / instantiates edges keep their target and go from 0.7 to 0.9, because a file importing package:authentication_repository/… now sees one of the two packages instead of both.

Removed edges, by what they were before:

  • riverpod:
    • 1,235 own-import edges of dart: / outside-package URIs.
    • 223 outside packages linked to a namesake. 130 of those were package:flutter/widgets.dart → riverpod_analyzer_utils' riverpod_types/widgets.dart.
    • 12 project paths naming a file the repo doesn't have. These are website/i18n copies of docs pages whose sibling page wasn't translated; 2 of them had gone to website/docs/….
  • bloc:
    • 630 own-import edges.
    • 9 namesakes. bloc_tools' package:args/command_runner.dart had linked its own command_runner.dart, and package:integration_test/… had linked test_driver/integration_test.dart.
    • 2 missing files.

Still unresolved though they name a project path: the 12 riverpod i18n copies, bloc's mason template URI {{{bloc_export}}}, and AngularDart's build-generated *.template.dart (2).

Results are identical on #2386's pre-squash commit and on main after #2387: #2387 changes no edge on either repo.

File dependents, which codegraph affected, the MCP file tool's "used by" and dead-code corroboration all read:

  • riverpod_analyzer_utils' widgets.dart goes from 132 to 2.
  • riverpod's src/core/foundation.dart goes from 51 to 29.
  • Distinct cross-file dependency pairs out of riverpod's Dart files go from 7,080 to 7,533.

Tests

  • New __tests__/dart-import-uris.test.ts (5 tests). All 5 fail on main (checked at 249d9a8 and 9924684) and pass with the fix, with the kernel and with CODEGRAPH_KERNEL=0.
  • tsc --noEmit is clean.
  • Full suite with the kernel loaded, on a busy Windows box: 6,233 passed and 11 timed out. The timeouts were all in git-index-currency, index-daemon-rebuild, mcp-status-freshness and sync, and those 4 files pass when rerun alone (78/78).
  • An incremental-sync probe works:
    • An importer that changes after its target appears now links the target, including a newly added package.
    • Deleting a target returns its import to unresolved, instead of the own import node.

Not covered

  • part / part of directives aren't extracted by either extractor, so they have no edge, before or after.
  • sync doesn't retry a failed import when the file it names is added later. Its retry key is the URI's last . segment, dart. It links once the importer changes, or on a full index. Before this change it never re-linked either: it stayed on the own import node.
  • Related: open contributor PR fix(resolution): link imported Go and Dart values #1257 resolves Dart value references with its own package: handling. If it is revived, dartDirectiveFile is the shared answer.

🤖 Generated with Claude Code

…mesake

A Dart `import` / `export` now resolves its URI through the library model
in src/resolution/dart-libraries.ts: `package:<name>/<path>` is
`<root>/lib/<path>` of the project package whose pubspec.yaml says
`name: <name>`, any other URI is a path from the importing file, and a
`dart:` library, a package from outside the project or a file the index
does not hold links nothing. The ref never reaches the name-matcher, which
took the URI's last segment for a file name (`package:flutter/widgets.dart`
reached riverpod_analyzer_utils' widgets.dart from 130 files, bloc_tools'
`package:args/command_runner.dart` its own command_runner.dart) or bound
it to the importing file's own `import` node.

When packages share a name and neither the importer's own package nor one
enclosing it settles which, the importing package's `path:` dependency of
that name (pubspec_overrides.yaml, then pubspec.yaml) does: bloc's two
example apps each keep an authentication_repository. Library visibility
reads the same answer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@colbymchenry
colbymchenry merged commit 631df01 into main Oct 6, 2026
colbymchenry added a commit that referenced this pull request Oct 6, 2026
…ater (#2392)

A sync retried parked failed refs by name_tail (#1240), but an import
names a file, a folder or a namespace, not a symbol, so the lookup never
found one: the tail of `package:app/b.dart` was `dart`, of `inc/db.php`
`php`, of `./req` `/req`. Most languages never parked theirs at all: an
import that named no file reached the importing file's own `import` node
through matchByQualifiedName (its qualified name is the module path) and
resolved there, so `./x` in TS/JS, `import pkg.mod`, `#include "a/b.h"`,
`<stdio.h>`, a C# `using` of an outside namespace, Go module paths and R,
Solidity, Svelte and Vue imports were consumed by an edge to themselves.
A Lua `require` bound the local it is assigned to. The import linked
only once the importer changed, or on a full index.

- resolveOne's target gate drops an `imports` ref's own import statement,
  so the ref parks as failed (#2388 did this for Dart alone).
- A Lua / Luau `require` resolves to a module file or nothing.
- An import written as a path is parked under its last segment's stem
  (`package:app/b.dart` -> `b`, `./z.js` -> `z`): src/db/reference-tail.ts.
- Sync retries the failed imports an ADDED file can satisfy: by its name,
  stem and folder (importPathKeys), as a failed import's tail or whole
  name, and by the changed files' namespace / module names as a whole
  name (C# `using Foo.Bar`, Java `import com.z.*`). Same per-key ceiling.
- Schema v12: two partial indexes over failed imports, which the planner
  needs to avoid reading every failed call that shares a tail; the
  migration rewrites the tails of path imports parked by an older version.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@colbymchenry
colbymchenry deleted the claude/intelligent-northcutt-841a04 branch October 10, 2026 06:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant