fn analyze_python_dead_code(
lines: &[&str],
dead_functions: &mut usize,
dead_classes: &mut usize,
dead_items: &mut Vec<crate::models::dead_code::DeadCodeItem>,
) {
analyze_python_dead_functions(lines, dead_functions, dead_items);
analyze_python_dead_classes(lines, dead_classes, dead_items);
}
#[allow(clippy::cast_possible_truncation)]
fn analyze_python_dead_functions(
lines: &[&str],
dead_functions: &mut usize,
dead_items: &mut Vec<crate::models::dead_code::DeadCodeItem>,
) {
use crate::models::dead_code::{DeadCodeItem, DeadCodeType};
for (line_num, line) in lines.iter().enumerate() {
let trimmed = line.trim();
if trimmed.starts_with("def _") {
if let Some(function_name) = extract_python_function_name_if_unused(lines, trimmed) {
*dead_functions += 1;
dead_items.push(DeadCodeItem {
item_type: DeadCodeType::Function,
name: function_name,
line: (line_num + 1) as u32,
reason: "Private function with no apparent callers".to_string(),
});
}
}
}
}
#[allow(clippy::cast_possible_truncation)]
fn analyze_python_dead_classes(
lines: &[&str],
dead_classes: &mut usize,
dead_items: &mut Vec<crate::models::dead_code::DeadCodeItem>,
) {
use crate::models::dead_code::{DeadCodeItem, DeadCodeType};
for (line_num, line) in lines.iter().enumerate() {
let trimmed = line.trim();
if trimmed.starts_with("class _") {
if let Some(class_name) = extract_python_class_name_if_unused(lines, trimmed) {
*dead_classes += 1;
dead_items.push(DeadCodeItem {
item_type: DeadCodeType::Class,
name: class_name,
line: (line_num + 1) as u32,
reason: "Private class with no apparent usage".to_string(),
});
}
}
}
}
fn extract_python_function_name_if_unused(lines: &[&str], trimmed: &str) -> Option<String> {
let function_name = extract_python_function_name(trimmed);
if !function_name.is_empty() && !is_function_called_in_file(lines, &function_name) {
Some(function_name)
} else {
None
}
}
fn extract_python_class_name_if_unused(lines: &[&str], trimmed: &str) -> Option<String> {
let class_name = extract_python_class_name(trimmed);
if !class_name.is_empty() && !is_type_used_in_file(lines, &class_name) {
Some(class_name)
} else {
None
}
}
fn extract_function_name(line: &str) -> String {
if let Some(start) = line.find("fn ") {
let after_fn = line.get(start + 3..).unwrap_or_default();
if let Some(paren_pos) = after_fn.find('(') {
after_fn
.get(..paren_pos)
.unwrap_or_default()
.trim()
.to_string()
} else {
String::with_capacity(1024)
}
} else {
String::with_capacity(1024)
}
}
fn extract_struct_name(line: &str) -> String {
if let Some(start) = line.find("struct ") {
let after_struct = line.get(start + 7..).unwrap_or_default();
after_struct
.split_whitespace()
.next()
.unwrap_or("")
.to_string()
} else {
String::with_capacity(1024)
}
}
fn extract_js_function_name(line: &str) -> String {
if let Some(start) = line.find("function ") {
let after_fn = line.get(start + 9..).unwrap_or_default();
if let Some(paren_pos) = after_fn.find('(') {
after_fn
.get(..paren_pos)
.unwrap_or_default()
.trim()
.to_string()
} else {
String::with_capacity(1024)
}
} else {
String::with_capacity(1024)
}
}
fn extract_class_name(line: &str) -> String {
if let Some(start) = line.find("class ") {
let after_class = line.get(start + 6..).unwrap_or_default();
after_class
.split_whitespace()
.next()
.unwrap_or("")
.to_string()
} else {
String::with_capacity(1024)
}
}
fn extract_python_function_name(line: &str) -> String {
if let Some(start) = line.find("def ") {
let after_def = line.get(start + 4..).unwrap_or_default();
if let Some(paren_pos) = after_def.find('(') {
after_def
.get(..paren_pos)
.unwrap_or_default()
.trim()
.to_string()
} else {
String::with_capacity(1024)
}
} else {
String::with_capacity(1024)
}
}
fn extract_python_class_name(line: &str) -> String {
if let Some(start) = line.find("class ") {
let after_class = line.get(start + 6..).unwrap_or_default();
if let Some(colon_pos) = after_class.find(':') {
after_class
.get(..colon_pos)
.unwrap_or_default()
.trim()
.split('(')
.next()
.unwrap_or("")
.trim()
.to_string()
} else {
after_class
.split_whitespace()
.next()
.unwrap_or("")
.to_string()
}
} else {
String::with_capacity(1024)
}
}
fn is_function_called_in_file(lines: &[&str], function_name: &str) -> bool {
let call_pattern = format!("{function_name}(");
lines.iter().any(|line| line.contains(&call_pattern))
}
fn is_type_used_in_file(lines: &[&str], type_name: &str) -> bool {
lines.iter().any(|line| {
line.contains(type_name)
&& (line.contains(&format!("new {type_name}"))
|| line.contains(&format!(": {type_name}"))
|| line.contains(&format!("<{type_name}>")))
})
}