use super::super::{CertRule, RuleViolation};
use crate::manifest::{RuleCategory, Severity};
use crate::utility::cert_c::ast_utils::get_node_text;
use tree_sitter::Node;
pub struct Pre13C;
impl Pre13C {
pub fn new() -> Self {
Self
}
#[allow(dead_code)]
fn is_standard_macro(&self, name: &str) -> bool {
matches!(
name,
"__STDC__"
| "__STDC_VERSION__"
| "__STDC_HOSTED__"
| "__STDC_MB_MIGHT_NEQ_WC__"
| "__STDC_UTF_16__"
| "__STDC_UTF_32__"
| "__STDC_ANALYZABLE__"
| "__STDC_IEC_559__"
| "__STDC_IEC_559_COMPLEX__"
| "__STDC_ISO_10646__"
| "__STDC_LIB_EXT1__"
| "__STDC_NO_ATOMICS__"
| "__STDC_NO_COMPLEX__"
| "__STDC_NO_THREADS__"
| "__STDC_NO_VLA__"
| "__DATE__"
| "__FILE__"
| "__LINE__"
| "__TIME__"
)
}
fn check_preprocessor_conditional(
&self,
node: &Node,
source: &str,
violations: &mut Vec<RuleViolation>,
defined_macros: &[String],
) {
let is_conditional = node.kind() == "preproc_if" || node.kind() == "preproc_elif";
if !is_conditional {
return;
}
let condition = match node.child_by_field_name("condition") {
Some(c) => c,
None => return,
};
let condition_text = get_node_text(&condition, source);
for macro_name in self.get_standard_macros() {
if condition_text.contains(macro_name) {
let defined_pattern = format!("defined({})", macro_name);
let defined_pattern_spaces = format!("defined ({})", macro_name);
if condition_text.contains(&defined_pattern)
|| condition_text.contains(&defined_pattern_spaces)
{
continue;
}
if defined_macros.contains(¯o_name.to_string()) {
continue;
}
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
severity: self.severity(),
message: format!(
"Standard macro '{}' is used in preprocessor conditional without first checking if it is defined. Use 'defined({})' before testing its value.",
macro_name, macro_name
),
file_path: String::new(),
line: node.start_position().row + 1,
column: node.start_position().column + 1,
suggestion: Some(format!(
"Wrap the condition with 'defined({})' check: '#if defined({}) && ({})'",
macro_name, macro_name, condition_text
)),
..Default::default()
});
return;
}
}
}
}
impl CertRule for Pre13C {
fn rule_id(&self) -> &'static str {
"PRE13-C"
}
fn description(&self) -> &'static str {
"Use the Standard predefined macros to test for versions and features"
}
fn severity(&self) -> Severity {
Severity::Low
}
fn category(&self) -> RuleCategory {
RuleCategory::Recommendation
}
fn cert_id(&self) -> &'static str {
"PRE13-C"
}
fn check(&self, node: &Node, source: &str) -> Vec<RuleViolation> {
let mut violations = Vec::new();
let defined_macros: Vec<String> = Vec::new();
self.check_node(node, source, &mut violations, &defined_macros);
violations
}
}
impl Pre13C {
fn check_node(
&self,
node: &Node,
source: &str,
violations: &mut Vec<RuleViolation>,
defined_macros: &[String],
) {
self.check_preprocessor_conditional(node, source, violations, defined_macros);
if node.kind() == "preproc_if" {
if let Some(condition) = node.child_by_field_name("condition") {
let condition_text = get_node_text(&condition, source);
let mut new_defined_macros = defined_macros.to_vec();
for macro_name in self.get_standard_macros() {
let defined_pattern = format!("defined({})", macro_name);
let defined_pattern_spaces = format!("defined ({})", macro_name);
if condition_text.contains(&defined_pattern)
|| condition_text.contains(&defined_pattern_spaces)
{
if !new_defined_macros.contains(¯o_name.to_string()) {
new_defined_macros.push(macro_name.to_string());
}
}
}
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
self.check_node(&child, source, violations, &new_defined_macros);
}
}
return;
}
}
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
self.check_node(&child, source, violations, defined_macros);
}
}
}
fn get_standard_macros(&self) -> &[&str] {
&[
"__STDC__",
"__STDC_VERSION__",
"__STDC_HOSTED__",
"__STDC_MB_MIGHT_NEQ_WC__",
"__STDC_UTF_16__",
"__STDC_UTF_32__",
"__STDC_ANALYZABLE__",
"__STDC_IEC_559__",
"__STDC_IEC_559_COMPLEX__",
"__STDC_ISO_10646__",
"__STDC_LIB_EXT1__",
"__STDC_NO_ATOMICS__",
"__STDC_NO_COMPLEX__",
"__STDC_NO_THREADS__",
"__STDC_NO_VLA__",
]
}
}