1- use oxc_ast:: { AstKind , ast:: Expression } ;
1+ use oxc_ast:: {
2+ AstKind ,
3+ ast:: { Argument , Expression } ,
4+ } ;
25use oxc_diagnostics:: OxcDiagnostic ;
36use oxc_macros:: declare_oxc_lint;
4- use oxc_span:: Span ;
7+ use oxc_semantic:: IsGlobalReference ;
8+ use oxc_span:: { GetSpan , Span } ;
59
610use crate :: { AstNode , context:: LintContext , rule:: Rule } ;
711
@@ -37,7 +41,7 @@ declare_oxc_lint!(
3741 NoNewBuffer ,
3842 unicorn,
3943 pedantic,
40- pending
44+ suggestion
4145) ;
4246
4347impl Rule for NoNewBuffer {
@@ -49,10 +53,54 @@ impl Rule for NoNewBuffer {
4953 let Expression :: Identifier ( ident) = & new_expr. callee . without_parentheses ( ) else {
5054 return ;
5155 } ;
52- if ident. name != "Buffer" {
56+ if ident. name != "Buffer" || !ident . is_global_reference ( ctx . scoping ( ) ) {
5357 return ;
5458 }
55- ctx. diagnostic ( no_new_buffer_diagnostic ( ident. span ) ) ;
59+
60+ // Determine which method to use based on argument type
61+ let method = determine_buffer_method ( new_expr) ;
62+ let expr_span = new_expr. span ;
63+
64+ ctx. diagnostic_with_suggestion ( no_new_buffer_diagnostic ( ident. span ) , |fixer| {
65+ let Some ( method) = method else {
66+ return fixer. noop ( ) ;
67+ } ;
68+
69+ // Build arguments string
70+ let args_text = new_expr
71+ . arguments
72+ . iter ( )
73+ . map ( |arg| ctx. source_range ( arg. span ( ) ) )
74+ . collect :: < Vec < _ > > ( )
75+ . join ( ", " ) ;
76+
77+ let replacement = format ! ( "Buffer.{method}({args_text})" ) ;
78+ fixer. replace ( expr_span, replacement)
79+ } ) ;
80+ }
81+ }
82+
83+ /// Determines which Buffer method to use based on the first argument.
84+ /// Returns `Some("alloc")` for numeric arguments, `Some("from")` for array/string arguments,
85+ /// or `None` if the type can't be determined (unsafe to fix).
86+ fn determine_buffer_method ( new_expr : & oxc_ast:: ast:: NewExpression ) -> Option < & ' static str > {
87+ // Handle spread arguments - unsafe to fix
88+ if new_expr. arguments . iter ( ) . any ( Argument :: is_spread) {
89+ return None ;
90+ }
91+
92+ let first_arg = new_expr. arguments . first ( ) ?. as_expression ( ) ?;
93+ let first_arg = first_arg. without_parentheses ( ) ;
94+
95+ match first_arg {
96+ // Numeric literals → Buffer.alloc
97+ Expression :: NumericLiteral ( _) => Some ( "alloc" ) ,
98+ // String/template literals → Buffer.from
99+ Expression :: StringLiteral ( _)
100+ | Expression :: TemplateLiteral ( _)
101+ | Expression :: ArrayExpression ( _) => Some ( "from" ) ,
102+ // For other expressions, we can't safely determine the type
103+ _ => None ,
56104 }
57105}
58106
@@ -67,6 +115,7 @@ fn test() {
67115 r"const buffer = Buffer.from('7468697320697320612074c3a97374', 'hex')" ,
68116 r"const buffer = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])" ,
69117 r"const buffer = Buffer.alloc(10)" ,
118+ r"const Buffer = function () {}; new Buffer(10);" ,
70119 ] ;
71120
72121 let fail = vec ! [
@@ -86,5 +135,26 @@ fn test() {
86135 r"new Buffer(input, encoding);" ,
87136 ] ;
88137
89- Tester :: new ( NoNewBuffer :: NAME , NoNewBuffer :: PLUGIN , pass, fail) . test_and_snapshot ( ) ;
138+ let fix = vec ! [
139+ // Numeric argument → Buffer.alloc
140+ ( r"const buffer = new Buffer(10);" , r"const buffer = Buffer.alloc(10);" ) ,
141+ // String argument → Buffer.from
142+ ( r#"const buffer = new Buffer("string");"# , r#"const buffer = Buffer.from("string");"# ) ,
143+ (
144+ r#"const buffer = new Buffer("7468697320697320612074c3a97374", "hex")"# ,
145+ r#"const buffer = Buffer.from("7468697320697320612074c3a97374", "hex")"# ,
146+ ) ,
147+ // Array argument → Buffer.from
148+ (
149+ r"const buffer = new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])" ,
150+ r"const buffer = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])" ,
151+ ) ,
152+ ( r"const buffer = new Buffer([0x62, bar])" , r"const buffer = Buffer.from([0x62, bar])" ) ,
153+ // Template literal → Buffer.from
154+ ( r"const buffer = new Buffer(`${unknown}`)" , r"const buffer = Buffer.from(`${unknown}`)" ) ,
155+ ] ;
156+
157+ Tester :: new ( NoNewBuffer :: NAME , NoNewBuffer :: PLUGIN , pass, fail)
158+ . expect_fix ( fix)
159+ . test_and_snapshot ( ) ;
90160}
0 commit comments