use super::super::{CertRule, RuleViolation};
use crate::manifest::{RuleCategory, Severity};
use crate::utility::cert_c::ast_utils;
use tree_sitter::Node;
pub struct Dcl04C;
impl CertRule for Dcl04C {
fn rule_id(&self) -> &'static str {
"DCL04-C"
}
fn description(&self) -> &'static str {
"Do not declare more than one variable per declaration"
}
fn severity(&self) -> Severity {
Severity::Low
}
fn category(&self) -> RuleCategory {
RuleCategory::Recommendation
}
fn cert_id(&self) -> &'static str {
"DCL04-C"
}
fn check(&self, node: &Node, source: &str) -> Vec<RuleViolation> {
let mut violations = Vec::new();
if node.kind() == "declaration" {
if self.is_for_loop_declaration(node) {
return violations;
}
let declarator_count = self.count_declarators(node);
if declarator_count > 1 {
let start_point = node.start_position();
let declaration_text = ast_utils::get_node_text(node, source);
violations.push(RuleViolation {
rule_id: "DCL04-C".to_string(),
severity: Severity::Low,
message: format!(
"Multiple variables ({}) declared in single declaration: {}",
declarator_count,
declaration_text.lines().next().unwrap_or(&declaration_text)
),
file_path: String::new(),
line: start_point.row + 1,
column: start_point.column + 1,
suggestion: Some(
"Declare each variable on its own line with an explanatory comment"
.to_string(),
),
..Default::default()
});
}
}
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
violations.extend(self.check(&child, source));
}
}
violations
}
}
impl Dcl04C {
fn is_for_loop_declaration(&self, node: &Node) -> bool {
if let Some(parent) = node.parent() {
if parent.kind() == "for_statement" {
return true;
}
}
false
}
fn count_declarators(&self, declaration_node: &Node) -> usize {
let mut count = 0;
for i in 0..declaration_node.child_count() {
if let Some(child) = declaration_node.child(i) {
match child.kind() {
"init_declarator" => {
count += 1;
}
"pointer_declarator"
| "array_declarator"
| "function_declarator"
| "identifier"
if self.is_direct_declarator(&child, declaration_node) => {
count += 1;
}
_ => {}
}
}
}
count
}
fn is_direct_declarator(&self, node: &Node, declaration: &Node) -> bool {
if let Some(parent) = node.parent() {
if parent.id() == declaration.id() {
matches!(
node.kind(),
"pointer_declarator"
| "array_declarator"
| "function_declarator"
| "identifier"
)
} else {
false
}
} else {
false
}
}
}