Skip to content

Dart imports are never resolved to file nodes (0/139 in a real Flutter repo) — affected is blind #2329

Description

@salveki

Summary

Dart import edges are never resolved to file nodes. The edge is created, but its target is the import path as a string node with source_file: None, so nothing connects it to the file it names. Reverse traversal is therefore blind: affected returns "No affected nodes found" for a file whose importer is visible on line 5 of the source.

Measured on a real Flutter repo (76 source files, 636 nodes, 840 edges), graphify 0.9.30:

Language internal import edges resolved to a file node
Python 696 / 696 — 100%
TypeScript resolves (605 usable imports_from edges)
Dart 0 / 139 — 0%

Reproduce

Any Flutter/Dart project:

GRAPHIFY_FORCE=1 graphify . --code-only
import json
g = json.load(open("graphify-out/graph.json"))
idx = {n["id"]: n for n in g["nodes"]}
res = unres = 0
for e in g["links"]:
    if e.get("relation") not in ("imports", "imports_from"):
        continue
    if not (idx.get(e["source"], {}).get("source_file") or "").endswith(".dart"):
        continue
    t = idx.get(e["target"], {})
    lbl = t.get("label") or ""
    if lbl.startswith("dart:") or (lbl.startswith("package:") and "/" not in lbl):
        continue
    res += 1 if t.get("source_file") else 0
    unres += 0 if t.get("source_file") else 1
print(res, unres)

A concrete edge from the repo I measured, where grid_screen.dart line 5 is
import '../cell/cell_widget.dart';:

grid_screen.dart --imports--> "../cell/cell_widget.dart"   (source_file: None)

Cause

graphify/extractors/dart.py, section # 6. Imports and Exports:

for m in re.finditer(r"""^\s*import\s+['"]([^'"]+)['"]""", src_clean, re.MULTILINE):
    pkg = m.group(1)
    tgt_nid = _make_id(pkg)                    # id from the raw string
    add_node(tgt_nid, pkg, source_file=None)   # never linked to a file
    add_edge(file_nid, tgt_nid, "imports")

JS/TS goes through _resolve_js_import_target() in graphify/extractors/resolution.py, which returns _make_id(str(resolved_path)) — the same id the file node carries — so the edge lands on the real file. Dart has an extractor but no resolver counterpart.

Impact

Every question needing reverse traversal fails on Dart, while symbol-level relations work fine. On a 9-question battery over the same repo, the five questions requiring resolved import edges (who imports X, who instantiates Y, which file is most depended on) all failed; the three resting on inherits / implements / references all passed. Coverage itself is fine — 55/55 .dart files are in the graph.

This matters more in Flutter than the numbers suggest, because widget composition lives inside build(): "where is CellWidget constructed" is the ordinary question, and it is unanswerable without a cross-file edge to walk.

Note on #2114 and #2174

Not a criticism of either PR — just flagging that neither closes this, in case it is assumed:

Suggested shape

Mirroring _resolve_js_import_target, Dart needs noticeably less machinery than JS/TS — no extension inference (.dart is mandatory), no directory index convention, no path aliases:

  1. raw.startswith(".") → relative to the importing file's directory, normalized.
  2. package:<name>/<subpath> where <name> matches name: in the nearest pubspec.yaml<pubspec_dir>/lib/<subpath>.
  3. dart:* and other package:* → external; return the _make_id("ref", raw) namespaced id, as _resolve_js_import_target already does to avoid the Unresolved bare npm import gets aliased onto an unrelated same-named local file (cross-language phantom edge) #1638 collision.

part / part of directives are a separate concern and could stay out of a first pass.

Happy to send a PR for this if it would be useful, or to leave it to whoever is already in dart.py — say which is easier, since #2114 touches the same function.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions