fn mark_function_body(lines: &[&str], fn_line: usize, protected: &mut HashSet<usize>) {
let mut depth: usize = 0;
let mut entered_body = false;
for k in fn_line..lines.len() {
depth += lines[k].matches('{').count();
if depth > 0 {
entered_body = true;
}
depth = depth.saturating_sub(lines[k].matches('}').count());
protected.insert(k);
if entered_body && depth == 0 {
break;
}
}
}
pub(super) fn compute_target_feature_protected_lines(lines: &[&str]) -> HashSet<usize> {
let mut protected = HashSet::new();
for (i, line) in lines.iter().enumerate() {
let is_protected = line.trim().starts_with("#[target_feature")
|| (line.contains("#[cfg(") && line.contains("target_feature"));
if !is_protected {
continue;
}
for j in i..lines.len() {
if lines[j].contains("fn ") {
mark_function_body(lines, j, &mut protected);
break;
}
}
}
protected
}
fn check_file_for_simd_violations(entry: &Path) -> Vec<CbPatternViolation> {
let content = match fs::read_to_string(entry) {
Ok(c) => c,
Err(_) => return vec![],
};
let lines: Vec<&str> = content.lines().collect();
let test_lines = compute_test_code_lines(&lines);
let protected_lines = compute_target_feature_protected_lines(&lines);
let file_path = entry.display().to_string();
let mut violations = Vec::new();
for (line_num, line) in lines.iter().enumerate() {
if test_lines.contains(&line_num) || protected_lines.contains(&line_num) {
continue;
}
for &(pattern, kind) in SIMD_INTRINSIC_PATTERNS.iter().chain(PORTABLE_SIMD_PATTERNS) {
if line.contains(pattern) {
violations.push(CbPatternViolation {
pattern_id: "CB-021".to_string(),
file: file_path.clone(),
line: line_num + 1,
description: format!("{kind} {pattern} without #[target_feature]"),
severity: Severity::Warning,
});
}
}
}
violations
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub fn detect_cb021_simd_without_target_feature(project_path: &Path) -> Vec<CbPatternViolation> {
let src_dir = project_path.join("src");
if !src_dir.exists() {
return vec![];
}
let entries = match walkdir_rs_files(&src_dir) {
Ok(e) => e,
Err(_) => return vec![],
};
entries
.iter()
.flat_map(|e| check_file_for_simd_violations(e))
.collect()
}