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 { 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"] ); }