use crate::stack::StackGraph;
use crate::syntax::SyntaxGraph;
use crate::NodeId;
#[must_use]
pub fn definition_site(
stack: &StackGraph,
syntax: &SyntaxGraph,
definition: NodeId,
) -> Option<NodeId> {
if syntax.nodes.contains_key(&definition) {
return Some(definition);
}
stack
.successors(definition)
.into_iter()
.map(|(sink, _)| sink)
.find(|sink| syntax.nodes.contains_key(sink))
}
#[cfg(test)]
mod tests {
use super::definition_site;
use crate::stack::{
build_stack_graph, Definition, StackGraph, StackNode, StackScope, SymbolPop,
};
use crate::syntax::{SyntaxGraph, SyntaxNode};
use crate::{Language, NodeId};
use alloc::borrow::ToOwned;
use alloc::string::String;
const FILE: NodeId = NodeId(1);
const BLOCK: NodeId = NodeId(2);
const FUNCTION: NodeId = NodeId(3);
const SYNTHETIC: NodeId = NodeId(90);
const SYNTHETIC_SCOPE: NodeId = NodeId(91);
fn definition(symbol: &str) -> StackNode {
StackNode::PopSymbol(SymbolPop {
symbol: symbol.to_owned(),
precedence: 1,
definition: Some(Definition::default()),
import: None,
})
}
fn function_statement() -> SyntaxGraph {
let mut syntax = SyntaxGraph::new();
syntax.add_node(FILE, SyntaxNode::File);
syntax.add_node(BLOCK, SyntaxNode::ExecutionBlock);
syntax.add_ast_edge(FILE, BLOCK);
syntax.add_node(
FUNCTION,
SyntaxNode::MethodDeclaration {
name: Some(String::from("f")),
access_modifiers: None,
block_id: None,
modifiers_id: None,
parameters_id: None,
},
);
syntax.add_ast_edge(BLOCK, FUNCTION);
syntax.scope_parent.insert(FUNCTION, FILE);
syntax
}
#[test]
fn a_function_definition_stands_for_its_declaration() {
let syntax = function_statement();
let stack = build_stack_graph(&syntax, Language::Python, "mod.py");
let definition = stack
.nodes
.iter()
.find_map(|(id, node)| (node.is_definition() && *id != FUNCTION).then_some(*id))
.expect("the function statement defines its name");
assert_eq!(definition_site(&stack, &syntax, definition), Some(FUNCTION));
}
#[test]
fn a_definition_with_a_syntax_id_stands_for_itself() {
let syntax = function_statement();
let mut stack = StackGraph::new();
stack.add_node(FUNCTION, definition("f"));
assert_eq!(definition_site(&stack, &syntax, FUNCTION), Some(FUNCTION));
}
#[test]
fn a_definition_that_leads_to_no_syntax_node_stands_for_nothing() {
let syntax = function_statement();
let mut stack = StackGraph::new();
stack.add_node(SYNTHETIC, definition("f"));
stack.add_node(SYNTHETIC_SCOPE, StackNode::Scope(StackScope::default()));
stack.add_edge(SYNTHETIC, SYNTHETIC_SCOPE, 0);
assert_eq!(definition_site(&stack, &syntax, SYNTHETIC), None);
}
}