@@ -5,23 +5,22 @@ use oxc_span::{GetSpan, Span};
55
66use crate :: { AstNode , ast_util:: get_preceding_indent_str, context:: LintContext , rule:: Rule } ;
77
8- #[ derive( Clone , Copy ) ]
9- enum Diagnostic {
10- EmptyClause ,
11- MissingBraces ,
12- UnnecessaryBraces ,
8+ fn switch_case_braces_diagnostic_empty_clause ( span : Span ) -> OxcDiagnostic {
9+ OxcDiagnostic :: warn ( "Unexpected braces in empty case clause." )
10+ . with_help ( "Remove braces in empty case clause." )
11+ . with_label ( span)
1312}
1413
15- fn switch_case_braces_diagnostic ( span : Span , diagnostic_type : Diagnostic ) -> OxcDiagnostic {
16- ( match diagnostic_type {
17- Diagnostic :: EmptyClause => OxcDiagnostic :: warn ( "Unexpected braces in empty case clause.")
18- . with_help ( "Remove braces in empty case clause." ) ,
19- Diagnostic :: MissingBraces => OxcDiagnostic :: warn ( "Missing braces in case clause." )
20- . with_help ( "Add Braces for case clause." ) ,
21- Diagnostic :: UnnecessaryBraces = > OxcDiagnostic :: warn ( "Unnecessary braces in case clause." )
22- . with_help ( "Remove Braces for case clause.") ,
23- } )
24- . with_label ( span)
14+ fn switch_case_braces_diagnostic_missing_braces ( span : Span ) -> OxcDiagnostic {
15+ OxcDiagnostic :: warn ( "Missing braces in case clause." )
16+ . with_help ( "Add Braces for case clause.")
17+ . with_label ( span )
18+ }
19+
20+ fn switch_case_braces_diagnostic_unnecessary_braces ( span : Span ) - > OxcDiagnostic {
21+ OxcDiagnostic :: warn ( "Unnecessary braces in case clause.")
22+ . with_help ( "Remove Braces for case clause." )
23+ . with_label ( span)
2524}
2625
2726#[ derive( Debug , Default , Clone ) ]
@@ -76,9 +75,8 @@ declare_oxc_lint!(
7675
7776impl Rule for SwitchCaseBraces {
7877 fn from_configuration ( value : serde_json:: Value ) -> Self {
79- let always = value. get ( 0 ) . is_none_or ( |v| v. as_str ( ) != Some ( "avoid" ) ) ;
80-
81- Self { always_braces : always }
78+ let always_braces = value. get ( 0 ) . is_none_or ( |v| v. as_str ( ) != Some ( "avoid" ) ) ;
79+ Self { always_braces }
8280 }
8381
8482 fn run < ' a > ( & self , node : & AstNode < ' a > , ctx : & LintContext < ' a > ) {
@@ -91,130 +89,70 @@ impl Rule for SwitchCaseBraces {
9189 }
9290
9391 for case in & switch. cases {
94- for case_consequent in & case. consequent {
95- match case_consequent {
96- Statement :: BlockStatement ( case_block) => {
97- if case_block. body . is_empty ( ) {
98- ctx. diagnostic_with_fix (
99- switch_case_braces_diagnostic (
100- case_block. span ,
101- Diagnostic :: EmptyClause ,
102- ) ,
103- |fixer| fixer. delete_range ( case_block. span ) ,
104- ) ;
105- }
106-
107- if !self . always_braces
108- && !case_block. body . iter ( ) . any ( |stmt| {
109- matches ! (
110- stmt,
111- Statement :: VariableDeclaration ( _)
112- | Statement :: FunctionDeclaration ( _)
113- )
114- } )
115- {
116- ctx. diagnostic_with_fix (
117- switch_case_braces_diagnostic (
118- case_block. span ( ) ,
119- Diagnostic :: UnnecessaryBraces ,
120- ) ,
121- |fixer| {
122- fixer. replace (
123- case_block. span ,
124- fixer. source_range ( Span :: new (
125- case_block. span . start + 1 ,
126- case_block. span . end - 1 ,
127- ) ) ,
128- )
129- } ,
130- ) ;
131- }
92+ if case. consequent . is_empty ( ) {
93+ continue ;
94+ }
95+ let missing_braces = match & case. consequent [ 0 ] {
96+ Statement :: BlockStatement ( block_stmt) if case. consequent . len ( ) == 1 => {
97+ if block_stmt. body . is_empty ( ) {
98+ ctx. diagnostic_with_fix (
99+ switch_case_braces_diagnostic_empty_clause ( block_stmt. span ) ,
100+ |fixer| fixer. delete_range ( block_stmt. span ) ,
101+ ) ;
102+ continue ;
132103 }
133- Statement :: EmptyStatement ( _) => { }
134- _ => {
135- if !self . always_braces
136- && !& case. consequent . iter ( ) . any ( |stmt| {
137- matches ! (
138- stmt,
139- Statement :: VariableDeclaration ( _)
140- | Statement :: FunctionDeclaration ( _)
141- )
142- } )
143- {
144- return ;
145- }
146-
147- let Some ( first_statement) = & case. consequent . first ( ) else {
148- return ;
149- } ;
150- let Some ( last_statement) = & case. consequent . last ( ) else {
151- return ;
152- } ;
153-
154- let case_body_span =
155- Span :: new ( first_statement. span ( ) . start , last_statement. span ( ) . end ) ;
156-
104+ if !self . always_braces
105+ && !block_stmt. body . iter ( ) . any ( |stmt| {
106+ matches ! (
107+ stmt,
108+ Statement :: VariableDeclaration ( _)
109+ | Statement :: FunctionDeclaration ( _)
110+ )
111+ } )
112+ {
157113 ctx. diagnostic_with_fix (
158- switch_case_braces_diagnostic (
159- case_body_span,
160- Diagnostic :: MissingBraces ,
161- ) ,
114+ switch_case_braces_diagnostic_unnecessary_braces ( block_stmt. span ( ) ) ,
162115 |fixer| {
163- let modified_code = {
164- let mut formatter = fixer. codegen ( ) ;
165-
166- if let Some ( case_test) = & case. test {
167- formatter. print_str ( "case " ) ;
168- formatter. print_expression ( case_test) ;
169- } else {
170- formatter. print_str ( "default" ) ;
171- }
172-
173- formatter. print_ascii_byte ( b':' ) ;
174- formatter. print_ascii_byte ( b' ' ) ;
175- formatter. print_ascii_byte ( b'{' ) ;
176-
177- let source_text = ctx. source_text ( ) ;
178-
179- for x in & case. consequent {
180- if matches ! (
181- x,
182- Statement :: ExpressionStatement ( _)
183- | Statement :: BreakStatement ( _)
184- ) {
185- // indent the statement in the case consequent, if needed
186- if let Some ( indent_str) =
187- get_preceding_indent_str ( source_text, x. span ( ) )
188- {
189- formatter. print_ascii_byte ( b'\n' ) ;
190- formatter. print_str ( indent_str) ;
191- }
192- }
193-
194- formatter. print_str ( x. span ( ) . source_text ( source_text) ) ;
195- }
196-
197- // indent the closing case bracket, if needed
198- if let Some ( case_indent_str) =
199- get_preceding_indent_str ( source_text, case. span ( ) )
200- {
201- formatter. print_ascii_byte ( b'\n' ) ;
202- formatter. print_str ( case_indent_str) ;
203- }
204-
205- formatter. print_ascii_byte ( b'}' ) ;
206-
207- formatter. into_source_text ( )
208- } ;
209-
210- fixer. replace ( case. span , modified_code)
116+ fixer. replace (
117+ block_stmt. span ,
118+ fixer. source_range ( block_stmt. span . shrink ( 1 ) ) ,
119+ )
211120 } ,
212121 ) ;
213-
214- // After first incorrect consequent we have to break to not repeat the work
215- break ;
122+ continue ;
216123 }
124+ false
217125 }
126+ _ => true ,
127+ } ;
128+
129+ if self . always_braces && missing_braces {
130+ let colon = u32:: try_from ( ctx. source_range ( case. span ) . find ( ':' ) . unwrap ( ) ) . unwrap ( ) ;
131+ let span = Span :: new ( case. span . start , case. span . start + colon + 1 ) ;
132+ ctx. diagnostic_with_fix (
133+ switch_case_braces_diagnostic_missing_braces ( span) ,
134+ |fixer| {
135+ let fixer = fixer. for_multifix ( ) ;
136+ let mut fix = fixer. new_fix_with_capacity ( 2 ) ;
137+
138+ fix. push ( fixer. insert_text_after_range ( span, " {" ) ) ;
139+
140+ // Indent the closing case bracket, if needed
141+ let code = match get_preceding_indent_str ( fixer. source_text ( ) , case. span ) {
142+ Some ( indent) => {
143+ let mut code = String :: with_capacity ( 2 + indent. len ( ) ) ;
144+ code. push ( '\n' ) ;
145+ code. push_str ( indent) ;
146+ code. push ( '}' ) ;
147+ code
148+ }
149+ None => String :: from ( '}' ) ,
150+ } ;
151+
152+ fix. push ( fixer. insert_text_after_range ( case. span , code) ) ;
153+ fix. with_message ( "Add Braces for case clause." )
154+ } ,
155+ ) ;
218156 }
219157 }
220158 }
@@ -243,6 +181,7 @@ fn test() {
243181 "switch(foo) { case 1: { doSomething(); } break; /* <-- This should be between braces */ }" ,
244182 "switch(foo) { default: label: {} }" ,
245183 "switch(something) { case 1: case 2: { console.log('something'); break; } case 3: console.log('something else'); }" ,
184+ "switch(foo){ case 1: {}; break; }" ,
246185 ] ;
247186
248187 let fix = vec ! [
@@ -253,15 +192,15 @@ fn test() {
253192 ) ,
254193 (
255194 "switch(something) { case 1: {} case 2: console.log('something'); break;}" ,
256- "switch(something) { case 1: case 2: {console.log('something');break;}}" ,
195+ "switch(something) { case 1: case 2: { console.log('something'); break;}}" ,
257196 None ,
258197 ) ,
259198 (
260199 "switch(foo) { default: doSomething(); }" ,
261- "switch(foo) { default: {doSomething();} }" ,
200+ "switch(foo) { default: { doSomething();} }" ,
262201 None ,
263202 ) ,
264- ( "switch(s){case'':/]/}" , "switch(s){case '': {/]/}}" , None ) ,
203+ ( "switch(s){case'':/]/}" , "switch(s){case'': {/]/}}" , None ) ,
265204 (
266205 "switch(foo) { default: {doSomething();} }" ,
267206 "switch(foo) { default: doSomething(); }" ,
@@ -275,7 +214,7 @@ fn test() {
275214 let gamma = 0
276215
277216 switch (alpha) {
278- case 1:
217+ case 1:
279218 beta = 'one'
280219 gamma = 1
281220 break
@@ -296,6 +235,7 @@ fn test() {
296235 " ,
297236 None ,
298237 ) ,
238+ ( "switch(foo){ case 1: {}; break; }" , "switch(foo){ case 1: { {}; break;} }" , None ) ,
299239 ] ;
300240
301241 Tester :: new ( SwitchCaseBraces :: NAME , SwitchCaseBraces :: PLUGIN , pass, fail)
0 commit comments