@@ -18,6 +18,7 @@ use rome_json_parser::parse_json;
1818use rome_service:: Rules ;
1919use similar:: TextDiff ;
2020use std:: fmt:: { Display , Formatter } ;
21+ use std:: sync:: OnceLock ;
2122
2223pub fn fuzz_js_parser_with_source_type ( data : & [ u8 ] , source : JsFileSource ) -> Corpus {
2324 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
3233 Corpus :: Keep
3334}
3435
35- static mut ANALYSIS_RULES : Option < Rules > = None ;
36- static mut ANALYSIS_RULE_FILTERS : Option < Vec < RuleFilter > > = None ;
37- static mut ANALYSIS_OPTIONS : Option < AnalyzerOptions > = None ;
36+ static ANALYSIS_RULES : OnceLock < Rules > = OnceLock :: new ( ) ;
37+ static ANALYSIS_RULE_FILTERS : OnceLock < Vec < RuleFilter > > = OnceLock :: new ( ) ;
38+
39+ // have to use thread local because AnalyzerOptions contains a Box<dyn Any>, which isn't thread-safe
40+ thread_local ! {
41+ static ANALYSIS_OPTIONS : AnalyzerOptions = AnalyzerOptions :: default ( ) ;
42+ }
3843
3944struct DiagnosticDescriptionExtractor < ' a , D > {
4045 diagnostic : & ' a D ,
@@ -58,43 +63,36 @@ where
5863pub fn fuzz_js_formatter_with_source_type ( data : & [ u8 ] , source : JsFileSource ) -> Corpus {
5964 let Ok ( code1) = std:: str:: from_utf8 ( data) else { return Corpus :: Reject ; } ;
6065
61- // TODO: replace with OnceLock when upgrading to 1.70
62- let rule_filters = if let Some ( rules) = unsafe { ANALYSIS_RULE_FILTERS . as_ref ( ) } {
63- rules
64- } else {
65- let rules = unsafe {
66- ANALYSIS_RULES . get_or_insert_with ( || Rules {
67- all : Some ( true ) ,
68- ..Default :: default ( )
69- } )
70- } ;
71- let rules = rules. as_enabled_rules ( ) . into_iter ( ) . collect :: < Vec < _ > > ( ) ;
72- unsafe {
73- ANALYSIS_RULE_FILTERS = Some ( rules) ;
74- ANALYSIS_RULE_FILTERS . as_ref ( ) . unwrap_unchecked ( )
75- }
76- } ;
77- let options = unsafe { ANALYSIS_OPTIONS . get_or_insert_with ( AnalyzerOptions :: default) } ;
66+ let rules = ANALYSIS_RULES . get_or_init ( || Rules {
67+ all : Some ( true ) ,
68+ ..Default :: default ( )
69+ } ) ;
70+ let rule_filters = ANALYSIS_RULE_FILTERS
71+ . get_or_init ( || rules. as_enabled_rules ( ) . into_iter ( ) . collect :: < Vec < _ > > ( ) ) ;
7872
7973 let parse1 = parse ( code1, source) ;
8074 if !parse1. has_errors ( ) {
8175 let language = JsFormatLanguage :: new ( JsFormatOptions :: new ( source) ) ;
8276 let tree1 = parse1. tree ( ) ;
8377 let mut linter_errors = Vec :: new ( ) ;
84- let _ = analyze (
85- & tree1,
86- AnalysisFilter :: from_enabled_rules ( Some ( rule_filters) ) ,
87- options,
88- source,
89- |e| -> ControlFlow < ( ) > {
90- if let Some ( diagnostic) = e. diagnostic ( ) {
91- linter_errors
92- . push ( DiagnosticDescriptionExtractor :: new ( & diagnostic) . to_string ( ) ) ;
93- }
78+ let _ = ANALYSIS_OPTIONS
79+ . try_with ( |options| {
80+ analyze (
81+ & tree1,
82+ AnalysisFilter :: from_enabled_rules ( Some ( rule_filters) ) ,
83+ options,
84+ source,
85+ |e| -> ControlFlow < ( ) > {
86+ if let Some ( diagnostic) = e. diagnostic ( ) {
87+ linter_errors
88+ . push ( DiagnosticDescriptionExtractor :: new ( & diagnostic) . to_string ( ) ) ;
89+ }
9490
95- ControlFlow :: Continue ( ( ) )
96- } ,
97- ) ;
91+ ControlFlow :: Continue ( ( ) )
92+ } ,
93+ )
94+ } )
95+ . unwrap ( ) ;
9896 let syntax1 = parse1. syntax ( ) ;
9997 if let Ok ( formatted1) = format_node ( & syntax1, language. clone ( ) ) {
10098 if let Ok ( printed1) = formatted1. print ( ) {
@@ -108,25 +106,32 @@ pub fn fuzz_js_formatter_with_source_type(data: &[u8], source: JsFileSource) ->
108106 . header( "original code" , "formatted" )
109107 ) ;
110108 let tree2 = parse2. tree ( ) ;
111- let ( maybe_diagnostic, _) = analyze (
112- & tree2,
113- AnalysisFilter :: from_enabled_rules ( Some ( rule_filters) ) ,
114- options,
115- source,
116- |e| {
117- if let Some ( diagnostic) = e. diagnostic ( ) {
118- let new_error =
119- DiagnosticDescriptionExtractor :: new ( & diagnostic) . to_string ( ) ;
120- if let Some ( idx) = linter_errors. iter ( ) . position ( |e| * e == new_error) {
121- linter_errors. remove ( idx) ;
122- } else {
123- return ControlFlow :: Break ( new_error) ;
124- }
125- }
126-
127- ControlFlow :: Continue ( ( ) )
128- } ,
129- ) ;
109+ let ( maybe_diagnostic, _) = ANALYSIS_OPTIONS
110+ . try_with ( |options| {
111+ analyze (
112+ & tree2,
113+ AnalysisFilter :: from_enabled_rules ( Some ( rule_filters) ) ,
114+ options,
115+ source,
116+ |e| {
117+ if let Some ( diagnostic) = e. diagnostic ( ) {
118+ let new_error =
119+ DiagnosticDescriptionExtractor :: new ( & diagnostic)
120+ . to_string ( ) ;
121+ if let Some ( idx) =
122+ linter_errors. iter ( ) . position ( |e| * e == new_error)
123+ {
124+ linter_errors. remove ( idx) ;
125+ } else {
126+ return ControlFlow :: Break ( new_error) ;
127+ }
128+ }
129+
130+ ControlFlow :: Continue ( ( ) )
131+ } ,
132+ )
133+ } )
134+ . unwrap ( ) ;
130135 if let Some ( diagnostic) = maybe_diagnostic {
131136 panic ! (
132137 "formatter introduced linter failure: {} (expected one of: {})\n {}" ,
0 commit comments