use super::super::{CertRule, RuleViolation};
use crate::manifest::{RuleCategory, Severity};
use tree_sitter::Node;
pub struct Exp37C;
impl CertRule for Exp37C {
fn rule_id(&self) -> &'static str {
"EXP37-C"
}
fn description(&self) -> &'static str {
"Call functions with correct arguments"
}
fn severity(&self) -> Severity {
Severity::Medium
}
fn category(&self) -> RuleCategory {
RuleCategory::Rule
}
fn cert_id(&self) -> &'static str {
"EXP37-C"
}
fn check(&self, node: &Node, source: &str) -> Vec<RuleViolation> {
let mut violations = Vec::new();
self.check_node(node, source, &mut violations);
violations
}
}
impl Exp37C {
fn check_node(&self, node: &Node, source: &str, violations: &mut Vec<RuleViolation>) {
if node.kind() == "call_expression" {
let text = node.utf8_text(source.as_bytes()).unwrap_or("");
if text.starts_with("log2(") && !text.contains("creal(") {
let lines_before = source
.lines()
.take(node.start_position().row + 1)
.collect::<Vec<_>>()
.join("\n");
if lines_before.contains("complex") && lines_before.contains("=") {
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
severity: self.severity(),
line: node.start_position().row + 1,
column: node.start_position().column + 1,
file_path: String::new(),
message: "log2() does not support complex numbers; use log(x)/log(2) or log2(creal(x))".to_string(),
suggestion: Some("Replace log2(complex) with log(complex)/log(2) or log2(creal(complex))".to_string()),
requires_manual_review: None,
});
}
}
if text.starts_with("open(") && text.contains("O_CREAT") {
let comma_count = text.matches(',').count();
if comma_count < 2 {
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
severity: self.severity(),
line: node.start_position().row + 1,
column: node.start_position().column + 1,
file_path: String::new(),
message: "open() with O_CREAT requires mode parameter".to_string(),
suggestion: Some("Add mode_t parameter to open() call".to_string()),
requires_manual_review: None,
});
}
}
}
if node.kind() == "declaration" {
let text = node.utf8_text(source.as_bytes()).unwrap_or("");
let has_init = (0..node.child_count())
.any(|i| node.child(i).is_some_and(|c| c.kind() == "init_declarator"));
if !has_init && text.contains("()") && !text.contains("void") {
if !text.contains("(*") && !text.contains("{") {
if text.trim().ends_with(");") {
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
severity: self.severity(),
line: node.start_position().row + 1,
column: node.start_position().column + 1,
file_path: String::new(),
message: "Old-style function declaration without parameter types"
.to_string(),
suggestion: Some(
"Specify parameter types in function declaration".to_string(),
),
requires_manual_review: None,
});
}
}
}
if text.contains("...") && !text.contains("#include") {
if text.trim().ends_with(";") && !text.contains("{") {
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
severity: self.severity(),
line: node.start_position().row + 1,
column: node.start_position().column + 1,
file_path: String::new(),
message: "Variadic function declaration should use proper header"
.to_string(),
suggestion: Some(
"Include proper header instead of declaring variadic function"
.to_string(),
),
requires_manual_review: None,
});
}
}
if text.contains("(*fp)()") || (text.contains("(*fp)") && text.contains("()")) {
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
severity: self.severity(),
line: node.start_position().row + 1,
column: node.start_position().column + 1,
file_path: String::new(),
message: "Function pointer declared without parameter types".to_string(),
suggestion: Some(
"Declare function pointer with explicit parameter types".to_string(),
),
requires_manual_review: None,
});
}
}
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
self.check_node(&child, source, violations);
}
}
}