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>,
|
nodes: Vec<Node>,
|
||||||
edges: Vec<Edge>,
|
edges: Vec<Edge>,
|
||||||
edge_keys: HashSet<(String, String, EdgeKind)>,
|
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>>,
|
files_by_key: HashMap<String, Vec<String>>,
|
||||||
pending: Vec<PendingRelationship>,
|
pending: Vec<PendingRelationship>,
|
||||||
diagnostics: Vec<Diagnostic>,
|
diagnostics: Vec<Diagnostic>,
|
||||||
|
|
@ -315,8 +315,14 @@ impl ScanState {
|
||||||
&& !is_keyword(name.as_str())
|
&& !is_keyword(name.as_str())
|
||||||
{
|
{
|
||||||
definition_name = Some(name.as_str().to_owned());
|
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(
|
let id = self.add_symbol(
|
||||||
file_id,
|
parent_id,
|
||||||
path,
|
path,
|
||||||
language,
|
language,
|
||||||
name.as_str(),
|
name.as_str(),
|
||||||
|
|
@ -378,7 +384,7 @@ impl ScanState {
|
||||||
|
|
||||||
fn add_symbol(
|
fn add_symbol(
|
||||||
&mut self,
|
&mut self,
|
||||||
file_id: &str,
|
parent_id: &str,
|
||||||
path: &str,
|
path: &str,
|
||||||
language: &str,
|
language: &str,
|
||||||
name: &str,
|
name: &str,
|
||||||
|
|
@ -397,18 +403,18 @@ impl ScanState {
|
||||||
kind,
|
kind,
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
path: Some(path.into()),
|
path: Some(path.into()),
|
||||||
parent: Some(file_id.into()),
|
parent: Some(parent_id.into()),
|
||||||
language: Some(language.into()),
|
language: Some(language.into()),
|
||||||
line: Some(line),
|
line: Some(line),
|
||||||
lines_of_code: 0,
|
lines_of_code: 0,
|
||||||
attributes: BTreeMap::new(),
|
attributes: BTreeMap::new(),
|
||||||
});
|
});
|
||||||
self.symbols_by_name
|
self.symbols_by_name
|
||||||
.entry(name.into())
|
.entry((language.into(), name.into()))
|
||||||
.or_default()
|
.or_default()
|
||||||
.push(id.clone());
|
.push(id.clone());
|
||||||
self.add_edge(
|
self.add_edge(
|
||||||
file_id.into(),
|
parent_id.into(),
|
||||||
id.clone(),
|
id.clone(),
|
||||||
EdgeKind::Defines,
|
EdgeKind::Defines,
|
||||||
"brick-lexical",
|
"brick-lexical",
|
||||||
|
|
@ -452,10 +458,13 @@ impl ScanState {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
self.symbols_by_name
|
self.symbols_by_name
|
||||||
.get(name)
|
.get(&(language.into(), name.into()))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
(!name.contains("::"))
|
(!name.contains("::"))
|
||||||
.then(|| self.symbols_by_name.get(leaf_name))
|
.then(|| {
|
||||||
|
self.symbols_by_name
|
||||||
|
.get(&(language.into(), leaf_name.into()))
|
||||||
|
})
|
||||||
.flatten()
|
.flatten()
|
||||||
})
|
})
|
||||||
.and_then(|ids| (ids.len() == 1).then(|| ids[0].clone()))
|
.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]
|
#[test]
|
||||||
fn scans_dart_structure_without_promoting_control_flow_or_strings() {
|
fn scans_dart_structure_without_promoting_control_flow_or_strings() {
|
||||||
let dir = tempfile::tempdir().unwrap();
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue