use alloc::borrow::ToOwned;
use alloc::format;
use alloc::string::String;
use crate::syntax::builders::utils::{
get_event_subpath, receiver_base_symbol, register_usage_event, setup_scope_index_binding,
};
use crate::syntax::metadata::java::del_metadata_instance;
use crate::syntax::{SyntaxGraph, SyntaxGraphArgs, SyntaxGraphError, SyntaxNode, UsageRole};
use crate::{Language, NodeId};
pub fn build_assignment_node(
args: &mut SyntaxGraphArgs<'_>,
n_id: NodeId,
variable_id: NodeId,
value_id: Option<NodeId>,
operator: Option<String>,
) -> Result<NodeId, SyntaxGraphError> {
args.syntax_graph.add_node(
n_id,
SyntaxNode::Assignment {
variable_id,
value_id,
operator: operator.filter(|value| !value.is_empty()),
},
);
let built_variable = args.generic(variable_id)?;
args.syntax_graph.add_ast_edge(n_id, built_variable);
if let Some(value_id) = value_id {
let built_value = args.generic(value_id)?;
args.syntax_graph.add_ast_edge(n_id, built_value);
}
if let Some(value_id) = value_id {
if args.language == Language::Java && args.syntax_graph.nodes.contains_key(&NodeId(0)) {
del_metadata_instance(args, variable_id, value_id)?;
}
}
setup_scope_index_binding(
args.ast_graph,
args.syntax_graph,
args.metadata,
variable_id,
n_id,
);
index_member_write(args, n_id, variable_id);
Ok(n_id)
}
fn index_member_write(args: &mut SyntaxGraphArgs<'_>, n_id: NodeId, var_id: NodeId) {
let (base, dotted) = member_write_names(args.syntax_graph, var_id);
let Some(base) = base else {
return;
};
let subpath = get_event_subpath(args.metadata);
register_usage_event(
args.syntax_graph,
args.metadata,
n_id,
&base,
UsageRole::MemberWrite,
None,
subpath,
);
if let Some(dotted) = dotted {
if dotted != base {
register_usage_event(
args.syntax_graph,
args.metadata,
n_id,
&dotted,
UsageRole::MemberWrite,
None,
subpath,
);
}
}
}
fn member_write_names(graph: &SyntaxGraph, var_id: NodeId) -> (Option<String>, Option<String>) {
match graph.nodes.get(&var_id) {
Some(SyntaxNode::MemberAccess {
expression, member, ..
}) => {
let dotted =
if !expression.is_empty() && !member.is_empty() && !expression.contains('(') {
Some(format!("{expression}.{member}"))
} else {
None
};
(receiver_base_symbol(graph, var_id), dotted)
}
Some(SyntaxNode::SymbolLookup { symbol, .. }) => {
if symbol.contains('.') && !symbol.contains('(') {
let base = symbol.split('.').next().unwrap_or(symbol.as_str());
(Some(base.to_owned()), Some(symbol.clone()))
} else {
(None, None)
}
}
_ => (None, None),
}
}
#[cfg(test)]
mod tests {
use super::member_write_names;
use crate::syntax::{SyntaxGraph, SyntaxNode};
use crate::NodeId;
use alloc::borrow::ToOwned;
#[test]
fn member_write_names_reads_member_access_base_and_dotted() {
let mut graph = SyntaxGraph::new();
graph.add_node(
NodeId(1),
SyntaxNode::SymbolLookup {
symbol: "user".to_owned(),
symbol_scope: None,
value: None,
},
);
graph.add_node(
NodeId(2),
SyntaxNode::MemberAccess {
member: "name".to_owned(),
expression: "user".to_owned(),
expression_id: NodeId(1),
symbol_scope: None,
},
);
assert_eq!(
member_write_names(&graph, NodeId(2)),
(Some("user".to_owned()), Some("user.name".to_owned()))
);
}
#[test]
fn member_write_names_skips_call_expressions() {
let mut graph = SyntaxGraph::new();
graph.add_node(
NodeId(1),
SyntaxNode::MemberAccess {
member: "name".to_owned(),
expression: "get()".to_owned(),
expression_id: NodeId(9),
symbol_scope: None,
},
);
assert_eq!(member_write_names(&graph, NodeId(1)).1, None);
}
#[test]
fn member_write_names_splits_dotted_symbol_lookup() {
let mut graph = SyntaxGraph::new();
graph.add_node(
NodeId(1),
SyntaxNode::SymbolLookup {
symbol: "obj.field".to_owned(),
symbol_scope: None,
value: None,
},
);
assert_eq!(
member_write_names(&graph, NodeId(1)),
(Some("obj".to_owned()), Some("obj.field".to_owned()))
);
}
#[test]
fn member_write_names_ignores_plain_symbol_and_other_nodes() {
let mut graph = SyntaxGraph::new();
graph.add_node(
NodeId(1),
SyntaxNode::SymbolLookup {
symbol: "x".to_owned(),
symbol_scope: None,
value: None,
},
);
graph.add_node(NodeId(2), SyntaxNode::ArrayInitializer);
assert_eq!(member_write_names(&graph, NodeId(1)), (None, None));
assert_eq!(member_write_names(&graph, NodeId(2)), (None, None));
}
}