use super::super::{CertRule, RuleViolation};
use crate::manifest::{RuleCategory, Severity};
use crate::utility::cert_c::ast_utils::get_node_text;
use std::collections::HashMap;
use tree_sitter::Node;
pub struct Con03C;
impl CertRule for Con03C {
fn rule_id(&self) -> &'static str {
"CON03-C"
}
fn description(&self) -> &'static str {
"Ensure visibility when accessing shared variables"
}
fn severity(&self) -> Severity {
Severity::Medium
}
fn category(&self) -> RuleCategory {
RuleCategory::Rule
}
fn cert_id(&self) -> &'static str {
"CON03-C"
}
fn check(&self, node: &Node, source: &str) -> Vec<RuleViolation> {
let mut violations = Vec::new();
let mut shared_vars: HashMap<String, (usize, usize, bool, bool)> = HashMap::new();
self.collect_shared_variables(node, source, &mut shared_vars);
for (var_name, (line, column, is_volatile, is_atomic)) in shared_vars {
if !is_volatile && !is_atomic {
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
severity: Severity::Medium,
message: format!(
"Shared variable '{}' lacks proper synchronization (not volatile or atomic). This may cause visibility issues across threads.",
var_name
),
file_path: String::new(),
line,
column,
suggestion: Some(
"Consider declaring the variable as 'volatile', using atomic types (atomic_int, etc.), or protecting access with mutexes".to_string()
),
..Default::default()
});
}
}
violations
}
}
impl Con03C {
fn collect_shared_variables(
&self,
node: &Node,
source: &str,
shared_vars: &mut HashMap<String, (usize, usize, bool, bool)>,
) {
if node.kind() == "declaration" {
let is_static = self.has_storage_class(node, source, "static");
let is_global = self.is_global_scope(node);
if is_static || is_global {
if self.has_type_qualifier(node, source, "const") {
return;
}
let decl_text = get_node_text(node, source);
if self.is_synchronization_type(&decl_text) {
return;
}
let is_volatile = self.has_type_qualifier(node, source, "volatile");
let is_atomic = self.has_atomic_type(node, source);
let line = node.start_position().row + 1;
let column = node.start_position().column + 1;
if let Some(declarator_list) = self.find_child_by_kind(node, "init_declarator") {
if let Some(declarator) = declarator_list.child_by_field_name("declarator") {
let var_name = self.extract_variable_name(&declarator, source);
if !var_name.is_empty() {
shared_vars.insert(var_name, (line, column, is_volatile, is_atomic));
}
}
} else {
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
if child.kind() == "init_declarator" {
if let Some(declarator) = child.child_by_field_name("declarator") {
let var_name = self.extract_variable_name(&declarator, source);
if !var_name.is_empty() {
shared_vars.insert(
var_name,
(line, column, is_volatile, is_atomic),
);
}
}
}
}
}
}
}
}
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
self.collect_shared_variables(&child, source, shared_vars);
}
}
}
fn has_storage_class(&self, node: &Node, source: &str, class: &str) -> bool {
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
if child.kind() == "storage_class_specifier" {
let text = get_node_text(&child, source);
if text == class {
return true;
}
}
}
}
false
}
fn has_type_qualifier(&self, node: &Node, source: &str, qualifier: &str) -> bool {
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
if child.kind() == "type_qualifier" {
let text = get_node_text(&child, source);
if text == qualifier {
return true;
}
}
}
}
false
}
fn has_atomic_type(&self, node: &Node, source: &str) -> bool {
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
let text = get_node_text(&child, source);
if text.contains("atomic_") || text.contains("_Atomic") {
return true;
}
if child.kind() == "type_specifier" && self.has_atomic_type(&child, source) {
return true;
}
}
}
false
}
fn is_synchronization_type(&self, decl_text: &str) -> bool {
let sync_types = [
"pthread_mutex_t",
"pthread_rwlock_t",
"pthread_cond_t",
"pthread_spinlock_t",
"pthread_barrier_t",
"mtx_t",
"cnd_t",
"sem_t",
];
sync_types.iter().any(|t| decl_text.contains(t))
}
fn is_global_scope(&self, node: &Node) -> bool {
if let Some(parent) = node.parent() {
parent.kind() == "translation_unit"
} else {
false
}
}
fn find_child_by_kind<'a>(&self, node: &'a Node, kind: &str) -> Option<Node<'a>> {
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
if child.kind() == kind {
return Some(child);
}
}
}
None
}
fn extract_variable_name(&self, declarator: &Node, source: &str) -> String {
match declarator.kind() {
"identifier" => get_node_text(declarator, source).to_string(),
"pointer_declarator" | "array_declarator" => {
if let Some(inner) = declarator.child_by_field_name("declarator") {
return self.extract_variable_name(&inner, source);
}
String::new()
}
_ => {
for i in 0..declarator.child_count() {
if let Some(child) = declarator.child(i) {
if child.kind() == "identifier" {
return get_node_text(&child, source).to_string();
}
let name = self.extract_variable_name(&child, source);
if !name.is_empty() {
return name;
}
}
}
String::new()
}
}
}
}