use super::super::{CertRule, RuleViolation};
use crate::manifest::{RuleCategory, Severity};
use tree_sitter::Node;
#[derive(Debug)]
pub struct Dcl41C;
impl Dcl41C {
#[allow(dead_code)]
pub fn new() -> Self {
Dcl41C
}
fn is_case_label(&self, node: &Node) -> bool {
matches!(node.kind(), "case_statement" | "default")
}
fn is_statement_or_declaration(&self, node: &Node) -> bool {
matches!(
node.kind(),
"declaration"
| "expression_statement"
| "if_statement"
| "while_statement"
| "do_statement"
| "for_statement"
| "return_statement"
| "break_statement"
| "continue_statement"
| "goto_statement"
| "switch_statement"
| "function_definition"
| "labeled_statement"
)
}
fn check_switch_statement(
&self,
node: &Node,
_source: &str,
violations: &mut Vec<RuleViolation>,
) {
if node.kind() != "switch_statement" {
return;
}
if let Some(body) = node.child_by_field_name("body") {
if body.kind() != "compound_statement" {
return;
}
let mut found_first_label = false;
let mut cursor = body.walk();
for child in body.children(&mut cursor) {
if child.kind() == "{" || child.kind() == "}" {
continue;
}
if self.is_case_label(&child) {
found_first_label = true;
continue;
}
if !found_first_label && self.is_statement_or_declaration(&child) {
let kind_desc = match child.kind() {
"declaration" => "Variable declaration",
"expression_statement" => "Expression statement",
_ => "Statement",
};
violations.push(RuleViolation {
rule_id: "DCL41-C".to_string(),
severity: Severity::Medium,
line: child.start_position().row + 1,
column: child.start_position().column + 1,
message: format!(
"{} appears before the first case label in switch statement",
kind_desc
),
file_path: String::new(),
suggestion: Some(
"Move declarations and statements outside the switch statement or after the first case label"
.to_string(),
),
requires_manual_review: Some(false),
});
}
}
}
}
fn traverse(&self, node: &Node, source: &str, violations: &mut Vec<RuleViolation>) {
self.check_switch_statement(node, source, violations);
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
self.traverse(&child, source, violations);
}
}
}
impl CertRule for Dcl41C {
fn rule_id(&self) -> &'static str {
"DCL41-C"
}
fn description(&self) -> &'static str {
"Do not declare variables inside a switch statement before the first case label"
}
fn category(&self) -> RuleCategory {
RuleCategory::Rule
}
fn severity(&self) -> Severity {
Severity::Medium
}
fn cert_id(&self) -> &'static str {
"DCL41-C"
}
fn check(&self, root: &Node, source: &str) -> Vec<RuleViolation> {
let mut violations = Vec::new();
self.traverse(root, source, &mut violations);
violations
}
}