@@ -18,6 +18,7 @@ use rome_json_parser::parse_json;
18
18
use rome_service:: Rules ;
19
19
use similar:: TextDiff ;
20
20
use std:: fmt:: { Display , Formatter } ;
21
+ use std:: sync:: OnceLock ;
21
22
22
23
pub fn fuzz_js_parser_with_source_type ( data : & [ u8 ] , source : JsFileSource ) -> Corpus {
23
24
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
32
33
Corpus :: Keep
33
34
}
34
35
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
+ }
38
43
39
44
struct DiagnosticDescriptionExtractor < ' a , D > {
40
45
diagnostic : & ' a D ,
@@ -58,43 +63,36 @@ where
58
63
pub fn fuzz_js_formatter_with_source_type ( data : & [ u8 ] , source : JsFileSource ) -> Corpus {
59
64
let Ok ( code1) = std:: str:: from_utf8 ( data) else { return Corpus :: Reject ; } ;
60
65
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 < _ > > ( ) ) ;
78
72
79
73
let parse1 = parse ( code1, source) ;
80
74
if !parse1. has_errors ( ) {
81
75
let language = JsFormatLanguage :: new ( JsFormatOptions :: new ( source) ) ;
82
76
let tree1 = parse1. tree ( ) ;
83
77
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
+ }
94
90
95
- ControlFlow :: Continue ( ( ) )
96
- } ,
97
- ) ;
91
+ ControlFlow :: Continue ( ( ) )
92
+ } ,
93
+ )
94
+ } )
95
+ . unwrap ( ) ;
98
96
let syntax1 = parse1. syntax ( ) ;
99
97
if let Ok ( formatted1) = format_node ( & syntax1, language. clone ( ) ) {
100
98
if let Ok ( printed1) = formatted1. print ( ) {
@@ -108,25 +106,32 @@ pub fn fuzz_js_formatter_with_source_type(data: &[u8], source: JsFileSource) ->
108
106
. header( "original code" , "formatted" )
109
107
) ;
110
108
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 ( ) ;
130
135
if let Some ( diagnostic) = maybe_diagnostic {
131
136
panic ! (
132
137
"formatter introduced linter failure: {} (expected one of: {})\n {}" ,
0 commit comments