brick/tests/dart_imports.rs
trvon 324900287b
Some checks failed
build / build-macos (pull_request) Has been cancelled
build / release (pull_request) Has been cancelled
build / build-linux (pull_request) Has been cancelled
fix(dart): honour import prefixes, show/hide and one level of export
Problem: a bare call in a Dart file resolved against every imported
library. An `import 'a.dart' as p;` still bound `foo()` to a.dart's foo,
`hide` and `show` combinators were ignored, and a name re-exported through
a barrel library (`export 'impl.dart';`) never resolved.

Fix: record each Dart import/export directive with its `as` prefix and
show/hide lists. A bare name resolves only in libraries imported without a
prefix whose combinators let it through, plus that library's own exports
that also let it through (one level).

Test: tests/dart_imports.rs covers a prefixed, a hiding, a showing and a
barrelled import; it failed on the prefixed case before this change.
2026-09-24 21:11:48 -06:00

72 lines
2.4 KiB
Rust

use brick::model::{Building, EdgeKind};
use brick::scan;
use std::fs;
fn scan_files(files: &[(&str, &str)]) -> Building {
let root = tempfile::tempdir().unwrap();
for (path, content) in files {
let file = root.path().join(path);
fs::create_dir_all(file.parent().unwrap()).unwrap();
fs::write(file, content).unwrap();
}
scan::scan(root.path()).unwrap()
}
/// Paths of the resolved targets of calls leaving functions named `caller`.
fn call_targets(model: &Building, caller: &str) -> Vec<String> {
model
.edges
.iter()
.filter(|edge| edge.kind == EdgeKind::Calls)
.filter(|edge| {
model
.nodes
.iter()
.any(|n| n.id == edge.source && n.name == caller)
})
.filter_map(|edge| {
let target = model.nodes.iter().find(|n| n.id == edge.target)?;
Some(format!("{}:{}", target.path.clone()?, target.name))
})
.collect()
}
#[test]
fn dart_import_prefixes_combinators_and_exports_decide_what_a_bare_name_binds() {
// Dart calls resolved through every imported library, ignoring `as` prefixes
// and `show`/`hide`, and never through a library's own `export`s.
let model = scan_files(&[
("lib/a.dart", "void foo() {}\n"),
("lib/h.dart", "void hidden() {}\nvoid visible() {}\n"),
("lib/s.dart", "void shown() {}\nvoid other() {}\n"),
("lib/impl.dart", "void exported() {}\n"),
("lib/barrel.dart", "export 'impl.dart';\n"),
(
"lib/prefixed.dart",
"import 'a.dart' as a;\nvoid usePrefix() { foo(); }\n",
),
(
"lib/hiding.dart",
"import 'h.dart' hide hidden;\nvoid useHide() { hidden(); visible(); }\n",
),
(
"lib/showing.dart",
"import 's.dart' show shown;\nvoid useShow() { shown(); other(); }\n",
),
(
"lib/barrelled.dart",
"import 'barrel.dart';\nvoid useExport() { exported(); }\n",
),
]);
assert!(
call_targets(&model, "usePrefix").is_empty(),
"{:?}",
call_targets(&model, "usePrefix")
);
assert_eq!(call_targets(&model, "useHide"), ["lib/h.dart:visible"]);
assert_eq!(call_targets(&model, "useShow"), ["lib/s.dart:shown"]);
assert_eq!(
call_targets(&model, "useExport"),
["lib/impl.dart:exported"]
);
}