@@ -165,8 +165,13 @@ declare_oxc_lint!(
165
165
conditional_fix
166
166
) ;
167
167
168
- fn no_else_return_diagnostic ( else_stmt : & Statement ) -> OxcDiagnostic {
169
- OxcDiagnostic :: warn ( "Unnecessary 'else' after 'return'." ) . with_label ( else_stmt. span ( ) )
168
+ fn no_else_return_diagnostic ( else_keyword : Span , last_return : Span ) -> OxcDiagnostic {
169
+ OxcDiagnostic :: warn ( "Unnecessary 'else' after 'return'." )
170
+ . with_labels ( [
171
+ last_return. label ( "This consequent block always returns," ) ,
172
+ else_keyword. label ( "Making this `else` block unnecessary." ) ,
173
+ ] )
174
+ . with_help ( "Remove the `else` block, moving its contents outside of the `if` statement." )
170
175
}
171
176
172
177
fn is_safe_from_name_collisions (
@@ -201,20 +206,22 @@ fn is_safe_from_name_collisions(
201
206
202
207
fn no_else_return_diagnostic_fix (
203
208
ctx : & LintContext ,
209
+ last_return_span : Span ,
204
210
else_stmt_prev : & Statement ,
205
211
else_stmt : & Statement ,
206
212
if_block_node : & AstNode ,
207
213
) {
208
- let diagnostic = no_else_return_diagnostic ( else_stmt) ;
214
+ let prev_span = else_stmt_prev. span ( ) ;
215
+ let else_content_span = else_stmt. span ( ) ;
216
+ let else_keyword_span = Span :: new ( prev_span. end , else_content_span. start ) ;
217
+ let diagnostic = no_else_return_diagnostic ( else_keyword_span, last_return_span) ;
209
218
let parent_scope_id = if_block_node. scope_id ( ) ;
210
219
211
220
if !is_safe_from_name_collisions ( ctx, else_stmt, parent_scope_id) {
212
221
ctx. diagnostic ( diagnostic) ;
213
222
return ;
214
223
}
215
224
ctx. diagnostic_with_fix ( diagnostic, |fixer| {
216
- let prev_span = else_stmt_prev. span ( ) ;
217
- let else_content_span = else_stmt. span ( ) ;
218
225
let target_span = Span :: new ( prev_span. end , else_content_span. end ) ;
219
226
220
227
// Capture the contents of the `else` statement, removing curly braces
@@ -262,35 +269,41 @@ fn left_offset_for_whitespace(ctx: &LintContext, position: u32) -> u32 {
262
269
offset as u32
263
270
}
264
271
265
- fn naive_has_return ( node : & Statement ) -> bool {
272
+ fn naive_has_return ( node : & Statement ) -> Option < Span > {
266
273
match node {
267
274
Statement :: BlockStatement ( block) => {
268
- let Some ( last_child) = block. body . last ( ) else {
269
- return false ;
270
- } ;
271
- matches ! ( last_child, Statement :: ReturnStatement ( _) )
275
+ let last_child = block. body . last ( ) ?;
276
+ if let Statement :: ReturnStatement ( r) = last_child {
277
+ Some ( r. span )
278
+ } else {
279
+ None
280
+ }
272
281
}
273
- Statement :: ReturnStatement ( _ ) => true ,
274
- _ => false ,
282
+ Statement :: ReturnStatement ( r ) => Some ( r . span ) ,
283
+ _ => None ,
275
284
}
276
285
}
277
286
278
- fn check_for_return_or_if ( node : & Statement ) -> bool {
287
+ fn check_for_return_or_if ( node : & Statement ) -> Option < Span > {
279
288
match node {
280
- Statement :: ReturnStatement ( _ ) => true ,
289
+ Statement :: ReturnStatement ( r ) => Some ( r . span ) ,
281
290
Statement :: IfStatement ( if_stmt) => {
282
- let Some ( alternate) = & if_stmt. alternate else {
283
- return false ;
284
- } ;
285
- naive_has_return ( alternate) && naive_has_return ( & if_stmt. consequent )
291
+ let alternate = if_stmt. alternate . as_ref ( ) ?;
292
+ if let ( Some ( _) , Some ( ret_span) ) =
293
+ ( naive_has_return ( alternate) , naive_has_return ( & if_stmt. consequent ) )
294
+ {
295
+ Some ( ret_span)
296
+ } else {
297
+ None
298
+ }
286
299
}
287
- _ => false ,
300
+ _ => None ,
288
301
}
289
302
}
290
303
291
- fn always_returns ( stmt : & Statement ) -> bool {
304
+ fn always_returns ( stmt : & Statement ) -> Option < Span > {
292
305
match stmt {
293
- Statement :: BlockStatement ( block) => block. body . iter ( ) . any ( check_for_return_or_if) ,
306
+ Statement :: BlockStatement ( block) => block. body . iter ( ) . find_map ( check_for_return_or_if) ,
294
307
node => check_for_return_or_if ( node) ,
295
308
}
296
309
}
@@ -303,8 +316,8 @@ fn check_if_with_else(ctx: &LintContext, node: &AstNode) {
303
316
return ;
304
317
} ;
305
318
306
- if always_returns ( & if_stmt. consequent ) {
307
- no_else_return_diagnostic_fix ( ctx, & if_stmt. consequent , alternate, node) ;
319
+ if let Some ( last_return_span ) = always_returns ( & if_stmt. consequent ) {
320
+ no_else_return_diagnostic_fix ( ctx, last_return_span , & if_stmt. consequent , alternate, node) ;
308
321
}
309
322
}
310
323
@@ -315,16 +328,18 @@ fn check_if_without_else(ctx: &LintContext, node: &AstNode) {
315
328
let mut current_node = if_stmt;
316
329
let mut last_alternate;
317
330
let mut last_alternate_prev;
331
+ let mut last_return_span;
318
332
319
333
loop {
320
334
let Some ( alternate) = & current_node. alternate else {
321
335
return ;
322
336
} ;
323
- if ! always_returns ( & current_node. consequent ) {
337
+ let Some ( ret_span ) = always_returns ( & current_node. consequent ) else {
324
338
return ;
325
- }
339
+ } ;
326
340
last_alternate_prev = & current_node. consequent ;
327
341
last_alternate = alternate;
342
+ last_return_span = ret_span;
328
343
match alternate {
329
344
Statement :: IfStatement ( if_stmt) => {
330
345
current_node = if_stmt;
@@ -333,7 +348,7 @@ fn check_if_without_else(ctx: &LintContext, node: &AstNode) {
333
348
}
334
349
}
335
350
336
- no_else_return_diagnostic_fix ( ctx, last_alternate_prev, last_alternate, node) ;
351
+ no_else_return_diagnostic_fix ( ctx, last_return_span , last_alternate_prev, last_alternate, node) ;
337
352
}
338
353
339
354
impl Rule for NoElseReturn {
0 commit comments