use crate::utility::cert_c::ast_utils;
use std::collections::HashMap;
use tree_sitter::Node;
pub type StructFieldTypes = HashMap<String, HashMap<String, String>>;
pub fn is_float_type(type_str: &str) -> bool {
type_str
.split(|c: char| !(c.is_alphanumeric() || c == '_'))
.any(|tok| matches!(tok, "float" | "double" | "float_t" | "double_t"))
}
pub fn is_float_literal(text: &str) -> bool {
let t = text.trim();
if t.starts_with("0x") || t.starts_with("0X") {
return t.contains('p') || t.contains('P');
}
t.contains('.') || t.contains('e') || t.contains('E') || t.ends_with('f') || t.ends_with('F')
}
pub fn expr_is_float(
node: &Node,
source: &str,
type_map: &HashMap<String, String>,
struct_field_types: &StructFieldTypes,
) -> bool {
match node.kind() {
"number_literal" => is_float_literal(ast_utils::get_node_text(node, source)),
"identifier" => {
let name = ast_utils::get_node_text(node, source);
type_map
.get(name)
.map(|t| is_float_type(t))
.unwrap_or(false)
}
"field_expression" => {
ast_utils::resolve_field_expression_type(node, source, type_map, struct_field_types)
.map(|t| is_float_type(&t))
.unwrap_or(false)
}
"cast_expression" => {
if let Some(t) = node.child_by_field_name("type") {
is_float_type(ast_utils::get_node_text(&t, source))
} else if let Some(v) = node.child_by_field_name("value") {
expr_is_float(&v, source, type_map, struct_field_types)
} else {
false
}
}
"parenthesized_expression" => node
.named_child(0)
.map(|c| expr_is_float(&c, source, type_map, struct_field_types))
.unwrap_or(false),
"unary_expression" | "pointer_expression" => node
.child_by_field_name("argument")
.map(|a| expr_is_float(&a, source, type_map, struct_field_types))
.unwrap_or(false),
"binary_expression" => {
let l = node
.child_by_field_name("left")
.map(|n| expr_is_float(&n, source, type_map, struct_field_types))
.unwrap_or(false);
let r = node
.child_by_field_name("right")
.map(|n| expr_is_float(&n, source, type_map, struct_field_types))
.unwrap_or(false);
l || r
}
_ => false,
}
}
pub fn expr_is_definitely_integer(
node: &Node,
source: &str,
type_map: &HashMap<String, String>,
) -> bool {
match node.kind() {
"number_literal" => !is_float_literal(ast_utils::get_node_text(node, source)),
"identifier" => {
let name = ast_utils::get_node_text(node, source);
match type_map.get(name) {
Some(t) => !is_float_type(t) && !t.contains('*'),
None => false,
}
}
"parenthesized_expression" => node
.named_child(0)
.map(|c| expr_is_definitely_integer(&c, source, type_map))
.unwrap_or(false),
"unary_expression" => node
.child_by_field_name("argument")
.map(|a| expr_is_definitely_integer(&a, source, type_map))
.unwrap_or(false),
"binary_expression" => {
let l = node
.child_by_field_name("left")
.map(|n| expr_is_definitely_integer(&n, source, type_map))
.unwrap_or(false);
let r = node
.child_by_field_name("right")
.map(|n| expr_is_definitely_integer(&n, source, type_map))
.unwrap_or(false);
l && r
}
_ => false,
}
}
pub fn collect_variable_types(node: &Node, source: &str) -> HashMap<String, String> {
let mut type_map = HashMap::new();
if node.kind() == "function_definition" {
if let Some(declarator) = node.child_by_field_name("declarator") {
collect_params_from_declarator(&declarator, source, &mut type_map);
}
if let Some(body) = node.child_by_field_name("body") {
collect_local_declarations(&body, source, &mut type_map);
}
}
type_map
}
fn collect_params_from_declarator(
node: &Node,
source: &str,
type_map: &mut HashMap<String, String>,
) {
if node.kind() == "function_declarator" {
if let Some(params) = node.child_by_field_name("parameters") {
for i in 0..params.child_count() {
if let Some(param) = params.child(i) {
if param.kind() == "parameter_declaration" {
extract_type_and_name(¶m, source, type_map);
}
}
}
}
}
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
collect_params_from_declarator(&child, source, type_map);
}
}
}
fn collect_local_declarations(node: &Node, source: &str, type_map: &mut HashMap<String, String>) {
if node.kind() == "declaration" {
extract_type_and_name(node, source, type_map);
}
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
collect_local_declarations(&child, source, type_map);
}
}
}
fn extract_type_and_name(node: &Node, source: &str, type_map: &mut HashMap<String, String>) {
let mut type_text = String::new();
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
match child.kind() {
"primitive_type"
| "sized_type_specifier"
| "type_identifier"
| "struct_specifier" => {
type_text = ast_utils::get_node_text(&child, source).to_string();
}
_ => {}
}
}
}
if type_text.is_empty() {
return;
}
if let Some(declarator) = node.child_by_field_name("declarator") {
let is_pointer = declarator.kind() == "pointer_declarator";
if let Some(name) = extract_identifier_name(&declarator, source) {
let full_type = if is_pointer {
format!("{} *", type_text)
} else {
type_text.clone()
};
type_map.insert(name, full_type);
}
}
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
if child.kind() == "init_declarator" {
if let Some(decl) = child.child_by_field_name("declarator") {
let is_pointer = decl.kind() == "pointer_declarator";
if let Some(name) = extract_identifier_name(&decl, source) {
let full_type = if is_pointer {
format!("{} *", type_text)
} else {
type_text.clone()
};
type_map.insert(name, full_type);
}
}
}
}
}
}
fn extract_identifier_name(node: &Node, source: &str) -> Option<String> {
match node.kind() {
"identifier" => Some(ast_utils::get_node_text(node, source).to_string()),
"pointer_declarator" | "array_declarator" | "parenthesized_declarator" => node
.child_by_field_name("declarator")
.and_then(|inner| extract_identifier_name(&inner, source)),
_ => {
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
if child.kind() == "identifier" {
return Some(ast_utils::get_node_text(&child, source).to_string());
}
}
}
None
}
}
}