use std::collections::HashMap;
use petgraph::graph::NodeIndex;
use petgraph::Direction;
use super::super::ast::YieldItem;
use super::super::result::ResultRow;
use super::rule_procedures::{make_node_row, require_node_yield, type_indices};
use crate::datatypes::values::Value;
use crate::graph::dir_graph::DirGraph;
use crate::graph::schema::InternedKey;
use crate::graph::storage::GraphRead;
const PROC: &str = "dead_code";
fn is_used(graph: &DirGraph, nidx: NodeIndex) -> bool {
let calls = InternedKey::from_str("CALLS");
let refs_fn = InternedKey::from_str("REFERENCES_FN");
let handles = InternedKey::from_str("HANDLES");
let implemented_by = InternedKey::from_str("IMPLEMENTED_BY");
let decorates = InternedKey::from_str("DECORATES");
for er in graph.graph.edges_directed(nidx, Direction::Incoming) {
let k = er.weight().connection_type;
if k == calls || k == refs_fn || k == handles || k == implemented_by || k == decorates {
return true;
}
}
for er in graph.graph.edges_directed(nidx, Direction::Outgoing) {
if er.weight().connection_type == decorates {
return true;
}
}
false
}
fn bool_param(params: &HashMap<String, Value>, key: &str) -> bool {
matches!(params.get(key), Some(Value::Boolean(true)))
}
fn short_name(qname: &str) -> &str {
qname.rsplit(['.', ':']).next().unwrap_or(qname)
}
pub(super) fn execute_dead_code(
graph: &DirGraph,
params: &HashMap<String, Value>,
yield_items: &[YieldItem],
) -> Result<Vec<ResultRow>, String> {
let yield_var = require_node_yield(yield_items, PROC, "node")?;
let exclude_public = bool_param(params, "exclude_public");
let include_tests = bool_param(params, "include_tests");
let funcs = match type_indices(graph, "Function") {
Ok(f) => f,
Err(_) => return Ok(Vec::new()), };
const PUBLIC_VIS: &[&str] = &["pub", "public", "export", "exported"];
let mut rows = Vec::new();
for nidx in funcs.iter() {
let node = match graph.graph.node_weight(nidx) {
Some(n) => n,
None => continue,
};
if !include_tests
&& matches!(
node.get_property("is_test").as_deref(),
Some(Value::Boolean(true))
)
{
continue;
}
if exclude_public {
if let Some(Value::String(v)) = node.get_property("visibility").as_deref() {
if PUBLIC_VIS.contains(&v.as_str()) {
continue;
}
}
}
if let Value::String(qn) = node.id().as_ref() {
let short = short_name(qn);
if short == "main" || (short.starts_with("__") && short.ends_with("__")) {
continue;
}
}
if is_used(graph, nidx) {
continue;
}
rows.push(make_node_row(&yield_var, nidx));
}
Ok(rows)
}