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.
72 lines
2.4 KiB
Rust
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"]
|
|
);
|
|
}
|