pub use crate::domain::{Dimension, Suppression};
pub const ANNOTATION_WINDOW: usize = 3;
pub fn is_within_window(annotation_line: usize, target_line: usize) -> bool {
annotation_line <= target_line && target_line - annotation_line <= ANNOTATION_WINDOW
}
pub fn has_annotation_in_window(
lines: &std::collections::HashSet<usize>,
target_line: usize,
) -> bool {
(0..=ANNOTATION_WINDOW).any(|off| target_line >= off && lines.contains(&(target_line - off)))
}
pub fn is_api_marker(trimmed: &str) -> bool {
trimmed == "// qual:api" || trimmed.starts_with("// qual:api ")
}
pub fn is_test_helper_marker(trimmed: &str) -> bool {
trimmed == "// qual:test_helper" || trimmed.starts_with("// qual:test_helper ")
}
pub fn is_unsafe_allow_marker(trimmed: &str) -> bool {
trimmed == "// qual:allow(unsafe)" || trimmed.starts_with("// qual:allow(unsafe) ")
}
pub fn is_recursive_marker(trimmed: &str) -> bool {
trimmed == "// qual:recursive" || trimmed.starts_with("// qual:recursive ")
}
pub fn parse_inverse_marker(trimmed: &str) -> Option<String> {
trimmed
.strip_prefix("// qual:inverse(")
.and_then(|rest| rest.strip_suffix(')'))
.map(|name| name.trim().to_string())
.filter(|name| !name.is_empty())
}
pub fn parse_suppression(line_number: usize, trimmed: &str) -> Option<Suppression> {
if is_unsafe_allow_marker(trimmed) || is_test_helper_marker(trimmed) {
return None;
}
trimmed
.strip_prefix("// qual:allow")
.and_then(|rest| parse_qual_allow(line_number, rest))
.or_else(|| parse_iosp_legacy(line_number, trimmed))
}
fn parse_iosp_legacy(line_number: usize, trimmed: &str) -> Option<Suppression> {
if trimmed == "// iosp:allow" || trimmed.starts_with("// iosp:allow ") {
let reason = trimmed
.strip_prefix("// iosp:allow ")
.map(|s| s.to_string());
Some(Suppression {
line: line_number,
dimensions: vec![Dimension::Iosp],
reason,
})
} else {
None
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum InvalidQualAllow {
UnknownDimensions(String),
UnclosedParens(String),
}
impl InvalidQualAllow {
pub fn reason(&self) -> String {
match self {
Self::UnknownDimensions(spec) => format!(
"invalid qual:allow — '{spec}' did not parse to any known dimension"
),
Self::UnclosedParens(spec) => format!(
"invalid qual:allow — marker has unclosed parens (missing `)`); content was '{spec}'"
),
}
}
}
pub fn detect_invalid_qual_allow(trimmed: &str) -> Option<InvalidQualAllow> {
if is_unsafe_allow_marker(trimmed) {
return None;
}
let rest = trimmed.strip_prefix("// qual:allow")?.trim_start();
if !rest.starts_with('(') {
return None;
}
let (dims_str, malformed_parens) = match rest.find(')') {
Some(close_paren) => (rest[1..close_paren].trim().to_string(), false),
None => (rest[1..].trim().to_string(), true),
};
if dims_str.is_empty() {
return None;
}
if malformed_parens {
return Some(InvalidQualAllow::UnclosedParens(dims_str));
}
let any_recognized = dims_str
.split(',')
.any(|s| Dimension::from_str_opt(s.trim()).is_some());
if any_recognized {
return None;
}
Some(InvalidQualAllow::UnknownDimensions(dims_str))
}
fn parse_qual_allow(line_number: usize, rest: &str) -> Option<Suppression> {
let rest = rest.trim();
let (dimensions, reason_text) = if rest.is_empty() || !rest.starts_with('(') {
(vec![], rest)
} else {
let close_paren = rest.find(')')?;
let dims_str = &rest[1..close_paren];
let dimensions: Vec<Dimension> = dims_str
.split(',')
.filter_map(|s| Dimension::from_str_opt(s.trim()))
.collect();
let after_parens = rest.get(close_paren + 1..).map(str::trim).unwrap_or("");
(dimensions, after_parens)
};
if dimensions.is_empty() {
return None;
}
let reason = (!reason_text.is_empty())
.then(|| extract_reason(reason_text))
.flatten();
Some(Suppression {
line: line_number,
dimensions,
reason,
})
}
fn extract_reason(text: &str) -> Option<String> {
let text = text.trim();
if text.is_empty() {
return None;
}
if let Some(rest) = text.strip_prefix("reason:") {
let rest = rest.trim();
if rest.starts_with('"') && rest.ends_with('"') && rest.len() > 1 {
return Some(rest[1..rest.len() - 1].to_string());
}
return Some(rest.to_string());
}
Some(text.to_string())
}