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 Pre04C {
standard_headers: Vec<&'static str>,
}
impl Pre04C {
pub fn new() -> Self {
Self {
standard_headers: vec![
"assert.h",
"complex.h",
"ctype.h",
"errno.h",
"fenv.h",
"float.h",
"inttypes.h",
"iso646.h",
"limits.h",
"locale.h",
"math.h",
"setjmp.h",
"signal.h",
"stdalign.h",
"stdarg.h",
"stdatomic.h",
"stdbool.h",
"stddef.h",
"stdint.h",
"stdio.h",
"stdlib.h",
"string.h",
"tgmath.h",
"threads.h",
"time.h",
"uchar.h",
"wchar.h",
"wctype.h",
],
}
}
fn extract_header_name(&self, node: &Node, source: &str) -> Option<String> {
let path_node = node.child_by_field_name("path")?;
let path_text = get_node_text(&path_node, source);
let trimmed = path_text.trim();
if trimmed.starts_with('"') && trimmed.ends_with('"') {
let filename = trimmed.trim_matches('"');
return Some(filename.to_string());
}
None
}
fn is_standard_header(&self, header_name: &str) -> bool {
self.standard_headers.contains(&header_name)
}
fn check_include_directive(
&self,
node: &Node,
source: &str,
violations: &mut Vec<RuleViolation>,
) {
if node.kind() != "preproc_include" {
return;
}
let header_name = match self.extract_header_name(node, source) {
Some(name) => name,
None => return,
};
if self.is_standard_header(&header_name) {
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
severity: self.severity(),
message: format!(
"User-defined header file '{}' reuses a standard C library header name. Use a unique name instead.",
header_name
),
file_path: String::new(),
line: node.start_position().row + 1,
column: node.start_position().column + 1,
suggestion: Some(format!(
"Rename the header file to a unique name (e.g., 'my{}')",
header_name
)),
..Default::default()
});
}
}
}
impl CertRule for Pre04C {
fn rule_id(&self) -> &'static str {
"PRE04-C"
}
fn description(&self) -> &'static str {
"Do not reuse a standard header file name"
}
fn severity(&self) -> Severity {
Severity::Low
}
fn category(&self) -> RuleCategory {
RuleCategory::Rule
}
fn cert_id(&self) -> &'static str {
"PRE04-C"
}
fn check(&self, node: &Node, source: &str) -> Vec<RuleViolation> {
let mut violations = Vec::new();
self.check_node(node, source, &mut violations);
violations
}
}
impl Pre04C {
fn check_node(&self, node: &Node, source: &str, violations: &mut Vec<RuleViolation>) {
self.check_include_directive(node, source, violations);
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
self.check_node(&child, source, violations);
}
}
}
}