use super::super::{CertRule, RuleViolation};
use crate::manifest::{RuleCategory, Severity};
use crate::utility::cert_c::ast_utils::get_node_text;
use tree_sitter::Node;
pub struct Arr02C;
impl CertRule for Arr02C {
fn rule_id(&self) -> &'static str {
"ARR02-C"
}
fn description(&self) -> &'static str {
"Explicitly specify array bounds, even if implicitly defined by an initializer"
}
fn severity(&self) -> Severity {
Severity::Medium
}
fn category(&self) -> RuleCategory {
RuleCategory::Recommendation
}
fn cert_id(&self) -> &'static str {
"ARR02-C"
}
fn check(&self, node: &Node, source: &str) -> Vec<RuleViolation> {
let mut violations = Vec::new();
self.check_declarations(node, source, &mut violations);
violations
}
}
impl Arr02C {
fn has_extern_specifier(node: &Node, source: &str) -> bool {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "storage_class_specifier" {
let text = get_node_text(&child, source);
if text == "extern" {
return true;
}
}
}
false
}
fn check_declarations(&self, node: &Node, source: &str, violations: &mut Vec<RuleViolation>) {
if node.kind() == "declaration" {
if Self::has_extern_specifier(node, source) {
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
self.check_declarations(&child, source, violations);
}
}
return;
}
if let Some(declarator) = node.child_by_field_name("declarator") {
self.check_declarator(&declarator, source, violations);
}
}
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
self.check_declarations(&child, source, violations);
}
}
}
fn check_declarator(&self, node: &Node, source: &str, violations: &mut Vec<RuleViolation>) {
match node.kind() {
"init_declarator" => {
if let Some(declarator) = node.child_by_field_name("declarator") {
let has_string_init = node
.child_by_field_name("value")
.map(|v| v.kind() == "string_literal" || v.kind() == "concatenated_string")
.unwrap_or(false);
if !has_string_init {
self.check_array_declarator(&declarator, source, violations);
}
if let Some(value) = node.child_by_field_name("value") {
self.check_initializer_size(&declarator, &value, source, violations);
}
}
}
"array_declarator" => {
self.check_array_declarator(node, source, violations);
}
_ => {
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
self.check_declarator(&child, source, violations);
}
}
}
}
}
fn check_array_declarator(
&self,
node: &Node,
source: &str,
violations: &mut Vec<RuleViolation>,
) {
if node.kind() == "array_declarator" {
if node.child_by_field_name("size").is_none() {
let has_brackets = node
.children(&mut node.walk())
.any(|child| child.kind() == "[" || child.kind() == "]");
if has_brackets {
let line = node.start_position().row + 1;
let column = node.start_position().column + 1;
let text = get_node_text(node, source);
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
severity: self.severity(),
file_path: String::new(),
line,
column,
message: format!(
"Array declaration '{}' has implicit bounds; specify explicit size",
text
),
suggestion: Some(
"Explicitly specify array bounds even when using an initializer"
.to_string(),
),
requires_manual_review: None,
});
}
}
if let Some(declarator) = node.child_by_field_name("declarator") {
self.check_array_declarator(&declarator, source, violations);
}
}
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
if child.kind() == "array_declarator" {
self.check_array_declarator(&child, source, violations);
}
}
}
}
fn check_initializer_size(
&self,
declarator: &Node,
initializer: &Node,
source: &str,
violations: &mut Vec<RuleViolation>,
) {
if let Some(array_size) = self.extract_array_size(declarator, source) {
if let Some(init_count) = self.count_initializer_elements(initializer) {
if init_count > array_size {
let line = declarator.start_position().row + 1;
let column = declarator.start_position().column + 1;
let text = get_node_text(declarator, source);
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
severity: self.severity(),
file_path: String::new(),
line,
column,
message: format!(
"Array '{}' of size {} has {} initializers (excess elements)",
text, array_size, init_count
),
suggestion: Some(format!(
"Either increase array size to {} or reduce number of initializers",
init_count
)),
requires_manual_review: None,
});
}
}
}
}
fn extract_array_size(&self, node: &Node, source: &str) -> Option<usize> {
if node.kind() == "array_declarator" {
if let Some(size_node) = node.child_by_field_name("size") {
let size_text = get_node_text(&size_node, source);
if let Ok(size) = size_text.parse::<usize>() {
return Some(size);
}
}
}
None
}
fn count_initializer_elements(&self, node: &Node) -> Option<usize> {
if node.kind() == "initializer_list" {
let mut count = 0;
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"number_literal" | "string_literal" | "char_literal" | "identifier"
| "binary_expression" | "unary_expression" | "call_expression"
| "initializer_list" => {
count += 1;
}
"," | "{" | "}" => {}
_ => {
if child.kind().contains("expression") {
count += 1;
}
}
}
}
return Some(count);
}
None
}
}