@@ -2,15 +2,17 @@ use oxc_ast::{ast::Expression, AstKind};
22use oxc_diagnostics:: OxcDiagnostic ;
33
44use oxc_macros:: declare_oxc_lint;
5- use oxc_span:: Span ;
5+ use oxc_span:: { GetSpan , Span } ;
66use oxc_syntax:: operator:: LogicalOperator ;
77
8- use crate :: { ast_util:: outermost_paren_parent, context:: LintContext , rule:: Rule , AstNode } ;
8+ use crate :: {
9+ ast_util:: outermost_paren_parent, context:: LintContext , fixer:: Fix , rule:: Rule , AstNode ,
10+ } ;
911
10- fn no_useless_fallback_in_spread_diagnostic ( span0 : Span ) -> OxcDiagnostic {
12+ fn no_useless_fallback_in_spread_diagnostic ( span : Span ) -> OxcDiagnostic {
1113 OxcDiagnostic :: warn ( "eslint-plugin-unicorn(no-useless-fallback-in-spread): Disallow useless fallback when spreading in object literals" )
1214 . with_help ( "Spreading falsy values in object literals won't add any unexpected properties, so it's unnecessary to add an empty object as fallback." )
13- . with_labels ( [ span0 . into ( ) ] )
15+ . with_labels ( [ span . into ( ) ] )
1416}
1517
1618#[ derive( Debug , Default , Clone ) ]
@@ -75,7 +77,29 @@ impl Rule for NoUselessFallbackInSpread {
7577 return ;
7678 }
7779
78- ctx. diagnostic ( no_useless_fallback_in_spread_diagnostic ( spread_element. span ) ) ;
80+ let diagnostic = no_useless_fallback_in_spread_diagnostic ( spread_element. span ) ;
81+
82+ if can_fix ( & logical_expression. left ) {
83+ ctx. diagnostic_with_fix ( diagnostic, || {
84+ let left_text = logical_expression. left . span ( ) . source_text ( ctx. source_text ( ) ) ;
85+ Fix :: new ( format ! ( "...{left_text}" ) , spread_element. span )
86+ } ) ;
87+ } else {
88+ ctx. diagnostic ( diagnostic) ;
89+ }
90+ }
91+ }
92+
93+ fn can_fix ( left : & Expression < ' _ > ) -> bool {
94+ const BANNED_IDENTIFIERS : [ & str ; 3 ] = [ "undefined" , "NaN" , "Infinity" ] ;
95+ match left. without_parenthesized ( ) {
96+ Expression :: Identifier ( ident) => !BANNED_IDENTIFIERS . contains ( & ident. name . as_str ( ) ) ,
97+ Expression :: LogicalExpression ( expr) => can_fix ( & expr. left ) ,
98+ Expression :: ObjectExpression ( _)
99+ | Expression :: CallExpression ( _)
100+ | Expression :: StaticMemberExpression ( _)
101+ | Expression :: ComputedMemberExpression ( _) => true ,
102+ _ => false ,
79103 }
80104}
81105
@@ -131,5 +155,15 @@ fn test() {
131155 r"const object = {...(document.all || {})}" ,
132156 ] ;
133157
134- Tester :: new ( NoUselessFallbackInSpread :: NAME , pass, fail) . test_and_snapshot ( ) ;
158+ let fix = vec ! [
159+ //
160+ ( r"const object = {...(foo || {})}" , r"const object = {...foo}" ) ,
161+ ( r"const object = {...(foo() || {})}" , r"const object = {...foo()}" ) ,
162+ ( r"const object = {...((foo && {}) || {})}" , "const object = {...(foo && {})}" ) ,
163+ ( r"const object = {...(0 || {})}" , r"const object = {...(0 || {})}" ) ,
164+ ( r"const object = {...(NaN || {})}" , r"const object = {...(NaN || {})}" ) ,
165+ ( r"const object = {...(Infinity || {})}" , r"const object = {...(Infinity || {})}" ) ,
166+ ] ;
167+
168+ Tester :: new ( NoUselessFallbackInSpread :: NAME , pass, fail) . expect_fix ( fix) . test_and_snapshot ( ) ;
135169}
0 commit comments