use crate::analyze::const_eval::{try_evaluate_expr, MacroConstantMap};
use crate::utility::cert_c::ast_utils::find_identifier_in_declarator;
use tree_sitter::Node;
pub fn resolve_declared_array_size(
use_node: &Node,
var_name: &str,
source: &str,
macros: &MacroConstantMap,
) -> Option<usize> {
let arr_decl = find_array_declarator_in_scope(use_node, var_name, source)?;
let declared = match arr_decl.child_by_field_name("size") {
Some(size_expr) => match try_evaluate_expr(&size_expr, source, macros) {
Some(v) if v > 0 => Some(v as usize),
_ => return None,
},
None => None,
};
let init_count = arr_decl
.parent()
.filter(|p| p.kind() == "init_declarator")
.and_then(|p| p.child_by_field_name("value"))
.filter(|v| v.kind() == "initializer_list")
.and_then(|v| count_initializer_elements(&v));
match (declared, init_count) {
(Some(d), Some(c)) => Some(d.max(c)),
(Some(d), None) => Some(d),
(None, Some(c)) => Some(c),
(None, None) => None,
}
}
fn find_array_declarator_in_scope<'a>(
use_node: &Node<'a>,
var_name: &str,
source: &str,
) -> Option<Node<'a>> {
let before = use_node.start_byte();
let mut scope = use_node.parent();
while let Some(s) = scope {
if let Some(found) = scan_block_declarators(&s, var_name, source, before) {
return Some(found);
}
scope = s.parent();
}
None
}
fn scan_block_declarators<'a>(
scope: &Node<'a>,
var_name: &str,
source: &str,
before: usize,
) -> Option<Node<'a>> {
for i in 0..scope.child_count() {
if let Some(child) = scope.child(i) {
if matches!(child.kind(), "compound_statement" | "function_definition") {
continue;
}
if child.kind() == "array_declarator"
&& child.start_byte() < before
&& find_identifier_in_declarator(&child, source).as_deref() == Some(var_name)
{
return Some(child);
}
if let Some(found) = scan_block_declarators(&child, var_name, source, before) {
return Some(found);
}
}
}
None
}
fn count_initializer_elements(list: &Node) -> Option<usize> {
let mut count = 0;
for i in 0..list.child_count() {
if let Some(child) = list.child(i) {
match child.kind() {
"{" | "}" | "," | "comment" => {}
"initializer_pair" => return None,
_ => count += 1,
}
}
}
if count > 0 {
Some(count)
} else {
None
}
}