use super::super::{CertRule, RuleViolation};
use crate::manifest::{RuleCategory, Severity};
use crate::utility::cert_c::ast_utils;
use tree_sitter::Node;
pub struct Exp14C;
impl CertRule for Exp14C {
fn rule_id(&self) -> &'static str {
"EXP14-C"
}
fn description(&self) -> &'static str {
"Beware of integer promotion when performing bitwise operations on integer types smaller than int"
}
fn severity(&self) -> Severity {
Severity::Medium
}
fn category(&self) -> RuleCategory {
RuleCategory::Rule
}
fn cert_id(&self) -> &'static str {
"EXP14-C"
}
fn check(&self, node: &Node, source: &str) -> Vec<RuleViolation> {
let mut violations = Vec::new();
check_node(node, source, &mut violations);
violations
}
}
fn check_node(node: &Node, source: &str, violations: &mut Vec<RuleViolation>) {
match node.kind() {
"assignment_expression" => check_assignment(node, source, violations),
"init_declarator" => check_init_declarator(node, source, violations),
_ => {}
}
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
check_node(&child, source, violations);
}
}
fn check_assignment(node: &Node, source: &str, violations: &mut Vec<RuleViolation>) {
if let Some(left) = node.child_by_field_name("left") {
let _left_text = ast_utils::get_node_text(&left, source);
if let Some(right) = node.child_by_field_name("right") {
check_bitwise_expression(&right, source, violations);
}
}
}
fn check_init_declarator(node: &Node, source: &str, violations: &mut Vec<RuleViolation>) {
if let Some(value) = node.child_by_field_name("value") {
check_bitwise_expression(&value, source, violations);
}
}
fn check_bitwise_expression(node: &Node, source: &str, violations: &mut Vec<RuleViolation>) {
if node.kind() == "cast_expression" {
return;
}
match node.kind() {
"unary_expression" => {
if let Some(operator) = node.child_by_field_name("operator") {
let op_text = ast_utils::get_node_text(&operator, source);
if op_text == "~" {
if !is_wrapped_in_cast(node) {
violations.push(RuleViolation {
rule_id: "EXP14-C".to_string(),
severity: Severity::Medium,
message: "Bitwise operation on type smaller than int may cause unexpected integer promotion. Use explicit cast to control promotion behavior.".to_string(),
file_path: String::new(),
line: node.start_position().row + 1,
column: node.start_position().column + 1,
suggestion: Some(
"Wrap the bitwise operation with an explicit cast: (uint8_t)(~value)".to_string()
),
..Default::default()
});
}
}
}
}
"binary_expression" => {
if let Some(operator) = node.child_by_field_name("operator") {
let op_text = ast_utils::get_node_text(&operator, source);
if matches!(op_text, "<<") {
let left_has_cast = if let Some(left) = node.child_by_field_name("left") {
left.kind() == "cast_expression"
} else {
false
};
if !left_has_cast && !is_wrapped_in_cast(node) {
violations.push(RuleViolation {
rule_id: "EXP14-C".to_string(),
severity: Severity::Medium,
message: format!(
"Bitwise operation '{}' on type smaller than int may cause unexpected integer promotion. Use explicit cast to control promotion behavior.",
op_text
),
file_path: String::new(),
line: node.start_position().row + 1,
column: node.start_position().column + 1,
suggestion: Some(
"Wrap the bitwise operation with an explicit cast: (uint8_t)(value & mask)".to_string()
),
..Default::default()
});
}
}
}
}
_ => {}
}
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
check_bitwise_expression(&child, source, violations);
}
}
fn is_wrapped_in_cast(node: &Node) -> bool {
if let Some(parent) = node.parent() {
if parent.kind() == "cast_expression" {
if let Some(value) = parent.child_by_field_name("value") {
if value.id() == node.id() {
return true;
}
}
}
if parent.kind() == "parenthesized_expression" {
return is_wrapped_in_cast(&parent);
}
}
false
}