use std::num::NonZeroUsize;
use rustc_ast::MacCall;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use super::parser::{build_raw_string_suggestion, scan_body};
use super::queue::PendingViolation;
use super::resolved_config;
use crate::macro_template::find_all_cooked_str_literals;
pub(super) struct PreferRawStringEarly {
min_escapes_to_trigger: NonZeroUsize,
eligible_escapes: Vec<String>,
}
impl PreferRawStringEarly {
pub(super) fn new() -> Self {
let resolved = resolved_config();
Self {
min_escapes_to_trigger: resolved.min_escapes_to_trigger,
eligible_escapes: resolved.eligible_escapes,
}
}
}
impl EarlyLintPass for PreferRawStringEarly {
fn check_mac(&mut self, lint_context: &EarlyContext<'_>, mac_call: &MacCall) {
let source_map = lint_context.sess().source_map();
for literal_span in find_all_cooked_str_literals(&mac_call.args.tokens) {
let Ok(snippet) = source_map.span_to_snippet(literal_span) else {
continue;
};
let Some(body) = snippet
.strip_prefix('"')
.and_then(|rest| rest.strip_suffix('"'))
else {
continue;
};
let Some(scan) = scan_body(body, &self.eligible_escapes) else {
continue;
};
if scan.eliminable_count < self.min_escapes_to_trigger.get() {
continue;
}
super::queue(PendingViolation {
span: literal_span,
suggestion: build_raw_string_suggestion(&scan.decoded),
});
}
}
}