1- use oxc_ast:: { AstKind , ast:: Expression } ;
1+ use oxc_ast:: {
2+ AstKind ,
3+ ast:: { Argument , Expression , NewExpression } ,
4+ } ;
25use oxc_diagnostics:: OxcDiagnostic ;
36use oxc_macros:: declare_oxc_lint;
47use oxc_span:: Span ;
58
6- use crate :: { AstNode , context:: LintContext , rule:: Rule } ;
9+ use crate :: {
10+ AstNode ,
11+ context:: LintContext ,
12+ fixer:: { RuleFix , RuleFixer } ,
13+ rule:: Rule ,
14+ } ;
715
816fn no_new_wrappers_diagnostic ( builtin_name : & str , new_span : Span ) -> OxcDiagnostic {
917 OxcDiagnostic :: warn ( format ! ( "Do not use `{builtin_name}` as a constructor" ) )
1018 . with_help ( "Remove the `new` operator." )
1119 . with_label ( new_span)
1220}
1321
22+ fn not_a_constructor_diagnostic ( builtin_name : & str , new_span : Span ) -> OxcDiagnostic {
23+ OxcDiagnostic :: warn ( format ! ( "`{builtin_name}` is not a constructor" ) )
24+ . with_help ( "Remove the `new` operator." )
25+ . with_label ( new_span)
26+ }
27+
1428#[ derive( Debug , Default , Clone ) ]
1529pub struct NoNewWrappers ;
1630
@@ -36,6 +50,7 @@ declare_oxc_lint!(
3650 /// var stringObject = new String('Hello world');
3751 /// var numberObject = new Number(33);
3852 /// var booleanObject = new Boolean(false);
53+ /// var symbolObject = new Symbol('foo'); // symbol is not a constructor
3954 /// ```
4055 ///
4156 /// Examples of **correct** code for this rule:
@@ -44,11 +59,12 @@ declare_oxc_lint!(
4459 /// var stringObject2 = String(value);
4560 /// var numberObject = Number(value);
4661 /// var booleanObject = Boolean(value);
62+ /// var symbolObject = Symbol('foo');
4763 /// ```
4864 NoNewWrappers ,
4965 eslint,
5066 pedantic,
51- pending
67+ fix ,
5268) ;
5369
5470impl Rule for NoNewWrappers {
@@ -59,14 +75,51 @@ impl Rule for NoNewWrappers {
5975 let Expression :: Identifier ( ident) = & expr. callee else {
6076 return ;
6177 } ;
62- if ( ident. name == "String" || ident. name == "Number" || ident. name == "Boolean" )
63- && ctx. is_reference_to_global_variable ( ident)
64- {
65- ctx. diagnostic ( no_new_wrappers_diagnostic ( ident. name . as_str ( ) , expr. span ) ) ;
78+ if !ctx. is_reference_to_global_variable ( ident) {
79+ return ;
80+ }
81+ let name = ident. name . as_str ( ) ;
82+ let span = if expr. span . size ( ) > 24 {
83+ Span :: new ( expr. span . start , ident. span . end )
84+ } else {
85+ expr. span
86+ } ;
87+ match name {
88+ "String" | "Number" | "Boolean" => {
89+ ctx. diagnostic_with_fix ( no_new_wrappers_diagnostic ( name, span) , |fixer| {
90+ remove_new_operator ( fixer, expr, name)
91+ } ) ;
92+ }
93+ "Symbol" => {
94+ ctx. diagnostic_with_fix ( not_a_constructor_diagnostic ( name, span) , |fixer| {
95+ remove_new_operator ( fixer, expr, name)
96+ } ) ;
97+ }
98+ _ => { }
6699 }
67100 }
68101}
69102
103+ fn remove_new_operator < ' a > (
104+ fixer : RuleFixer < ' _ , ' a > ,
105+ expr : & NewExpression < ' a > ,
106+ name : & ' a str ,
107+ ) -> RuleFix < ' a > {
108+ debug_assert ! ( expr. callee. is_identifier_reference( ) ) ;
109+ let remove_new_fix = fixer. delete_range ( Span :: sized ( expr. span . start , 4 /* "new " */ ) ) ;
110+
111+ let Some ( arg) = expr. arguments . first ( ) . and_then ( Argument :: as_expression) else {
112+ return remove_new_fix;
113+ } ;
114+
115+ match ( name, arg. get_inner_expression ( ) ) {
116+ ( "Boolean" , Expression :: BooleanLiteral ( lit) ) => fixer. replace_with ( expr, lit. as_ref ( ) ) ,
117+ ( "String" , Expression :: StringLiteral ( lit) ) => fixer. replace_with ( expr, lit. as_ref ( ) ) ,
118+ ( "Number" , Expression :: NumericLiteral ( lit) ) => fixer. replace_with ( expr, lit. as_ref ( ) ) ,
119+ _ => remove_new_fix,
120+ }
121+ }
122+
70123#[ test]
71124fn test ( ) {
72125 use crate :: tester:: Tester ;
@@ -107,7 +160,29 @@ fn test() {
107160 const b = new String('foo');
108161 }
109162 " ,
163+ "
164+ var a = new String('wow look at me im a really long string, ' +
165+ 'it sure would be annoying if this whole thing ' +
166+ 'was underlined instead of just the constructor');
167+ " ,
168+ ] ;
169+ let fix = vec ! [
170+ ( "var a = new Number(foo);" , "var a = Number(foo);" ) ,
171+ ( "var a = new Number(1 + 1);" , "var a = Number(1 + 1);" ) ,
172+ ( "var a = new String(foo);" , "var a = String(foo);" ) ,
173+ ( "var a = new Boolean(foo);" , "var a = Boolean(foo);" ) ,
174+ ( "var a = new Boolean(!!x);" , "var a = Boolean(!!x);" ) ,
175+ ( "var a = new Symbol('foo');" , "var a = Symbol('foo');" ) ,
176+ // literals dont need to be wrapped
177+ ( "var a = new Boolean(false);" , "var a = false;" ) ,
178+ ( "var a = new Boolean(true);" , "var a = true;" ) ,
179+ ( "var a = new String('hello');" , "var a = 'hello';" ) ,
180+ ( "var a = new String((((('hello')))));" , "var a = 'hello';" ) ,
181+ ( "var a = new Number(10);" , "var a = 10;" ) ,
182+ ( "var a = new Number(10 as number);" , "var a = 10;" ) ,
110183 ] ;
111184
112- Tester :: new ( NoNewWrappers :: NAME , NoNewWrappers :: PLUGIN , pass, fail) . test_and_snapshot ( ) ;
185+ Tester :: new ( NoNewWrappers :: NAME , NoNewWrappers :: PLUGIN , pass, fail)
186+ . expect_fix ( fix)
187+ . test_and_snapshot ( ) ;
113188}
0 commit comments