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 Int17C;
impl CertRule for Int17C {
fn rule_id(&self) -> &'static str {
"INT17-C"
}
fn description(&self) -> &'static str {
"Define integer constants in an implementation-independent manner"
}
fn severity(&self) -> Severity {
Severity::High
}
fn category(&self) -> RuleCategory {
RuleCategory::Recommendation
}
fn cert_id(&self) -> &'static str {
"INT17-C"
}
fn check(&self, node: &Node, source: &str) -> Vec<RuleViolation> {
let mut violations = Vec::new();
self.traverse(node, source, &mut violations);
violations
}
}
impl Int17C {
fn traverse(&self, node: &Node, source: &str, violations: &mut Vec<RuleViolation>) {
if node.kind() == "number_literal" {
let literal_text = get_node_text(node, source);
if self.is_implementation_dependent_constant(literal_text) {
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
message: format!(
"Hex constant '{}' assumes specific bit-width and is implementation-dependent. \
Use -1 for all bits set, or shift expressions like ~(ULONG_MAX >> 1) for MSB.",
literal_text
),
severity: self.severity(),
line: node.start_position().row + 1,
column: node.start_position().column + 1,
file_path: String::new(),
suggestion: Some(
"Use -1 for all bits set (unsigned), or macros from <limits.h> for portable values"
.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_implementation_dependent_constant(&self, literal: &str) -> bool {
let normalized = literal
.to_lowercase()
.trim_end_matches('u')
.trim_end_matches('l')
.trim_end_matches("ul")
.trim_end_matches("lu")
.trim_end_matches("ll")
.trim_end_matches("ull")
.trim_end_matches("llu")
.to_string();
if !normalized.starts_with("0x") && !normalized.starts_with("0X") {
return false;
}
let hex_part = &normalized[2..];
if hex_part.is_empty() {
return false;
}
if let Ok(val) = u64::from_str_radix(hex_part, 16) {
if val <= 0xFFFF {
return false; }
}
if hex_part.len() >= 4 {
if hex_part.chars().all(|c| c == 'f') {
return true;
}
if hex_part.starts_with('8') && hex_part.chars().skip(1).all(|c| c == '0') {
return true;
}
if hex_part.starts_with('7') && hex_part.chars().skip(1).all(|c| c == 'f') {
return true;
}
}
false
}
}