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 Int35C;
impl CertRule for Int35C {
fn rule_id(&self) -> &'static str {
"INT35-C"
}
fn description(&self) -> &'static str {
"Use correct integer precisions"
}
fn severity(&self) -> Severity {
Severity::Low
}
fn category(&self) -> RuleCategory {
RuleCategory::Rule
}
fn cert_id(&self) -> &'static str {
"INT35-C"
}
fn check(&self, node: &Node, source: &str) -> Vec<RuleViolation> {
let mut violations = Vec::new();
self.traverse(node, source, &mut violations);
violations
}
}
impl Int35C {
fn traverse(&self, node: &Node, source: &str, violations: &mut Vec<RuleViolation>) {
if node.kind() == "binary_expression" {
if let Some(operator) = node.child_by_field_name("operator") {
let op_text = get_node_text(&operator, source);
if op_text == "*" {
if let (Some(left), Some(right)) = (
node.child_by_field_name("left"),
node.child_by_field_name("right"),
) {
let left_text = get_node_text(&left, source);
let right_text = get_node_text(&right, source);
let has_sizeof_char_bit = (self.is_sizeof_expression(left_text)
&& self.is_char_bit(right_text))
|| (self.is_char_bit(left_text)
&& self.is_sizeof_expression(right_text));
if has_sizeof_char_bit {
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
message: "Using 'sizeof(...) * CHAR_BIT' to determine integer precision \
is incorrect when padding bits exist. Use PRECISION() macro, \
popcount(), or C23 *_WIDTH macros instead.".to_string(),
severity: self.severity(),
line: node.start_position().row + 1,
column: node.start_position().column + 1,
file_path: String::new(),
suggestion: Some(
"Use popcount(TYPE_MAX) for actual precision, or *_WIDTH macros in C23"
.to_string(),
),
requires_manual_review: None,
});
}
}
}
}
}
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
self.traverse(&child, source, violations);
}
}
}
fn is_sizeof_expression(&self, expr: &str) -> bool {
let trimmed = expr.trim();
trimmed.starts_with("sizeof") || trimmed.contains("sizeof(") || trimmed.contains("sizeof ")
}
fn is_char_bit(&self, expr: &str) -> bool {
let trimmed = expr.trim();
trimmed == "CHAR_BIT"
|| trimmed == "(CHAR_BIT)"
|| trimmed.ends_with("CHAR_BIT")
|| trimmed.contains("CHAR_BIT")
}
}