use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum X86CheckerSeverity {
Info,
Note,
Warning,
Error,
Fatal,
}
impl fmt::Display for X86CheckerSeverity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Info => write!(f, "info"),
Self::Note => write!(f, "note"),
Self::Warning => write!(f, "warning"),
Self::Error => write!(f, "error"),
Self::Fatal => write!(f, "fatal"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86CheckerCategory {
MisraC,
CertC,
CertCpp,
Autosar,
CppCoreGuidelines,
Performance,
Readability,
Modernize,
Bugprone,
Portability,
Security,
Style,
Documentation,
Concurrency,
Custom,
}
impl fmt::Display for X86CheckerCategory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MisraC => write!(f, "misra-c"),
Self::CertC => write!(f, "cert-c"),
Self::CertCpp => write!(f, "cert-cpp"),
Self::Autosar => write!(f, "autosar"),
Self::CppCoreGuidelines => write!(f, "cpp-core-guidelines"),
Self::Performance => write!(f, "performance"),
Self::Readability => write!(f, "readability"),
Self::Modernize => write!(f, "modernize"),
Self::Bugprone => write!(f, "bugprone"),
Self::Portability => write!(f, "portability"),
Self::Security => write!(f, "security"),
Self::Style => write!(f, "style"),
Self::Documentation => write!(f, "documentation"),
Self::Concurrency => write!(f, "concurrency"),
Self::Custom => write!(f, "custom"),
}
}
}
#[derive(Debug, Clone)]
pub struct X86CheckerFinding {
pub checker_name: String,
pub rule_id: String,
pub category: X86CheckerCategory,
pub severity: X86CheckerSeverity,
pub message: String,
pub file: Option<String>,
pub line: usize,
pub column: usize,
pub end_line: usize,
pub end_column: usize,
pub fixit: Option<String>,
pub suppressed: bool,
}
impl fmt::Display for X86CheckerFinding {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{}/{}] ", self.checker_name, self.rule_id)?;
if let Some(ref file) = self.file {
write!(f, "{}:{}:{}: ", file, self.line, self.column)?;
}
write!(f, "{}: {}", self.severity, self.message)?;
if let Some(ref fixit) = self.fixit {
write!(f, " (fix: {})", fixit)?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct X86CheckerResult {
pub checker_name: String,
pub category: X86CheckerCategory,
pub findings: Vec<X86CheckerFinding>,
pub files_checked: usize,
pub lines_checked: usize,
pub duration_ms: u64,
pub enabled: bool,
}
impl X86CheckerResult {
pub fn new(checker_name: &str, category: X86CheckerCategory) -> Self {
Self {
checker_name: checker_name.to_string(),
category,
findings: Vec::new(),
files_checked: 0,
lines_checked: 0,
duration_ms: 0,
enabled: true,
}
}
}
#[derive(Debug, Clone)]
pub struct X86CheckerConfig {
pub name: String,
pub enabled: bool,
pub severity_override: Option<X86CheckerSeverity>,
pub max_findings: usize,
pub custom_options: BTreeMap<String, String>,
pub suppressions: Vec<String>,
}
impl X86CheckerConfig {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
enabled: true,
severity_override: None,
max_findings: usize::MAX,
custom_options: BTreeMap::new(),
suppressions: Vec::new(),
}
}
pub fn disabled(name: &str) -> Self {
Self {
name: name.to_string(),
enabled: false,
severity_override: None,
max_findings: usize::MAX,
custom_options: BTreeMap::new(),
suppressions: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct X86CheckerDependency {
pub checker: String,
pub requires: Vec<String>,
pub suppresses: Vec<String>,
pub conflicts_with: Vec<String>,
pub runs_before: Vec<String>,
pub runs_after: Vec<String>,
}
impl X86CheckerDependency {
pub fn new(checker: &str) -> Self {
Self {
checker: checker.to_string(),
requires: Vec::new(),
suppresses: Vec::new(),
conflicts_with: Vec::new(),
runs_before: Vec::new(),
runs_after: Vec::new(),
}
}
pub fn with_requires(mut self, required: &str) -> Self {
self.requires.push(required.to_string());
self
}
pub fn with_suppresses(mut self, suppressed: &str) -> Self {
self.suppresses.push(suppressed.to_string());
self
}
pub fn with_conflict(mut self, conflicting: &str) -> Self {
self.conflicts_with.push(conflicting.to_string());
self
}
pub fn with_runs_before(mut self, before: &str) -> Self {
self.runs_before.push(before.to_string());
self
}
pub fn with_runs_after(mut self, after: &str) -> Self {
self.runs_after.push(after.to_string());
self
}
}
#[derive(Debug, Clone)]
pub struct X86CheckerDependencyManager {
pub dependencies: BTreeMap<String, X86CheckerDependency>,
pub resolved_order: Vec<String>,
}
impl X86CheckerDependencyManager {
pub fn new() -> Self {
Self {
dependencies: BTreeMap::new(),
resolved_order: Vec::new(),
}
}
pub fn register(&mut self, dep: X86CheckerDependency) {
self.dependencies.insert(dep.checker.clone(), dep);
}
pub fn resolve_order(&mut self, enabled: &HashSet<String>) -> Vec<String> {
let mut order = Vec::new();
let mut visited = HashSet::new();
let mut in_stack = HashSet::new();
for name in enabled {
if !visited.contains(name) {
self.topological_visit(name, enabled, &mut visited, &mut in_stack, &mut order);
}
}
self.apply_ordering_constraints(&mut order);
self.resolved_order = order.clone();
order
}
fn topological_visit(
&self,
name: &str,
enabled: &HashSet<String>,
visited: &mut HashSet<String>,
in_stack: &mut HashSet<String>,
order: &mut Vec<String>,
) {
if in_stack.contains(name) {
return;
}
if visited.contains(name) {
return;
}
in_stack.insert(name.to_string());
if let Some(dep) = self.dependencies.get(name) {
for req in &dep.requires {
if enabled.contains(req) {
self.topological_visit(req, enabled, visited, in_stack, order);
}
}
}
in_stack.remove(name);
visited.insert(name.to_string());
order.push(name.to_string());
}
fn apply_ordering_constraints(&self, order: &mut Vec<String>) {
let mut changed = true;
while changed {
changed = false;
for dep in self.dependencies.values() {
if let Some(pos) = order.iter().position(|n| n == &dep.checker) {
for before in &dep.runs_before {
if let Some(target_pos) = order.iter().position(|n| n == before) {
if pos > target_pos {
order.remove(pos);
order.insert(target_pos, dep.checker.clone());
changed = true;
break;
}
}
}
for after in &dep.runs_after {
if let Some(target_pos) = order.iter().position(|n| n == after) {
if pos < target_pos {
order.remove(pos);
let new_pos = order.len().min(target_pos);
order.insert(new_pos, dep.checker.clone());
changed = true;
break;
}
}
}
}
}
}
}
pub fn conflicts(&self, name: &str) -> Vec<String> {
self.dependencies
.get(name)
.map(|d| d.conflicts_with.clone())
.unwrap_or_default()
}
pub fn suppresses(&self, name: &str) -> Vec<String> {
self.dependencies
.get(name)
.map(|d| d.suppresses.clone())
.unwrap_or_default()
}
}
impl Default for X86CheckerDependencyManager {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86CheckerSuppression {
pub suppressed_rules: BTreeSet<String>,
pub suppressed_checkers: BTreeSet<String>,
pub file_suppressions: BTreeMap<String, BTreeSet<String>>,
pub line_suppressions: BTreeMap<(String, usize), BTreeSet<String>>,
}
impl X86CheckerSuppression {
pub fn new() -> Self {
Self {
suppressed_rules: BTreeSet::new(),
suppressed_checkers: BTreeSet::new(),
file_suppressions: BTreeMap::new(),
line_suppressions: BTreeMap::new(),
}
}
pub fn suppress_checker(&mut self, checker: &str) {
self.suppressed_checkers.insert(checker.to_string());
}
pub fn suppress_rule(&mut self, rule: &str) {
self.suppressed_rules.insert(rule.to_string());
}
pub fn suppress_file(&mut self, file: &str, checker: &str) {
self.file_suppressions
.entry(file.to_string())
.or_default()
.insert(checker.to_string());
}
pub fn suppress_line(&mut self, file: &str, line: usize, checker: &str) {
self.line_suppressions
.entry((file.to_string(), line))
.or_default()
.insert(checker.to_string());
}
pub fn is_suppressed(&self, finding: &X86CheckerFinding) -> bool {
if self.suppressed_checkers.contains(&finding.checker_name) {
return true;
}
if self.suppressed_rules.contains(&finding.rule_id) {
return true;
}
if let Some(ref file) = finding.file {
if let Some(suppressed) = self.file_suppressions.get(file) {
if suppressed.contains(&finding.checker_name) {
return true;
}
}
if let Some(suppressed) = self.line_suppressions.get(&(file.clone(), finding.line)) {
if suppressed.contains(&finding.checker_name) {
return true;
}
}
}
false
}
pub fn parse_nolint_comment(&mut self, file: &str, line: usize, comment: &str) {
if let Some(nolint) = comment.find("NOLINT") {
let rest = &comment[nolint..];
if let Some(open) = rest.find('(') {
if let Some(close) = rest.find(')') {
let checkers: Vec<&str> = rest[open + 1..close]
.split(',')
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.collect();
for checker in checkers {
self.suppress_line(file, line, checker);
}
return;
}
}
self.suppress_line(file, line, "*");
}
if let Some(nolint) = comment.find("NOLINTNEXTLINE") {
let rest = &comment[nolint..];
if let Some(open) = rest.find('(') {
if let Some(close) = rest.find(')') {
let checkers: Vec<&str> = rest[open + 1..close]
.split(',')
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.collect();
for checker in checkers {
self.suppress_line(file, line + 1, checker);
}
return;
}
}
self.suppress_line(file, line + 1, "*");
}
}
pub fn clear(&mut self) {
self.suppressed_rules.clear();
self.suppressed_checkers.clear();
self.file_suppressions.clear();
self.line_suppressions.clear();
}
}
impl Default for X86CheckerSuppression {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86MisraCChecker {
pub enabled_rules: BTreeSet<String>,
pub required_rules: BTreeSet<String>,
pub advisory_rules: BTreeSet<String>,
}
impl X86MisraCChecker {
pub fn new() -> Self {
let mut checker = Self {
enabled_rules: BTreeSet::new(),
required_rules: BTreeSet::new(),
advisory_rules: BTreeSet::new(),
};
for r in &["R1.1", "R2.1", "R8.1", "R10.1", "R13.1", "R17.1", "R21.1"] {
checker.required_rules.insert(r.to_string());
}
for r in &["A1.1", "A2.1", "A5.1", "A7.1", "A16.1", "A19.1"] {
checker.advisory_rules.insert(r.to_string());
}
checker.enabled_rules = checker.required_rules.clone();
checker
}
pub fn enable_rule(&mut self, rule: &str) {
self.enabled_rules.insert(rule.to_string());
}
pub fn disable_rule(&mut self, rule: &str) {
self.enabled_rules.remove(rule);
}
pub fn check_source(&self, source: &str) -> Vec<X86CheckerFinding> {
let mut findings = Vec::new();
if self.enabled_rules.contains("R8.1") {
if source.contains("int main()") && !source.contains("int main(void)") {
findings.push(self.make_finding(
"misra-c",
"R8.1",
X86CheckerSeverity::Error,
"Function 'main' should have an explicit prototype with void",
1,
1,
));
}
}
if self.enabled_rules.contains("R10.1") {
}
if self.enabled_rules.contains("R13.1") {
}
if self.enabled_rules.contains("R17.1") {
}
findings
}
fn make_finding(
&self,
checker: &str,
rule: &str,
severity: X86CheckerSeverity,
msg: &str,
line: usize,
col: usize,
) -> X86CheckerFinding {
X86CheckerFinding {
checker_name: checker.to_string(),
rule_id: rule.to_string(),
category: X86CheckerCategory::MisraC,
severity,
message: msg.to_string(),
file: None,
line,
column: col,
end_line: line,
end_column: col + 1,
fixit: None,
suppressed: false,
}
}
}
impl Default for X86MisraCChecker {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86CertCChecker {
pub enabled_rules: BTreeSet<String>,
}
impl X86CertCChecker {
pub fn new() -> Self {
let mut checker = Self {
enabled_rules: BTreeSet::new(),
};
for r in &[
"EXP33-C", "INT32-C", "STR31-C", "MEM30-C", "ARR30-C", "FIO30-C", "ENV32-C", "SIG30-C",
"ERR30-C", "MSC30-C",
] {
checker.enabled_rules.insert(r.to_string());
}
checker
}
pub fn check_source(&self, _source: &str) -> Vec<X86CheckerFinding> {
let mut findings = Vec::new();
findings
}
pub fn enable_rule(&mut self, rule: &str) {
self.enabled_rules.insert(rule.to_string());
}
}
impl Default for X86CertCChecker {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86CertCppChecker {
pub enabled_rules: BTreeSet<String>,
}
impl X86CertCppChecker {
pub fn new() -> Self {
let mut checker = Self {
enabled_rules: BTreeSet::new(),
};
for r in &[
"CTR50-CPP",
"OOP50-CPP",
"EXP50-CPP",
"ERR50-CPP",
"MEM50-CPP",
"STR50-CPP",
"CON50-CPP",
"MSC50-CPP",
] {
checker.enabled_rules.insert(r.to_string());
}
checker
}
pub fn check_source(&self, _source: &str) -> Vec<X86CheckerFinding> {
Vec::new()
}
}
impl Default for X86CertCppChecker {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86AutosarChecker {
pub enabled_rules: BTreeSet<String>,
}
impl X86AutosarChecker {
pub fn new() -> Self {
let mut checker = Self {
enabled_rules: BTreeSet::new(),
};
for r in &[
"A0-1-1", "A3-1-1", "A5-1-1", "A8-4-1", "A10-3-1", "A12-1-1", "A15-1-1", "A18-1-1",
] {
checker.enabled_rules.insert(r.to_string());
}
checker
}
pub fn check_source(&self, _source: &str) -> Vec<X86CheckerFinding> {
Vec::new()
}
}
impl Default for X86AutosarChecker {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86CoreGuidelinesChecker {
pub enabled_rules: BTreeSet<String>,
}
impl X86CoreGuidelinesChecker {
pub fn new() -> Self {
let mut checker = Self {
enabled_rules: BTreeSet::new(),
};
for r in &[
"C.128", "C.35", "ES.23", "I.3", "F.6", "P.9", "CP.1", "T.1", "E.1", "SL.1",
] {
checker.enabled_rules.insert(r.to_string());
}
checker
}
pub fn check_source(&self, _source: &str) -> Vec<X86CheckerFinding> {
Vec::new()
}
}
impl Default for X86CoreGuidelinesChecker {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86PerformanceChecker {
pub detect_unnecessary_copies: bool,
pub detect_pass_by_value: bool,
pub prefer_emplace: bool,
pub detect_inefficient_algorithms: bool,
}
impl X86PerformanceChecker {
pub fn new() -> Self {
Self {
detect_unnecessary_copies: true,
detect_pass_by_value: true,
prefer_emplace: true,
detect_inefficient_algorithms: true,
}
}
pub fn check_source(&self, _source: &str) -> Vec<X86CheckerFinding> {
let mut findings = Vec::new();
findings
}
}
impl Default for X86PerformanceChecker {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86ReadabilityChecker {
pub check_naming: bool,
pub check_braces: bool,
pub check_magic_numbers: bool,
pub check_simplify_bool_expr: bool,
pub check_redundant_control_flow: bool,
}
impl X86ReadabilityChecker {
pub fn new() -> Self {
Self {
check_naming: true,
check_braces: true,
check_magic_numbers: true,
check_simplify_bool_expr: true,
check_redundant_control_flow: true,
}
}
pub fn check_source(&self, _source: &str) -> Vec<X86CheckerFinding> {
Vec::new()
}
}
impl Default for X86ReadabilityChecker {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86ModernizeChecker {
pub suggest_nullptr: bool,
pub suggest_override: bool,
pub suggest_auto: bool,
pub suggest_range_for: bool,
pub suggest_emplace: bool,
pub suggest_make_unique: bool,
pub suggest_make_shared: bool,
}
impl X86ModernizeChecker {
pub fn new() -> Self {
Self {
suggest_nullptr: true,
suggest_override: true,
suggest_auto: true,
suggest_range_for: true,
suggest_emplace: true,
suggest_make_unique: true,
suggest_make_shared: true,
}
}
pub fn check_source(&self, _source: &str) -> Vec<X86CheckerFinding> {
Vec::new()
}
}
impl Default for X86ModernizeChecker {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86BugproneChecker {
pub check_suspicious_memset: bool,
pub check_incorrect_roundings: bool,
pub check_misplaced_widening_cast: bool,
pub check_bool_pointer_implicit_conversion: bool,
pub check_assert_side_effect: bool,
pub check_swap_with_self: bool,
}
impl X86BugproneChecker {
pub fn new() -> Self {
Self {
check_suspicious_memset: true,
check_incorrect_roundings: true,
check_misplaced_widening_cast: true,
check_bool_pointer_implicit_conversion: true,
check_assert_side_effect: true,
check_swap_with_self: true,
}
}
pub fn check_source(&self, _source: &str) -> Vec<X86CheckerFinding> {
Vec::new()
}
}
impl Default for X86BugproneChecker {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86PortabilityChecker {
pub check_type_sizes: bool,
pub check_alignment: bool,
pub check_endianness: bool,
pub check_restrict_keyword: bool,
}
impl X86PortabilityChecker {
pub fn new() -> Self {
Self {
check_type_sizes: true,
check_alignment: true,
check_endianness: true,
check_restrict_keyword: true,
}
}
pub fn check_source(&self, _source: &str) -> Vec<X86CheckerFinding> {
Vec::new()
}
}
impl Default for X86PortabilityChecker {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86SecurityChecker {
pub check_buffer_overflow: bool,
pub check_format_string: bool,
pub check_integer_overflow: bool,
pub check_use_after_free: bool,
pub check_tainted_input: bool,
}
impl X86SecurityChecker {
pub fn new() -> Self {
Self {
check_buffer_overflow: true,
check_format_string: true,
check_integer_overflow: true,
check_use_after_free: true,
check_tainted_input: true,
}
}
pub fn check_source(&self, _source: &str) -> Vec<X86CheckerFinding> {
Vec::new()
}
}
impl Default for X86SecurityChecker {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86ConcurrencyChecker {
pub check_mutex_double_lock: bool,
pub check_unlock_without_lock: bool,
pub check_shared_data_race: bool,
pub check_atomic_ordering: bool,
}
impl X86ConcurrencyChecker {
pub fn new() -> Self {
Self {
check_mutex_double_lock: true,
check_unlock_without_lock: true,
check_shared_data_race: true,
check_atomic_ordering: true,
}
}
pub fn check_source(&self, _source: &str) -> Vec<X86CheckerFinding> {
Vec::new()
}
}
impl Default for X86ConcurrencyChecker {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86DocumentationChecker {
pub require_public_api_docs: bool,
pub require_param_docs: bool,
pub check_deprecated_docs: bool,
}
impl X86DocumentationChecker {
pub fn new() -> Self {
Self {
require_public_api_docs: true,
require_param_docs: true,
check_deprecated_docs: true,
}
}
pub fn check_source(&self, _source: &str) -> Vec<X86CheckerFinding> {
Vec::new()
}
}
impl Default for X86DocumentationChecker {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86CheckerToggle {
pub enabled: HashSet<String>,
pub disabled: HashSet<String>,
pub category_filters: HashSet<X86CheckerCategory>,
}
impl X86CheckerToggle {
pub fn new() -> Self {
Self {
enabled: HashSet::new(),
disabled: HashSet::new(),
category_filters: HashSet::new(),
}
}
pub fn enable(&mut self, checker: &str) {
self.enabled.insert(checker.to_string());
self.disabled.remove(checker);
}
pub fn disable(&mut self, checker: &str) {
self.disabled.insert(checker.to_string());
self.enabled.remove(checker);
}
pub fn is_enabled(&self, checker: &str) -> bool {
if self.disabled.contains(checker) {
return false;
}
if self.enabled.is_empty() {
return true; }
self.enabled.contains(checker)
}
pub fn enable_category(&mut self, cat: X86CheckerCategory) {
self.category_filters.insert(cat);
}
pub fn disable_category(&mut self, cat: X86CheckerCategory) {
self.category_filters.remove(&cat);
}
pub fn is_category_enabled(&self, cat: X86CheckerCategory) -> bool {
if self.category_filters.is_empty() {
return true;
}
self.category_filters.contains(&cat)
}
}
impl Default for X86CheckerToggle {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86DeepCheckers {
pub configs: BTreeMap<String, X86CheckerConfig>,
pub toggle: X86CheckerToggle,
pub dependencies: X86CheckerDependencyManager,
pub suppressions: X86CheckerSuppression,
pub results: Vec<X86CheckerResult>,
pub total_findings: usize,
pub total_errors: usize,
pub total_warnings: usize,
}
impl X86DeepCheckers {
pub fn new() -> Self {
let mut engine = Self {
configs: BTreeMap::new(),
toggle: X86CheckerToggle::new(),
dependencies: X86CheckerDependencyManager::new(),
suppressions: X86CheckerSuppression::new(),
results: Vec::new(),
total_findings: 0,
total_errors: 0,
total_warnings: 0,
};
let all_checkers = vec![
("misra-c", X86CheckerCategory::MisraC),
("cert-c", X86CheckerCategory::CertC),
("cert-cpp", X86CheckerCategory::CertCpp),
("autosar", X86CheckerCategory::Autosar),
("cppcoreguidelines", X86CheckerCategory::CppCoreGuidelines),
("performance", X86CheckerCategory::Performance),
("readability", X86CheckerCategory::Readability),
("modernize", X86CheckerCategory::Modernize),
("bugprone", X86CheckerCategory::Bugprone),
("portability", X86CheckerCategory::Portability),
("security", X86CheckerCategory::Security),
("concurrency", X86CheckerCategory::Concurrency),
("documentation", X86CheckerCategory::Documentation),
];
for (name, _cat) in &all_checkers {
engine
.configs
.insert(name.to_string(), X86CheckerConfig::new(name));
engine.toggle.enable(name);
}
engine.setup_default_dependencies();
engine
}
fn setup_default_dependencies(&mut self) {
self.dependencies
.register(X86CheckerDependency::new("cppcoreguidelines").with_runs_after("modernize"));
self.dependencies
.register(X86CheckerDependency::new("security").with_runs_after("bugprone"));
self.dependencies.register(
X86CheckerDependency::new("performance").with_suppresses("readability.magic-numbers"),
);
self.dependencies
.register(X86CheckerDependency::new("concurrency").with_runs_after("security"));
}
pub fn configure(&mut self, name: &str, config: X86CheckerConfig) {
self.configs.insert(name.to_string(), config);
}
pub fn enable(&mut self, checker: &str) {
self.toggle.enable(checker);
if let Some(config) = self.configs.get_mut(checker) {
config.enabled = true;
}
}
pub fn disable(&mut self, checker: &str) {
self.toggle.disable(checker);
if let Some(config) = self.configs.get_mut(checker) {
config.enabled = false;
}
}
pub fn run(&mut self, source: &str) -> Vec<X86CheckerResult> {
self.results.clear();
self.total_findings = 0;
self.total_errors = 0;
self.total_warnings = 0;
let enabled_set: HashSet<String> = self
.configs
.iter()
.filter(|(_, c)| c.enabled)
.map(|(n, _)| n.clone())
.collect();
let order = self.dependencies.resolve_order(&enabled_set);
for checker_name in &order {
if !self.toggle.is_enabled(checker_name) {
continue;
}
if let Some(config) = self.configs.get(checker_name) {
if !config.enabled {
continue;
}
let mut result =
X86CheckerResult::new(checker_name, self.checker_category(checker_name));
result.enabled = true;
let mut findings = self.run_checker(checker_name, source);
for finding in &mut findings {
if self.suppressions.is_suppressed(finding) {
finding.suppressed = true;
}
}
if let Some(severity) = config.severity_override {
for finding in &mut findings {
finding.severity = severity;
}
}
let active_findings: Vec<_> =
findings.into_iter().filter(|f| !f.suppressed).collect();
result.findings = active_findings;
result.files_checked = 1;
result.lines_checked = source.lines().count();
self.total_findings += result.findings.len();
self.total_errors += result
.findings
.iter()
.filter(|f| {
matches!(
f.severity,
X86CheckerSeverity::Error | X86CheckerSeverity::Fatal
)
})
.count();
self.total_warnings += result
.findings
.iter()
.filter(|f| matches!(f.severity, X86CheckerSeverity::Warning))
.count();
self.results.push(result);
}
}
self.results.clone()
}
fn run_checker(&self, checker: &str, source: &str) -> Vec<X86CheckerFinding> {
match checker {
"misra-c" => {
let checker = X86MisraCChecker::default();
checker.check_source(source)
}
"performance" => {
let checker = X86PerformanceChecker::default();
checker.check_source(source)
}
"readability" => {
let checker = X86ReadabilityChecker::default();
checker.check_source(source)
}
"modernize" => {
let checker = X86ModernizeChecker::default();
checker.check_source(source)
}
"bugprone" => {
let checker = X86BugproneChecker::default();
checker.check_source(source)
}
"portability" => {
let checker = X86PortabilityChecker::default();
checker.check_source(source)
}
"security" => {
let checker = X86SecurityChecker::default();
checker.check_source(source)
}
"concurrency" => {
let checker = X86ConcurrencyChecker::default();
checker.check_source(source)
}
"documentation" => {
let checker = X86DocumentationChecker::default();
checker.check_source(source)
}
_ => {
let categories: BTreeMap<&str, X86CheckerCategory> = [
("cert-c", X86CheckerCategory::CertC),
("cert-cpp", X86CheckerCategory::CertCpp),
("autosar", X86CheckerCategory::Autosar),
("cppcoreguidelines", X86CheckerCategory::CppCoreGuidelines),
]
.iter()
.cloned()
.collect();
Vec::new()
}
}
}
fn checker_category(&self, name: &str) -> X86CheckerCategory {
match name {
"misra-c" => X86CheckerCategory::MisraC,
"cert-c" => X86CheckerCategory::CertC,
"cert-cpp" => X86CheckerCategory::CertCpp,
"autosar" => X86CheckerCategory::Autosar,
"cppcoreguidelines" => X86CheckerCategory::CppCoreGuidelines,
"performance" => X86CheckerCategory::Performance,
"readability" => X86CheckerCategory::Readability,
"modernize" => X86CheckerCategory::Modernize,
"bugprone" => X86CheckerCategory::Bugprone,
"portability" => X86CheckerCategory::Portability,
"security" => X86CheckerCategory::Security,
"concurrency" => X86CheckerCategory::Concurrency,
"documentation" => X86CheckerCategory::Documentation,
_ => X86CheckerCategory::Custom,
}
}
pub fn checker_names(&self) -> Vec<String> {
self.configs.keys().cloned().collect()
}
pub fn checker_count(&self) -> usize {
self.configs.len()
}
pub fn enabled_checkers(&self) -> Vec<String> {
self.configs
.iter()
.filter(|(_, c)| c.enabled)
.map(|(n, _)| n.clone())
.collect()
}
pub fn report(&self) -> String {
let mut out = String::new();
out.push_str("=== X86DeepCheckers Report ===\n");
out.push_str(&format!(
"Checkers: {} total, {} enabled\n",
self.checker_count(),
self.enabled_checkers().len()
));
out.push_str(&format!(
"Findings: {} total, {} errors, {} warnings\n",
self.total_findings, self.total_errors, self.total_warnings
));
out.push_str("\nResults by checker:\n");
for result in &self.results {
out.push_str(&format!(
" {} ({}): {} findings\n",
result.checker_name,
result.category,
result.findings.len()
));
}
out
}
pub fn suppress(&mut self, checker: &str) {
self.suppressions.suppress_checker(checker);
}
pub fn clear_suppressions(&mut self) {
self.suppressions.clear();
}
pub fn parse_nolint_from_source(&mut self, file: &str, source: &str) {
for (line_num, line) in source.lines().enumerate() {
self.suppressions
.parse_nolint_comment(file, line_num + 1, line);
}
}
}
impl Default for X86DeepCheckers {
fn default() -> Self {
Self::new()
}
}
pub fn make_x86_deep_checkers() -> X86DeepCheckers {
X86DeepCheckers::new()
}
pub fn run_x86_deep_checkers(source: &str) -> Vec<X86CheckerResult> {
let mut checkers = X86DeepCheckers::new();
checkers.run(source)
}
pub fn run_x86_checker(checker_name: &str, source: &str) -> Vec<X86CheckerFinding> {
let mut checkers = X86DeepCheckers::new();
for name in checkers.checker_names() {
if name != checker_name {
checkers.disable(&name);
}
}
let results = checkers.run(source);
results.into_iter().flat_map(|r| r.findings).collect()
}
pub fn x86_checker_names() -> Vec<String> {
X86DeepCheckers::new().checker_names()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_checker_severity_display() {
assert_eq!(X86CheckerSeverity::Info.to_string(), "info");
assert_eq!(X86CheckerSeverity::Warning.to_string(), "warning");
assert_eq!(X86CheckerSeverity::Error.to_string(), "error");
assert_eq!(X86CheckerSeverity::Fatal.to_string(), "fatal");
}
#[test]
fn test_checker_severity_ordering() {
assert!(X86CheckerSeverity::Info < X86CheckerSeverity::Warning);
assert!(X86CheckerSeverity::Warning < X86CheckerSeverity::Error);
assert!(X86CheckerSeverity::Error < X86CheckerSeverity::Fatal);
}
#[test]
fn test_checker_category_display() {
assert_eq!(X86CheckerCategory::MisraC.to_string(), "misra-c");
assert_eq!(X86CheckerCategory::CertC.to_string(), "cert-c");
assert_eq!(X86CheckerCategory::Performance.to_string(), "performance");
assert_eq!(X86CheckerCategory::Bugprone.to_string(), "bugprone");
}
#[test]
fn test_checker_config_new() {
let config = X86CheckerConfig::new("test-checker");
assert!(config.enabled);
assert!(config.severity_override.is_none());
}
#[test]
fn test_checker_config_disabled() {
let config = X86CheckerConfig::disabled("test-checker");
assert!(!config.enabled);
}
#[test]
fn test_checker_config_max_findings() {
let config = X86CheckerConfig::new("test");
assert_eq!(config.max_findings, usize::MAX);
}
#[test]
fn test_checker_finding_display() {
let finding = X86CheckerFinding {
checker_name: "misra-c".into(),
rule_id: "R8.1".into(),
category: X86CheckerCategory::MisraC,
severity: X86CheckerSeverity::Error,
message: "test message".into(),
file: Some("test.c".into()),
line: 10,
column: 5,
end_line: 10,
end_column: 6,
fixit: Some("add void".into()),
suppressed: false,
};
let s = finding.to_string();
assert!(s.contains("misra-c"));
assert!(s.contains("R8.1"));
assert!(s.contains("test.c"));
assert!(s.contains("test message"));
}
#[test]
fn test_checker_result_new() {
let result = X86CheckerResult::new("test-checker", X86CheckerCategory::Custom);
assert_eq!(result.checker_name, "test-checker");
assert!(result.enabled);
assert!(result.findings.is_empty());
}
#[test]
fn test_dep_manager_creation() {
let mgr = X86CheckerDependencyManager::new();
assert!(mgr.dependencies.is_empty());
}
#[test]
fn test_dep_manager_register() {
let mut mgr = X86CheckerDependencyManager::new();
let dep = X86CheckerDependency::new("checker-a").with_requires("checker-b");
mgr.register(dep);
assert!(mgr.dependencies.contains_key("checker-a"));
}
#[test]
fn test_dep_manager_resolve_simple() {
let mut mgr = X86CheckerDependencyManager::new();
mgr.register(X86CheckerDependency::new("a"));
mgr.register(X86CheckerDependency::new("b"));
let enabled: HashSet<String> = ["a", "b"].iter().map(|s| s.to_string()).collect();
let order = mgr.resolve_order(&enabled);
assert_eq!(order.len(), 2);
}
#[test]
fn test_dep_manager_resolve_with_deps() {
let mut mgr = X86CheckerDependencyManager::new();
mgr.register(X86CheckerDependency::new("b").with_requires("a"));
mgr.register(X86CheckerDependency::new("a"));
let enabled: HashSet<String> = ["a", "b"].iter().map(|s| s.to_string()).collect();
let order = mgr.resolve_order(&enabled);
let a_pos = order.iter().position(|n| n == "a").unwrap();
let b_pos = order.iter().position(|n| n == "b").unwrap();
assert!(a_pos < b_pos);
}
#[test]
fn test_dep_manager_suppresses() {
let mut mgr = X86CheckerDependencyManager::new();
mgr.register(X86CheckerDependency::new("a").with_suppresses("b"));
let suppressed = mgr.suppresses("a");
assert!(suppressed.contains(&"b".to_string()));
}
#[test]
fn test_dep_manager_conflicts() {
let mut mgr = X86CheckerDependencyManager::new();
mgr.register(X86CheckerDependency::new("a").with_conflict("b"));
let conflicts = mgr.conflicts("a");
assert!(conflicts.contains(&"b".to_string()));
}
#[test]
fn test_suppression_creation() {
let suppr = X86CheckerSuppression::new();
assert!(suppr.suppressed_checkers.is_empty());
}
#[test]
fn test_suppression_suppress_checker() {
let mut suppr = X86CheckerSuppression::new();
suppr.suppress_checker("misra-c");
assert!(suppr.is_suppressed(&X86CheckerFinding {
checker_name: "misra-c".into(),
rule_id: "R1.1".into(),
category: X86CheckerCategory::MisraC,
severity: X86CheckerSeverity::Error,
message: "".into(),
file: None,
line: 0,
column: 0,
end_line: 0,
end_column: 0,
fixit: None,
suppressed: false,
}));
}
#[test]
fn test_suppression_is_not_suppressed() {
let suppr = X86CheckerSuppression::new();
assert!(!suppr.is_suppressed(&X86CheckerFinding {
checker_name: "misra-c".into(),
rule_id: "R1.1".into(),
category: X86CheckerCategory::MisraC,
severity: X86CheckerSeverity::Error,
message: "".into(),
file: None,
line: 0,
column: 0,
end_line: 0,
end_column: 0,
fixit: None,
suppressed: false,
}));
}
#[test]
fn test_suppression_file_level() {
let mut suppr = X86CheckerSuppression::new();
suppr.suppress_file("test.c", "bugprone");
assert!(suppr.is_suppressed(&X86CheckerFinding {
checker_name: "bugprone".into(),
rule_id: "".into(),
category: X86CheckerCategory::Bugprone,
severity: X86CheckerSeverity::Warning,
message: "".into(),
file: Some("test.c".into()),
line: 1,
column: 0,
end_line: 0,
end_column: 0,
fixit: None,
suppressed: false,
}));
}
#[test]
fn test_suppression_line_level() {
let mut suppr = X86CheckerSuppression::new();
suppr.suppress_line("test.c", 42, "modernize");
assert!(suppr.is_suppressed(&X86CheckerFinding {
checker_name: "modernize".into(),
rule_id: "".into(),
category: X86CheckerCategory::Modernize,
severity: X86CheckerSeverity::Info,
message: "".into(),
file: Some("test.c".into()),
line: 42,
column: 0,
end_line: 0,
end_column: 0,
fixit: None,
suppressed: false,
}));
}
#[test]
fn test_suppression_parse_nolint() {
let mut suppr = X86CheckerSuppression::new();
suppr.parse_nolint_comment("test.c", 10, "// NOLINT(misra-c)");
assert!(suppr.is_suppressed(&X86CheckerFinding {
checker_name: "misra-c".into(),
rule_id: "".into(),
category: X86CheckerCategory::MisraC,
severity: X86CheckerSeverity::Warning,
message: "".into(),
file: Some("test.c".into()),
line: 10,
column: 0,
end_line: 0,
end_column: 0,
fixit: None,
suppressed: false,
}));
}
#[test]
fn test_suppression_parse_nolintnextline() {
let mut suppr = X86CheckerSuppression::new();
suppr.parse_nolint_comment("test.c", 5, "// NOLINTNEXTLINE(bugprone)");
assert!(suppr.is_suppressed(&X86CheckerFinding {
checker_name: "bugprone".into(),
rule_id: "".into(),
category: X86CheckerCategory::Bugprone,
severity: X86CheckerSeverity::Warning,
message: "".into(),
file: Some("test.c".into()),
line: 6,
column: 0,
end_line: 0,
end_column: 0,
fixit: None,
suppressed: false,
}));
}
#[test]
fn test_misra_c_checker_creation() {
let checker = X86MisraCChecker::new();
assert!(!checker.required_rules.is_empty());
assert!(!checker.advisory_rules.is_empty());
assert!(!checker.enabled_rules.is_empty());
}
#[test]
fn test_misra_c_checker_enable_disable() {
let mut checker = X86MisraCChecker::new();
checker.enable_rule("R99.1");
assert!(checker.enabled_rules.contains("R99.1"));
checker.disable_rule("R99.1");
assert!(!checker.enabled_rules.contains("R99.1"));
}
#[test]
fn test_misra_c_check_source() {
let checker = X86MisraCChecker::new();
let findings = checker.check_source("int main() { return 0; }");
assert!(!findings.is_empty());
}
#[test]
fn test_cert_c_checker_creation() {
let checker = X86CertCChecker::new();
assert!(checker.enabled_rules.len() >= 5);
}
#[test]
fn test_cert_cpp_checker_creation() {
let checker = X86CertCppChecker::new();
assert!(checker.enabled_rules.len() >= 5);
}
#[test]
fn test_autosar_checker_creation() {
let checker = X86AutosarChecker::new();
assert!(!checker.enabled_rules.is_empty());
}
#[test]
fn test_core_guidelines_checker_creation() {
let checker = X86CoreGuidelinesChecker::new();
assert!(!checker.enabled_rules.is_empty());
}
#[test]
fn test_performance_checker_creation() {
let checker = X86PerformanceChecker::new();
assert!(checker.detect_unnecessary_copies);
assert!(checker.prefer_emplace);
}
#[test]
fn test_readability_checker_creation() {
let checker = X86ReadabilityChecker::new();
assert!(checker.check_naming);
assert!(checker.check_braces);
}
#[test]
fn test_modernize_checker_creation() {
let checker = X86ModernizeChecker::new();
assert!(checker.suggest_nullptr);
assert!(checker.suggest_override);
}
#[test]
fn test_bugprone_checker_creation() {
let checker = X86BugproneChecker::new();
assert!(checker.check_suspicious_memset);
}
#[test]
fn test_portability_checker_creation() {
let checker = X86PortabilityChecker::new();
assert!(checker.check_type_sizes);
}
#[test]
fn test_security_checker_creation() {
let checker = X86SecurityChecker::new();
assert!(checker.check_buffer_overflow);
}
#[test]
fn test_concurrency_checker_creation() {
let checker = X86ConcurrencyChecker::new();
assert!(checker.check_mutex_double_lock);
}
#[test]
fn test_documentation_checker_creation() {
let checker = X86DocumentationChecker::new();
assert!(checker.require_public_api_docs);
}
#[test]
fn test_toggle_creation() {
let toggle = X86CheckerToggle::new();
assert!(toggle.enabled.is_empty());
}
#[test]
fn test_toggle_enable_disable() {
let mut toggle = X86CheckerToggle::new();
toggle.enable("checker-a");
assert!(toggle.is_enabled("checker-a"));
toggle.disable("checker-a");
assert!(!toggle.is_enabled("checker-a"));
}
#[test]
fn test_toggle_default_enabled() {
let toggle = X86CheckerToggle::new();
assert!(toggle.is_enabled("any-checker")); }
#[test]
fn test_toggle_category() {
let mut toggle = X86CheckerToggle::new();
toggle.enable_category(X86CheckerCategory::Security);
assert!(toggle.is_category_enabled(X86CheckerCategory::Security));
toggle.disable_category(X86CheckerCategory::Security);
assert!(!toggle.is_category_enabled(X86CheckerCategory::Security));
}
#[test]
fn test_deep_checkers_creation() {
let checkers = X86DeepCheckers::new();
assert!(checkers.checker_count() >= 10);
assert!(checkers.enabled_checkers().len() >= 10);
}
#[test]
fn test_deep_checkers_enable_disable() {
let mut checkers = X86DeepCheckers::new();
checkers.disable("misra-c");
assert!(!checkers.toggle.is_enabled("misra-c"));
checkers.enable("misra-c");
assert!(checkers.toggle.is_enabled("misra-c"));
}
#[test]
fn test_deep_checkers_run() {
let mut checkers = X86DeepCheckers::new();
let results = checkers.run("int main(void) { return 0; }");
assert!(!results.is_empty());
}
#[test]
fn test_deep_checkers_run_with_disabled() {
let mut checkers = X86DeepCheckers::new();
checkers.disable("misra-c");
let results = checkers.run("int main(void) { return 0; }");
let misra_found = results.iter().any(|r| r.checker_name == "misra-c");
assert!(!misra_found);
}
#[test]
fn test_deep_checkers_suppress() {
let mut checkers = X86DeepCheckers::new();
checkers.suppress("misra-c");
let results = checkers.run("int main() { return 0; }");
for result in &results {
if result.checker_name == "misra-c" {
assert!(result.findings.iter().all(|f| f.suppressed));
}
}
}
#[test]
fn test_deep_checkers_report() {
let mut checkers = X86DeepCheckers::new();
checkers.run("int x;");
let report = checkers.report();
assert!(report.contains("X86DeepCheckers Report"));
}
#[test]
fn test_deep_checkers_clear_suppressions() {
let mut checkers = X86DeepCheckers::new();
checkers.suppress("misra-c");
checkers.clear_suppressions();
assert!(checkers.suppressions.suppressed_checkers.is_empty());
}
#[test]
fn test_deep_checkers_nolint_parsing() {
let mut checkers = X86DeepCheckers::new();
let source = "// NOLINT(misra-c)\nint main() { return 0; }";
checkers.parse_nolint_from_source("test.c", source);
}
#[test]
fn test_deep_checkers_default() {
let checkers = X86DeepCheckers::default();
assert!(checkers.checker_count() >= 10);
}
#[test]
fn test_make_x86_deep_checkers() {
let checkers = make_x86_deep_checkers();
assert!(checkers.checker_count() >= 10);
}
#[test]
fn test_run_x86_deep_checkers() {
let results = run_x86_deep_checkers("int x;");
assert!(!results.is_empty());
}
#[test]
fn test_run_x86_checker_specific() {
let findings = run_x86_checker("misra-c", "int main() { return 0; }");
assert!(findings.len() >= 0);
}
#[test]
fn test_x86_checker_names() {
let names = x86_checker_names();
assert!(names.len() >= 10);
assert!(names.contains(&"misra-c".to_string()));
assert!(names.contains(&"performance".to_string()));
}
#[test]
fn stress_many_checker_runs() {
let mut checkers = X86DeepCheckers::new();
for i in 0..50 {
let source = format!("int var{};", i);
let _ = checkers.run(&source);
}
assert!(checkers.results.len() >= 10);
}
#[test]
fn stress_dependency_resolution() {
let mut mgr = X86CheckerDependencyManager::new();
for i in 0..30 {
let name = format!("checker-{}", i);
mgr.register(X86CheckerDependency::new(&name));
}
let enabled: HashSet<String> = (0..30).map(|i| format!("checker-{}", i)).collect();
let order = mgr.resolve_order(&enabled);
assert_eq!(order.len(), 30);
}
#[test]
fn stress_suppression_many_lines() {
let mut suppr = X86CheckerSuppression::new();
for i in 0..200 {
suppr.suppress_line("test.c", i, &format!("checker-{}", i % 5));
}
}
#[test]
fn smoke_all_categories_display() {
let categories = [
X86CheckerCategory::MisraC,
X86CheckerCategory::CertC,
X86CheckerCategory::CertCpp,
X86CheckerCategory::Autosar,
X86CheckerCategory::CppCoreGuidelines,
X86CheckerCategory::Performance,
X86CheckerCategory::Readability,
X86CheckerCategory::Modernize,
X86CheckerCategory::Bugprone,
X86CheckerCategory::Portability,
X86CheckerCategory::Security,
X86CheckerCategory::Style,
X86CheckerCategory::Documentation,
X86CheckerCategory::Concurrency,
X86CheckerCategory::Custom,
];
for cat in &categories {
let s = cat.to_string();
assert!(!s.is_empty());
}
}
#[test]
fn smoke_all_severities_display() {
let sevs = [
X86CheckerSeverity::Info,
X86CheckerSeverity::Note,
X86CheckerSeverity::Warning,
X86CheckerSeverity::Error,
X86CheckerSeverity::Fatal,
];
for sev in &sevs {
let s = sev.to_string();
assert!(!s.is_empty());
}
}
#[test]
fn test_misra_c_rule_r13_1() {
let mut checker = X86MisraCChecker::new();
checker.enable_rule("R13.1");
let findings = checker.check_source("int x = (y = 1, z = 2);");
assert!(findings.len() >= 0);
}
#[test]
fn test_misra_c_rule_r17_1() {
let mut checker = X86MisraCChecker::new();
checker.enable_rule("R17.1");
let findings = checker.check_source("void *malloc(size_t n) { return 0; }");
assert!(findings.len() >= 0);
}
#[test]
fn test_misra_c_rule_r21_1() {
let mut checker = X86MisraCChecker::new();
checker.enable_rule("R21.1");
let findings = checker.check_source("#define offsetof(s,m) ((size_t)&(((s*)0)->m))");
assert!(findings.len() >= 0);
}
#[test]
fn test_misra_c_all_required_rules_present() {
let checker = X86MisraCChecker::new();
assert!(checker.required_rules.len() >= 5);
for rule in &checker.required_rules {
assert!(rule.starts_with('R'));
}
}
#[test]
fn test_misra_c_all_advisory_rules_present() {
let checker = X86MisraCChecker::new();
assert!(checker.advisory_rules.len() >= 3);
for rule in &checker.advisory_rules {
assert!(rule.starts_with('A'));
}
}
#[test]
fn test_cert_c_rule_exp33_c() {
let checker = X86CertCChecker::new();
assert!(checker.enabled_rules.contains("EXP33-C"));
let findings = checker.check_source("");
assert!(findings.is_empty());
}
#[test]
fn test_cert_c_rule_int32_c() {
let mut checker = X86CertCChecker::new();
checker.enable_rule("INT32-C");
assert!(checker.enabled_rules.contains("INT32-C"));
}
#[test]
fn test_cert_c_all_rules_have_c_suffix() {
let checker = X86CertCChecker::new();
for rule in &checker.enabled_rules {
assert!(rule.ends_with("-C"), "Rule {} should end with -C", rule);
}
}
#[test]
fn test_cert_cpp_rule_ctr50() {
let checker = X86CertCppChecker::new();
assert!(checker.enabled_rules.contains("CTR50-CPP"));
}
#[test]
fn test_cert_cpp_all_rules_have_cpp_suffix() {
let checker = X86CertCppChecker::new();
for rule in &checker.enabled_rules {
assert!(rule.ends_with("-CPP"), "Rule {} should end with -CPP", rule);
}
}
#[test]
fn test_autosar_rule_format() {
let checker = X86AutosarChecker::new();
for rule in &checker.enabled_rules {
assert!(
rule.starts_with('A'),
"AUTOSAR rule {} should start with A",
rule
);
}
}
#[test]
fn test_core_guidelines_all_rules_format() {
let checker = X86CoreGuidelinesChecker::new();
for rule in &checker.enabled_rules {
assert!(!rule.is_empty());
assert!(rule.contains('.'));
}
}
#[test]
fn test_suppression_wildcard() {
let mut suppr = X86CheckerSuppression::new();
suppr.suppress_line("test.c", 1, "*");
assert!(suppr
.line_suppressions
.contains_key(&("test.c".to_string(), 1)));
}
#[test]
fn test_suppression_clear() {
let mut suppr = X86CheckerSuppression::new();
suppr.suppress_checker("misra-c");
suppr.suppress_rule("R8.1");
suppr.clear();
assert!(suppr.suppressed_checkers.is_empty());
assert!(suppr.suppressed_rules.is_empty());
}
#[test]
fn test_suppression_parse_nolint_multiple() {
let mut suppr = X86CheckerSuppression::new();
suppr.parse_nolint_comment("test.c", 10, "// NOLINT(misra-c, bugprone)");
assert!(suppr.is_suppressed(&X86CheckerFinding {
checker_name: "misra-c".into(),
rule_id: "".into(),
category: X86CheckerCategory::MisraC,
severity: X86CheckerSeverity::Warning,
message: "".into(),
file: Some("test.c".into()),
line: 10,
column: 0,
end_line: 0,
end_column: 0,
fixit: None,
suppressed: false,
}));
}
#[test]
fn test_suppression_nolint_no_parens() {
let mut suppr = X86CheckerSuppression::new();
suppr.parse_nolint_comment("test.c", 5, "// NOLINT");
assert!(suppr
.line_suppressions
.contains_key(&("test.c".to_string(), 5)));
}
#[test]
fn test_dep_manager_cycle_handling() {
let mut mgr = X86CheckerDependencyManager::new();
mgr.register(X86CheckerDependency::new("a").with_requires("b"));
mgr.register(X86CheckerDependency::new("b").with_requires("a"));
let enabled: HashSet<String> = ["a", "b"].iter().map(|s| s.to_string()).collect();
let order = mgr.resolve_order(&enabled);
assert_eq!(order.len(), 2);
}
#[test]
fn test_dep_manager_runs_before() {
let mut mgr = X86CheckerDependencyManager::new();
mgr.register(X86CheckerDependency::new("b"));
mgr.register(X86CheckerDependency::new("a").with_runs_before("b"));
let enabled: HashSet<String> = ["a", "b"].iter().map(|s| s.to_string()).collect();
let order = mgr.resolve_order(&enabled);
let a_pos = order.iter().position(|n| n == "a").unwrap();
let b_pos = order.iter().position(|n| n == "b").unwrap();
assert!(a_pos < b_pos, "a should run before b");
}
#[test]
fn test_dep_manager_runs_after() {
let mut mgr = X86CheckerDependencyManager::new();
mgr.register(X86CheckerDependency::new("a"));
mgr.register(X86CheckerDependency::new("b").with_runs_after("a"));
let enabled: HashSet<String> = ["a", "b"].iter().map(|s| s.to_string()).collect();
let order = mgr.resolve_order(&enabled);
let a_pos = order.iter().position(|n| n == "a").unwrap();
let b_pos = order.iter().position(|n| n == "b").unwrap();
assert!(a_pos < b_pos, "a should run before b");
}
#[test]
fn test_dep_manager_conflicts_detection() {
let mut mgr = X86CheckerDependencyManager::new();
mgr.register(X86CheckerDependency::new("a").with_conflict("b"));
let conflicts = mgr.conflicts("a");
assert_eq!(conflicts.len(), 1);
assert!(conflicts.contains(&"b".to_string()));
}
#[test]
fn test_toggle_category_empty_means_all_enabled() {
let toggle = X86CheckerToggle::new();
assert!(toggle.is_category_enabled(X86CheckerCategory::Security));
assert!(toggle.is_category_enabled(X86CheckerCategory::MisraC));
}
#[test]
fn test_toggle_category_specific() {
let mut toggle = X86CheckerToggle::new();
toggle.enable_category(X86CheckerCategory::Performance);
assert!(toggle.is_category_enabled(X86CheckerCategory::Performance));
assert!(!toggle.is_category_enabled(X86CheckerCategory::Security));
}
#[test]
fn test_deep_checkers_configure_checker() {
let mut checkers = X86DeepCheckers::new();
let config = X86CheckerConfig {
name: "misra-c".into(),
enabled: true,
severity_override: Some(X86CheckerSeverity::Warning),
max_findings: 10,
custom_options: BTreeMap::new(),
suppressions: vec!["R99.1".into()],
};
checkers.configure("misra-c", config);
let cfg = checkers.configs.get("misra-c").unwrap();
assert!(cfg.enabled);
assert_eq!(cfg.max_findings, 10);
}
#[test]
fn test_deep_checkers_enabled_checkers_count() {
let mut checkers = X86DeepCheckers::new();
let all = checkers.checker_count();
let enabled = checkers.enabled_checkers().len();
assert_eq!(all, enabled); }
#[test]
fn test_deep_checkers_after_disable() {
let mut checkers = X86DeepCheckers::new();
let before = checkers.enabled_checkers().len();
checkers.disable("misra-c");
checkers.disable("cert-c");
let after = checkers.enabled_checkers().len();
assert_eq!(after, before - 2);
}
#[test]
fn test_deep_checkers_parse_nolint_source() {
let mut checkers = X86DeepCheckers::new();
let source = "int main() { return 0; } // NOLINT(misra-c)\nint foo(void) { return 1; }";
checkers.parse_nolint_from_source("test.c", source);
}
#[test]
fn test_deep_checkers_total_findings_after_run() {
let mut checkers = X86DeepCheckers::new();
checkers.run("int main() { return 0; }");
assert!(checkers.total_findings >= 0);
}
#[test]
fn test_performance_checker_all_flags() {
let mut checker = X86PerformanceChecker::new();
checker.detect_unnecessary_copies = false;
checker.detect_pass_by_value = false;
checker.prefer_emplace = false;
checker.detect_inefficient_algorithms = false;
let findings = checker.check_source("int x = 1;");
assert!(findings.is_empty());
}
#[test]
fn test_readability_checker_all_flags() {
let mut checker = X86ReadabilityChecker::new();
checker.check_naming = false;
checker.check_braces = false;
checker.check_magic_numbers = false;
checker.check_simplify_bool_expr = false;
checker.check_redundant_control_flow = false;
let findings = checker.check_source("int x = 1;");
assert!(findings.is_empty());
}
#[test]
fn test_modernize_checker_all_suggestions() {
let mut checker = X86ModernizeChecker::new();
checker.suggest_nullptr = false;
checker.suggest_override = false;
checker.suggest_auto = false;
checker.suggest_range_for = false;
checker.suggest_emplace = false;
checker.suggest_make_unique = false;
checker.suggest_make_shared = false;
let findings = checker.check_source("int *p = 0;");
assert!(findings.is_empty());
}
#[test]
fn test_bugprone_checker_disabled() {
let mut checker = X86BugproneChecker::new();
checker.check_suspicious_memset = false;
checker.check_incorrect_roundings = false;
checker.check_misplaced_widening_cast = false;
checker.check_bool_pointer_implicit_conversion = false;
checker.check_assert_side_effect = false;
checker.check_swap_with_self = false;
let findings = checker.check_source("int x;");
assert!(findings.is_empty());
}
#[test]
fn stress_many_checker_configurations() {
let mut checkers = X86DeepCheckers::new();
for i in 0..100 {
let name = format!("custom-checker-{}", i);
checkers.configure(&name, X86CheckerConfig::new(&name));
}
assert!(checkers.checker_count() >= 10);
}
#[test]
fn stress_many_suppressions() {
let mut suppr = X86CheckerSuppression::new();
for i in 0..500 {
suppr.suppress_line("large.c", i % 200, &format!("checker-{}", i % 10));
}
}
#[test]
fn stress_deep_checkers_repeated_runs() {
let mut checkers = X86DeepCheckers::new();
for _ in 0..10 {
let results = checkers.run("int x; int y; int z;");
assert!(!results.is_empty());
}
}
#[test]
fn stress_dependency_resolution_large_graph() {
let mut mgr = X86CheckerDependencyManager::new();
for i in 0..50 {
let dep = if i > 0 {
X86CheckerDependency::new(&format!("checker-{}", i))
.with_requires(&format!("checker-{}", i - 1))
} else {
X86CheckerDependency::new("checker-0")
};
mgr.register(dep);
}
let enabled: HashSet<String> = (0..50).map(|i| format!("checker-{}", i)).collect();
let order = mgr.resolve_order(&enabled);
assert_eq!(order.len(), 50);
for i in 1..50 {
let prev = format!("checker-{}", i - 1);
let curr = format!("checker-{}", i);
let prev_pos = order.iter().position(|n| n == &prev).unwrap();
let curr_pos = order.iter().position(|n| n == &curr).unwrap();
assert!(
prev_pos < curr_pos,
"checker-{} should come before checker-{}",
i - 1,
i
);
}
}
#[test]
fn bulk_misra_r1() {
assert!(X86MisraCChecker::default().required_rules.contains("R1.1"));
}
#[test]
fn bulk_misra_r2() {
assert!(X86MisraCChecker::default().required_rules.contains("R2.1"));
}
#[test]
fn bulk_misra_r8() {
assert!(X86MisraCChecker::default().required_rules.contains("R8.1"));
}
#[test]
fn bulk_misra_r10() {
assert!(X86MisraCChecker::default().required_rules.contains("R10.1"));
}
#[test]
fn bulk_misra_r13() {
assert!(X86MisraCChecker::default().required_rules.contains("R13.1"));
}
#[test]
fn bulk_cert_exp33() {
assert!(X86CertCChecker::default().enabled_rules.contains("EXP33-C"));
}
#[test]
fn bulk_cert_int32() {
assert!(X86CertCChecker::default().enabled_rules.contains("INT32-C"));
}
#[test]
fn bulk_cert_str31() {
assert!(X86CertCChecker::default().enabled_rules.contains("STR31-C"));
}
#[test]
fn bulk_cert_mem30() {
assert!(X86CertCChecker::default().enabled_rules.contains("MEM30-C"));
}
#[test]
fn bulk_certcpp_ctr() {
assert!(X86CertCppChecker::default()
.enabled_rules
.contains("CTR50-CPP"));
}
#[test]
fn bulk_certcpp_oop() {
assert!(X86CertCppChecker::default()
.enabled_rules
.contains("OOP50-CPP"));
}
#[test]
fn bulk_autosar_a0() {
assert!(X86AutosarChecker::default()
.enabled_rules
.contains("A0-1-1"));
}
#[test]
fn bulk_autosar_a3() {
assert!(X86AutosarChecker::default()
.enabled_rules
.contains("A3-1-1"));
}
#[test]
fn bulk_core_c128() {
assert!(X86CoreGuidelinesChecker::default()
.enabled_rules
.contains("C.128"));
}
#[test]
fn bulk_core_c35() {
assert!(X86CoreGuidelinesChecker::default()
.enabled_rules
.contains("C.35"));
}
#[test]
fn bulk_core_es23() {
assert!(X86CoreGuidelinesChecker::default()
.enabled_rules
.contains("ES.23"));
}
#[test]
fn bulk_perf_copies() {
assert!(X86PerformanceChecker::default().detect_unnecessary_copies);
}
#[test]
fn bulk_perf_byval() {
assert!(X86PerformanceChecker::default().detect_pass_by_value);
}
#[test]
fn bulk_perf_emplace() {
assert!(X86PerformanceChecker::default().prefer_emplace);
}
#[test]
fn bulk_read_name() {
assert!(X86ReadabilityChecker::default().check_naming);
}
#[test]
fn bulk_read_brace() {
assert!(X86ReadabilityChecker::default().check_braces);
}
#[test]
fn bulk_read_magic() {
assert!(X86ReadabilityChecker::default().check_magic_numbers);
}
#[test]
fn bulk_mod_nullptr() {
assert!(X86ModernizeChecker::default().suggest_nullptr);
}
#[test]
fn bulk_mod_override() {
assert!(X86ModernizeChecker::default().suggest_override);
}
#[test]
fn bulk_mod_auto() {
assert!(X86ModernizeChecker::default().suggest_auto);
}
#[test]
fn bulk_mod_range() {
assert!(X86ModernizeChecker::default().suggest_range_for);
}
#[test]
fn bulk_bug_memset() {
assert!(X86BugproneChecker::default().check_suspicious_memset);
}
#[test]
fn bulk_bug_round() {
assert!(X86BugproneChecker::default().check_incorrect_roundings);
}
#[test]
fn bulk_bug_swap() {
assert!(X86BugproneChecker::default().check_swap_with_self);
}
#[test]
fn bulk_port_size() {
assert!(X86PortabilityChecker::default().check_type_sizes);
}
#[test]
fn bulk_port_align() {
assert!(X86PortabilityChecker::default().check_alignment);
}
#[test]
fn bulk_sec_buf() {
assert!(X86SecurityChecker::default().check_buffer_overflow);
}
#[test]
fn bulk_sec_fmt() {
assert!(X86SecurityChecker::default().check_format_string);
}
#[test]
fn bulk_sec_int() {
assert!(X86SecurityChecker::default().check_integer_overflow);
}
#[test]
fn bulk_sec_uaf() {
assert!(X86SecurityChecker::default().check_use_after_free);
}
#[test]
fn bulk_conc_lock() {
assert!(X86ConcurrencyChecker::default().check_mutex_double_lock);
}
#[test]
fn bulk_conc_unlock() {
assert!(X86ConcurrencyChecker::default().check_unlock_without_lock);
}
#[test]
fn bulk_conc_race() {
assert!(X86ConcurrencyChecker::default().check_shared_data_race);
}
#[test]
fn bulk_doc_api() {
assert!(X86DocumentationChecker::default().require_public_api_docs);
}
#[test]
fn bulk_doc_param() {
assert!(X86DocumentationChecker::default().require_param_docs);
}
}