feat: establish first-class lexical language parity #2
1 changed files with 93 additions and 8 deletions
fix(scan): isolate language resolution and method ownership
commit
9d5596f0dd
101
src/scan.rs
101
src/scan.rs
|
|
@ -94,7 +94,7 @@ struct ScanState {
|
|||
nodes: Vec<Node>,
|
||||
edges: Vec<Edge>,
|
||||
edge_keys: HashSet<(String, String, EdgeKind)>,
|
||||
symbols_by_name: HashMap<String, Vec<String>>,
|
||||
symbols_by_name: HashMap<(String, String), Vec<String>>,
|
||||
files_by_key: HashMap<String, Vec<String>>,
|
||||
pending: Vec<PendingRelationship>,
|
||||
diagnostics: Vec<Diagnostic>,
|
||||
|
|
@ -315,8 +315,14 @@ impl ScanState {
|
|||
&& !is_keyword(name.as_str())
|
||||
{
|
||||
definition_name = Some(name.as_str().to_owned());
|
||||
let parent_id = scopes
|
||||
.iter()
|
||||
.rev()
|
||||
.find(|scope| self.node_kind(&scope.id) == Some(NodeKind::Class))
|
||||
.map(|scope| scope.id.as_str())
|
||||
.unwrap_or(file_id);
|
||||
let id = self.add_symbol(
|
||||
file_id,
|
||||
parent_id,
|
||||
path,
|
||||
language,
|
||||
name.as_str(),
|
||||
|
|
@ -378,7 +384,7 @@ impl ScanState {
|
|||
|
||||
fn add_symbol(
|
||||
&mut self,
|
||||
file_id: &str,
|
||||
parent_id: &str,
|
||||
path: &str,
|
||||
language: &str,
|
||||
name: &str,
|
||||
|
|
@ -397,18 +403,18 @@ impl ScanState {
|
|||
kind,
|
||||
name: name.into(),
|
||||
path: Some(path.into()),
|
||||
parent: Some(file_id.into()),
|
||||
parent: Some(parent_id.into()),
|
||||
language: Some(language.into()),
|
||||
line: Some(line),
|
||||
lines_of_code: 0,
|
||||
attributes: BTreeMap::new(),
|
||||
});
|
||||
self.symbols_by_name
|
||||
.entry(name.into())
|
||||
.entry((language.into(), name.into()))
|
||||
.or_default()
|
||||
.push(id.clone());
|
||||
self.add_edge(
|
||||
file_id.into(),
|
||||
parent_id.into(),
|
||||
id.clone(),
|
||||
EdgeKind::Defines,
|
||||
"brick-lexical",
|
||||
|
|
@ -452,10 +458,13 @@ impl ScanState {
|
|||
None
|
||||
} else {
|
||||
self.symbols_by_name
|
||||
.get(name)
|
||||
.get(&(language.into(), name.into()))
|
||||
.or_else(|| {
|
||||
(!name.contains("::"))
|
||||
.then(|| self.symbols_by_name.get(leaf_name))
|
||||
.then(|| {
|
||||
self.symbols_by_name
|
||||
.get(&(language.into(), leaf_name.into()))
|
||||
})
|
||||
.flatten()
|
||||
})
|
||||
.and_then(|ids| (ids.len() == 1).then(|| ids[0].clone()))
|
||||
|
|
@ -953,6 +962,82 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn does_not_resolve_calls_across_language_boundaries() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
fs::write(dir.path().join("library.rs"), "fn shared() {}\n").unwrap();
|
||||
fs::write(
|
||||
dir.path().join("application.js"),
|
||||
"function local() {}\nfunction caller() { local(); shared(); }\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let building = scan(dir.path()).unwrap();
|
||||
let caller = building
|
||||
.nodes
|
||||
.iter()
|
||||
.find(|node| node.language.as_deref() == Some("javascript") && node.name == "caller")
|
||||
.unwrap();
|
||||
let javascript_local = building
|
||||
.nodes
|
||||
.iter()
|
||||
.find(|node| node.language.as_deref() == Some("javascript") && node.name == "local")
|
||||
.unwrap();
|
||||
let rust_shared = building
|
||||
.nodes
|
||||
.iter()
|
||||
.find(|node| node.language.as_deref() == Some("rust") && node.name == "shared")
|
||||
.unwrap();
|
||||
let caller_edges: Vec<_> = building
|
||||
.edges
|
||||
.iter()
|
||||
.filter(|edge| edge.kind == EdgeKind::Calls && edge.source == caller.id)
|
||||
.collect();
|
||||
|
||||
assert!(
|
||||
caller_edges
|
||||
.iter()
|
||||
.any(|edge| edge.target == javascript_local.id)
|
||||
);
|
||||
assert!(
|
||||
!caller_edges
|
||||
.iter()
|
||||
.any(|edge| edge.target == rust_shared.id)
|
||||
);
|
||||
assert!(caller_edges.iter().any(|edge| {
|
||||
building.nodes.iter().any(|node| {
|
||||
node.id == edge.target && node.kind == NodeKind::External && node.name == "shared"
|
||||
})
|
||||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parents_recognized_methods_to_their_enclosing_type() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
fs::write(
|
||||
dir.path().join("worker.dart"),
|
||||
"class Worker {\n void execute() {}\n}\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let building = scan(dir.path()).unwrap();
|
||||
let worker = building
|
||||
.nodes
|
||||
.iter()
|
||||
.find(|node| node.kind == NodeKind::Class && node.name == "Worker")
|
||||
.unwrap();
|
||||
let execute = building
|
||||
.nodes
|
||||
.iter()
|
||||
.find(|node| node.kind == NodeKind::Function && node.name == "execute")
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(execute.parent.as_deref(), Some(worker.id.as_str()));
|
||||
assert!(building.edges.iter().any(|edge| {
|
||||
edge.kind == EdgeKind::Defines && edge.source == worker.id && edge.target == execute.id
|
||||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scans_dart_structure_without_promoting_control_flow_or_strings() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue