use alloc::string::String;
use crate::stack::build::scopes::is_statement;
use crate::stack::build::StackBuilder;
use crate::stack::{Definition, ScopeType, StackNode, StackScope, SymbolPop};
use crate::syntax::{SyntaxGraph, SyntaxNode};
use crate::{CodeGraph, Language, NodeId};
#[derive(Clone, Copy)]
pub(super) struct Members {
pub(super) gate: NodeId,
scope: NodeId,
}
impl StackBuilder {
pub(super) fn add_members(&mut self, class: NodeId) {
let scope = self.add(StackNode::Scope(StackScope {
scope_type: Some(ScopeType::Class),
}));
let gate = self.add(StackNode::PopSymbol(SymbolPop {
symbol: String::from("."),
precedence: 0,
definition: None,
import: None,
}));
self.graph.add_edge(gate, scope, 0);
self.members.insert(class, Members { gate, scope });
}
pub(super) fn add_member(
&mut self,
language: Language,
class: NodeId,
name: &str,
member: NodeId,
) {
if let Some(members) = self.members.get(&class).copied() {
self.define(members.scope, name, member);
}
if !matches!(language, Language::JavaScript | Language::TypeScript) {
self.define(class, name, member);
}
}
pub(super) fn add_receiver(
&mut self,
syntax: &SyntaxGraph,
language: Language,
class: NodeId,
method: NodeId,
) {
let Some(members) = self.members.get(&class).copied() else {
return;
};
let receiver = match language {
Language::Python => python_receiver(syntax, method).map(|(id, name)| {
self.graph.add_node(id, receiver_definition(name));
id
}),
Language::JavaScript | Language::TypeScript
if matches!(
syntax.nodes.get(&method),
Some(SyntaxNode::MethodDeclaration { .. })
) =>
{
Some(self.add(receiver_definition("this")))
}
Language::Java | Language::CSharp => match syntax.nodes.get(&method) {
Some(SyntaxNode::MethodDeclaration {
access_modifiers, ..
}) if !access_modifiers
.iter()
.flat_map(|words| words.split_whitespace())
.any(|word| word == "static") =>
{
Some(self.add(receiver_definition("this")))
}
_ => None,
},
_ => None,
};
if let Some(receiver) = receiver {
self.graph.add_edge(method, receiver, 1);
self.graph.add_edge(receiver, members.gate, 0);
}
}
}
fn receiver_definition(name: &str) -> StackNode {
StackNode::PopSymbol(SymbolPop {
symbol: name.into(),
precedence: 1,
definition: Some(Definition::default()),
import: None,
})
}
fn python_receiver(syntax: &SyntaxGraph, method: NodeId) -> Option<(NodeId, &str)> {
let Some(SyntaxNode::MethodDeclaration {
parameters_id: Some(parameters),
..
}) = syntax.nodes.get(&method)
else {
return None;
};
if !is_statement(syntax, method) || is_static(syntax, method) {
return None;
}
let first = syntax.ast_children(*parameters).into_iter().next()?;
let node = syntax.nodes.get(&first)?;
Some((first, node.symbol().or_else(|| node.variable())?))
}
fn is_static(syntax: &SyntaxGraph, method: NodeId) -> bool {
syntax
.ast_children(method)
.into_iter()
.filter(|child| matches!(syntax.nodes.get(child), Some(SyntaxNode::Modifiers)))
.flat_map(|modifiers| syntax.ast_children(modifiers))
.any(|decorator| {
syntax.nodes.get(&decorator).and_then(SyntaxNode::symbol) == Some("staticmethod")
})
}