use super::ast_utils::find_containing_function;
use tree_sitter::Node;
pub fn find_element_size(var_name: &str, preceding_text: &str) -> usize {
let type_sizes = [
("char", 1),
("short", 2),
("int", 4),
("long", 8),
("float", 4),
("double", 8),
("unsigned char", 1),
("unsigned short", 2),
("unsigned int", 4),
("unsigned long", 8),
("signed char", 1),
("signed short", 2),
("signed int", 4),
("signed long", 8),
];
let pattern = format!("{}[", var_name);
if let Some(pos) = preceding_text.rfind(&pattern) {
let before_array = &preceding_text[..pos];
for (type_name, size) in &type_sizes {
if before_array.ends_with(type_name)
|| before_array.ends_with(&format!("{} ", type_name))
{
return *size;
}
}
}
4
}
pub fn find_string_literal_length(var_name: &str, node: &Node, source: &str) -> Option<usize> {
let function_node = find_containing_function(node)?;
let function_start = function_node.start_byte();
let call_position = node.start_byte();
let preceding_text = &source[function_start..call_position];
let pattern = format!("{} =", var_name);
if let Some(init_pos) = preceding_text.rfind(&pattern) {
let after_eq = &preceding_text[init_pos + pattern.len()..];
if let Some(quote_start) = after_eq.find('"') {
let mut i = quote_start + 1;
let chars: Vec<char> = after_eq.chars().collect();
while i < chars.len() {
if chars[i] == '"' && (i == 0 || chars[i - 1] != '\\') {
let literal = &after_eq[quote_start + 1..i];
return Some(literal.len());
}
i += 1;
}
}
}
None
}
pub fn find_allocation_size(ptr_name: &str, preceding_text: &str) -> Option<usize> {
let realloc_pattern = format!("{} = realloc", ptr_name);
let malloc_pattern = format!("{} = malloc", ptr_name);
let ptr_realloc_pattern = format!("*{} = realloc", ptr_name);
let ptr_malloc_pattern = format!("*{} = malloc", ptr_name);
let realloc_pos = preceding_text
.rfind(&realloc_pattern)
.or_else(|| preceding_text.rfind(&ptr_realloc_pattern));
let malloc_pos = preceding_text
.rfind(&malloc_pattern)
.or_else(|| preceding_text.rfind(&ptr_malloc_pattern));
let (_pattern, pos) = match (malloc_pos, realloc_pos) {
(Some(m), Some(r)) => {
if r > m {
("realloc", r)
} else {
("malloc", m)
}
}
(Some(m), None) => ("malloc", m),
(None, Some(r)) => ("realloc", r),
(None, None) => return None,
};
let after_call = &preceding_text[pos..];
if let Some(paren_start) = after_call.find('(') {
if let Some(paren_end) = after_call.find(')') {
let mut args = &after_call[paren_start + 1..paren_end];
if after_call.contains("realloc") {
if let Some(comma_pos) = args.find(',') {
args = args[comma_pos + 1..].trim();
}
}
if args.contains("sizeof") {
let parts: Vec<&str> = args.split('*').collect();
if let Some(size_str) = parts.first() {
let size_str = size_str.trim();
if let Ok(size) = size_str.parse::<usize>() {
return Some(size);
}
}
}
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_find_element_size() {
let code = "int numbers[10];";
assert_eq!(find_element_size("numbers", code), 4);
let code2 = "char buffer[100];";
assert_eq!(find_element_size("buffer", code2), 1);
let code3 = "double values[5];";
assert_eq!(find_element_size("values", code3), 8);
let code4 = "unknown_type data[20];";
assert_eq!(find_element_size("data", code4), 4); }
#[test]
fn test_find_allocation_size() {
let code = "int *arr = malloc(10 * sizeof(int));";
assert_eq!(find_allocation_size("arr", code), Some(10));
let code2 = "char *buf = realloc(buf, 5 * sizeof(char));";
assert_eq!(find_allocation_size("buf", code2), Some(5));
let code3 = "ptr = malloc(100*sizeof(int));";
assert_eq!(find_allocation_size("ptr", code3), Some(100));
let code4 = "int *p;"; assert_eq!(find_allocation_size("p", code4), None);
}
#[test]
fn test_find_allocation_size_realloc_priority() {
let code = "ptr = malloc(5 * sizeof(int)); ptr = realloc(ptr, 10 * sizeof(int));";
assert_eq!(find_allocation_size("ptr", code), Some(10));
}
}