use super::super::{CertRule, RuleViolation};
use crate::manifest::{RuleCategory, Severity};
use crate::utility::cert_c::ast_utils::get_node_text;
use std::cell::RefCell;
use std::collections::HashMap;
use tree_sitter::Node;
#[derive(Debug, Clone, Copy, PartialEq)]
enum Linkage {
Internal, External, }
#[allow(dead_code)]
#[derive(Debug, Clone)]
struct IdentifierInfo {
name: String,
linkage: Linkage,
line: usize,
column: usize,
}
#[derive(Debug)]
pub struct Dcl36C {
declarations: RefCell<HashMap<String, Vec<IdentifierInfo>>>,
}
impl Dcl36C {
pub fn new() -> Self {
Dcl36C {
declarations: RefCell::new(HashMap::new()),
}
}
fn get_linkage(&self, node: &Node) -> Option<Linkage> {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "storage_class_specifier" {
return Some(Linkage::Internal);
}
}
Some(Linkage::External)
}
fn extract_identifier(&self, declarator: &Node, source: &str) -> Option<String> {
match declarator.kind() {
"identifier" => Some(get_node_text(declarator, source).to_string()),
"pointer_declarator" | "array_declarator" | "function_declarator" => {
if let Some(inner) = declarator.child_by_field_name("declarator") {
return self.extract_identifier(&inner, source);
}
None
}
"init_declarator" => {
if let Some(decl) = declarator.child_by_field_name("declarator") {
return self.extract_identifier(&decl, source);
}
None
}
_ => {
let mut cursor = declarator.walk();
for child in declarator.children(&mut cursor) {
if child.kind() == "identifier" {
return Some(get_node_text(&child, source).to_string());
}
if let Some(name) = self.extract_identifier(&child, source) {
return Some(name);
}
}
None
}
}
}
fn is_file_scope(&self, node: &Node) -> bool {
let mut current = node.parent();
while let Some(parent) = current {
if parent.kind() == "function_definition" {
return false;
}
if parent.kind() == "compound_statement" {
if let Some(grandparent) = parent.parent() {
if grandparent.kind() == "function_definition" {
return false;
}
}
}
current = parent.parent();
}
true
}
fn process_declaration(&self, node: &Node, source: &str, violations: &mut Vec<RuleViolation>) {
if node.kind() != "declaration" {
return;
}
if !self.is_file_scope(node) {
return;
}
let linkage = match self.get_linkage(node) {
Some(l) => l,
None => return,
};
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "init_declarator" {
if let Some(declarator) = child.child_by_field_name("declarator") {
if let Some(name) = self.extract_identifier(&declarator, source) {
self.record_identifier(name, linkage, node, violations);
}
}
} else if let Some(name) = self.extract_identifier(&child, source) {
self.record_identifier(name, linkage, node, violations);
}
}
}
fn record_identifier(
&self,
name: String,
linkage: Linkage,
node: &Node,
violations: &mut Vec<RuleViolation>,
) {
let mut decls = self.declarations.borrow_mut();
let entry = decls.entry(name.clone()).or_default();
for prev in entry.iter() {
if prev.linkage != linkage {
violations.push(RuleViolation {
rule_id: "DCL36-C".to_string(),
severity: Severity::Medium,
line: node.start_position().row + 1,
column: node.start_position().column + 1,
message: format!(
"Identifier '{}' has conflicting linkage: previously declared as {} at line {}, now declared as {}",
name,
if prev.linkage == Linkage::Internal { "static (internal)" } else { "non-static (external)" },
prev.line,
if linkage == Linkage::Internal { "static (internal)" } else { "non-static (external)" }
),
file_path: String::new(),
suggestion: Some(
"Use consistent storage-class specifiers for all declarations of the same identifier"
.to_string(),
),
requires_manual_review: Some(false),
});
return;
}
}
entry.push(IdentifierInfo {
name,
linkage,
line: node.start_position().row + 1,
column: node.start_position().column + 1,
});
}
fn traverse(&self, node: &Node, source: &str, violations: &mut Vec<RuleViolation>) {
self.process_declaration(node, source, violations);
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
self.traverse(&child, source, violations);
}
}
}
impl CertRule for Dcl36C {
fn rule_id(&self) -> &'static str {
"DCL36-C"
}
fn description(&self) -> &'static str {
"Do not declare an identifier with conflicting linkage classifications"
}
fn category(&self) -> RuleCategory {
RuleCategory::Rule
}
fn severity(&self) -> Severity {
Severity::Medium
}
fn cert_id(&self) -> &'static str {
"DCL36-C"
}
fn check(&self, root: &Node, source: &str) -> Vec<RuleViolation> {
let mut violations = Vec::new();
self.declarations.borrow_mut().clear();
self.traverse(root, source, &mut violations);
violations
}
}