diff --git a/fuzz/fuzz_targets/rome_common.rs b/fuzz/fuzz_targets/rome_common.rs index 158045afc7a..f79a525f375 100644 --- a/fuzz/fuzz_targets/rome_common.rs +++ b/fuzz/fuzz_targets/rome_common.rs @@ -18,6 +18,7 @@ use rome_json_parser::parse_json; use rome_service::Rules; use similar::TextDiff; use std::fmt::{Display, Formatter}; +use std::sync::OnceLock; pub fn fuzz_js_parser_with_source_type(data: &[u8], source: JsFileSource) -> Corpus { let Ok(code1) = std::str::from_utf8(data) else { return Corpus::Reject; }; @@ -32,9 +33,13 @@ pub fn fuzz_js_parser_with_source_type(data: &[u8], source: JsFileSource) -> Cor Corpus::Keep } -static mut ANALYSIS_RULES: Option = None; -static mut ANALYSIS_RULE_FILTERS: Option> = None; -static mut ANALYSIS_OPTIONS: Option = None; +static ANALYSIS_RULES: OnceLock = OnceLock::new(); +static ANALYSIS_RULE_FILTERS: OnceLock> = OnceLock::new(); + +// have to use thread local because AnalyzerOptions contains a Box, which isn't thread-safe +thread_local! { + static ANALYSIS_OPTIONS: AnalyzerOptions = AnalyzerOptions::default(); +} struct DiagnosticDescriptionExtractor<'a, D> { diagnostic: &'a D, @@ -58,43 +63,36 @@ where pub fn fuzz_js_formatter_with_source_type(data: &[u8], source: JsFileSource) -> Corpus { let Ok(code1) = std::str::from_utf8(data) else { return Corpus::Reject; }; - // TODO: replace with OnceLock when upgrading to 1.70 - let rule_filters = if let Some(rules) = unsafe { ANALYSIS_RULE_FILTERS.as_ref() } { - rules - } else { - let rules = unsafe { - ANALYSIS_RULES.get_or_insert_with(|| Rules { - all: Some(true), - ..Default::default() - }) - }; - let rules = rules.as_enabled_rules().into_iter().collect::>(); - unsafe { - ANALYSIS_RULE_FILTERS = Some(rules); - ANALYSIS_RULE_FILTERS.as_ref().unwrap_unchecked() - } - }; - let options = unsafe { ANALYSIS_OPTIONS.get_or_insert_with(AnalyzerOptions::default) }; + let rules = ANALYSIS_RULES.get_or_init(|| Rules { + all: Some(true), + ..Default::default() + }); + let rule_filters = ANALYSIS_RULE_FILTERS + .get_or_init(|| rules.as_enabled_rules().into_iter().collect::>()); let parse1 = parse(code1, source); if !parse1.has_errors() { let language = JsFormatLanguage::new(JsFormatOptions::new(source)); let tree1 = parse1.tree(); let mut linter_errors = Vec::new(); - let _ = analyze( - &tree1, - AnalysisFilter::from_enabled_rules(Some(rule_filters)), - options, - source, - |e| -> ControlFlow<()> { - if let Some(diagnostic) = e.diagnostic() { - linter_errors - .push(DiagnosticDescriptionExtractor::new(&diagnostic).to_string()); - } + let _ = ANALYSIS_OPTIONS + .try_with(|options| { + analyze( + &tree1, + AnalysisFilter::from_enabled_rules(Some(rule_filters)), + options, + source, + |e| -> ControlFlow<()> { + if let Some(diagnostic) = e.diagnostic() { + linter_errors + .push(DiagnosticDescriptionExtractor::new(&diagnostic).to_string()); + } - ControlFlow::Continue(()) - }, - ); + ControlFlow::Continue(()) + }, + ) + }) + .unwrap(); let syntax1 = parse1.syntax(); if let Ok(formatted1) = format_node(&syntax1, language.clone()) { if let Ok(printed1) = formatted1.print() { @@ -108,25 +106,32 @@ pub fn fuzz_js_formatter_with_source_type(data: &[u8], source: JsFileSource) -> .header("original code", "formatted") ); let tree2 = parse2.tree(); - let (maybe_diagnostic, _) = analyze( - &tree2, - AnalysisFilter::from_enabled_rules(Some(rule_filters)), - options, - source, - |e| { - if let Some(diagnostic) = e.diagnostic() { - let new_error = - DiagnosticDescriptionExtractor::new(&diagnostic).to_string(); - if let Some(idx) = linter_errors.iter().position(|e| *e == new_error) { - linter_errors.remove(idx); - } else { - return ControlFlow::Break(new_error); - } - } - - ControlFlow::Continue(()) - }, - ); + let (maybe_diagnostic, _) = ANALYSIS_OPTIONS + .try_with(|options| { + analyze( + &tree2, + AnalysisFilter::from_enabled_rules(Some(rule_filters)), + options, + source, + |e| { + if let Some(diagnostic) = e.diagnostic() { + let new_error = + DiagnosticDescriptionExtractor::new(&diagnostic) + .to_string(); + if let Some(idx) = + linter_errors.iter().position(|e| *e == new_error) + { + linter_errors.remove(idx); + } else { + return ControlFlow::Break(new_error); + } + } + + ControlFlow::Continue(()) + }, + ) + }) + .unwrap(); if let Some(diagnostic) = maybe_diagnostic { panic!( "formatter introduced linter failure: {} (expected one of: {})\n{}",