use brick::model::{Building, EdgeKind, NodeKind}; use brick::scan; use std::fs; fn name_of<'a>(model: &'a Building, id: &str) -> &'a str { model .nodes .iter() .find(|node| node.id == id) .map_or("", |node| node.name.as_str()) } fn calls(model: &Building) -> Vec<(String, String, NodeKind)> { model .edges .iter() .filter(|edge| edge.kind == EdgeKind::Calls) .map(|edge| { let source_kind = model .nodes .iter() .find(|node| node.id == edge.source) .map(|node| node.kind) .unwrap(); ( name_of(model, &edge.source).to_owned(), name_of(model, &edge.target).to_owned(), source_kind, ) }) .collect() } #[test] fn swift_preview_declares_functions_and_ignores_comments() { // Swift ran through the C-style lexical patterns: `func` declarations were // never functions, each declaration line became a call from the file, calls // inside (nested) block comments became edges, and real calls were attributed // to the file instead of their function. let dir = tempfile::tempdir().unwrap(); fs::write( dir.path().join("a.swift"), r#"import Foundation func realTarget() -> Int { return 1 } func ghost() -> Int { return 2 } final class Worker: NSObject { let url = "http://example.com/*not-a-comment" func caller() -> Int { /* disabled: ghost() /* nested: ghost() */ still disabled: ghost() */ return realTarget() } } "#, ) .unwrap(); let model = scan::scan(dir.path()).unwrap(); let functions: Vec<&str> = model .nodes .iter() .filter(|node| node.kind == NodeKind::Function) .map(|node| node.name.as_str()) .collect(); for name in ["realTarget", "ghost", "caller"] { assert!( functions.contains(&name), "{name} not a function: {functions:?}" ); } assert!( model .nodes .iter() .any(|node| node.kind == NodeKind::Class && node.name == "Worker") ); let calls = calls(&model); assert!( calls.iter().any(|(source, target, kind)| source == "caller" && target == "realTarget" && *kind == NodeKind::Function), "{calls:?}" ); assert!( !calls.iter().any(|(_, target, _)| target == "ghost"), "a call inside a block comment became an edge: {calls:?}" ); assert!( !calls.iter().any(|(_, _, kind)| *kind == NodeKind::File), "a declaration or statement was attributed to the file: {calls:?}" ); }