@@ -14,6 +14,7 @@ fn no_inner_declarations_diagnostic(decl_type: &str, body: &str, span: Span) ->
1414#[ derive( Debug , Default , Clone ) ]
1515pub struct NoInnerDeclarations {
1616 config : NoInnerDeclarationsConfig ,
17+ block_scoped_functions : Option < BlockScopedFunctions > ,
1718}
1819
1920#[ derive( Debug , Default , Clone , Copy , Eq , PartialEq ) ]
@@ -25,6 +26,15 @@ enum NoInnerDeclarationsConfig {
2526 Both ,
2627}
2728
29+ #[ derive( Debug , Default , Clone , Copy , Eq , PartialEq ) ]
30+ enum BlockScopedFunctions {
31+ /// Allow function declarations in nested blocks in strict mode (ES6+ behavior)
32+ #[ default]
33+ Allow ,
34+ /// Disallow function declarations in nested blocks regardless of strict mode
35+ Disallow ,
36+ }
37+
2838declare_oxc_lint ! (
2939 /// ### What it does
3040 ///
@@ -66,7 +76,22 @@ impl Rule for NoInnerDeclarations {
6676 _ => NoInnerDeclarationsConfig :: Both ,
6777 } ,
6878 ) ;
69- Self { config }
79+
80+ let block_scoped_functions = if value. is_array ( ) && !value. is_null ( ) {
81+ value
82+ . get ( 1 )
83+ . and_then ( |v| v. get ( "blockScopedFunctions" ) )
84+ . and_then ( serde_json:: Value :: as_str)
85+ . map ( |value| match value {
86+ "disallow" => BlockScopedFunctions :: Disallow ,
87+ _ => BlockScopedFunctions :: Allow ,
88+ } )
89+ . or ( Some ( BlockScopedFunctions :: Allow ) )
90+ } else {
91+ None
92+ } ;
93+
94+ Self { config, block_scoped_functions }
7095 }
7196
7297 fn run < ' a > ( & self , node : & AstNode < ' a > , ctx : & LintContext < ' a > ) {
@@ -80,6 +105,20 @@ impl Rule for NoInnerDeclarations {
80105 if !func. is_function_declaration ( ) {
81106 return ;
82107 }
108+
109+ if self . config == NoInnerDeclarationsConfig :: Functions {
110+ if let Some ( block_scoped_functions) = self . block_scoped_functions {
111+ if block_scoped_functions == BlockScopedFunctions :: Allow {
112+ let is_module = ctx. source_type ( ) . is_module ( ) ;
113+ let scope_id = node. scope_id ( ) ;
114+ let is_strict = ctx. scoping ( ) . scope_flags ( scope_id) . is_strict_mode ( ) ;
115+
116+ if is_module || is_strict {
117+ return ;
118+ }
119+ }
120+ }
121+ }
83122 }
84123 _ => return ,
85124 }
@@ -141,46 +180,72 @@ fn test() {
141180 ( "if (test) { var fn = function expr() { }; }" , None ) ,
142181 ( "function decl() { var fn = function expr() { }; }" , None ) ,
143182 ( "function decl(arg) { var fn; if (arg) { fn = function() { }; } }" , None ) ,
144- ( "var x = {doSomething() {function doSomethingElse() {}}}" , None ) ,
145- ( "function decl(arg) { var fn; if (arg) { fn = function expr() { }; } }" , None ) ,
183+ ( "var x = {doSomething() {function doSomethingElse() {}}}" , None ) , // { "ecmaVersion": 6 },
184+ ( "function decl(arg) { var fn; if (arg) { fn = function expr() { }; } }" , None ) , // { "ecmaVersion": 6 },
146185 ( "function decl(arg) { var fn; if (arg) { fn = function expr() { }; } }" , None ) ,
147186 ( "if (test) { var foo; }" , None ) ,
148- ( "if (test) { let x = 1; }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
149- ( "if (test) { const x = 1; }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
187+ ( "if (test) { let x = 1; }" , Some ( serde_json:: json!( [ "both" ] ) ) ) , // { "ecmaVersion": 6 },
188+ ( "if (test) { const x = 1; }" , Some ( serde_json:: json!( [ "both" ] ) ) ) , // { "ecmaVersion": 6 },
189+ ( "if (test) { using x = 1; }" , Some ( serde_json:: json!( [ "both" ] ) ) ) , // { "ecmaVersion": 2026, "sourceType": "module", },
190+ ( "if (test) { await using x = 1; }" , Some ( serde_json:: json!( [ "both" ] ) ) ) , // { "ecmaVersion": 2026, "sourceType": "module", },
150191 ( "function doSomething() { while (test) { var foo; } }" , None ) ,
151192 ( "var foo;" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
152193 ( "var foo = 42;" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
153194 ( "function doSomething() { var foo; }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
154195 ( "(function() { var foo; }());" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
155- ( "foo(() => { function bar() { } });" , None ) ,
156- ( "var fn = () => {var foo;}" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
157- ( "var x = {doSomething() {var foo;}}" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
158- ( "export var foo;" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
159- ( "export function bar() {}" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
160- ( "export default function baz() {}" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
161- ( "exports.foo = () => {}" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
196+ ( "foo(() => { function bar() { } });" , None ) , // { "ecmaVersion": 6 },
197+ ( "var fn = () => {var foo;}" , Some ( serde_json:: json!( [ "both" ] ) ) ) , // { "ecmaVersion": 6 },
198+ ( "var x = {doSomething() {var foo;}}" , Some ( serde_json:: json!( [ "both" ] ) ) ) , // { "ecmaVersion": 6 },
199+ ( "export var foo;" , Some ( serde_json:: json!( [ "both" ] ) ) ) , // { "sourceType": "module", "ecmaVersion": 6 },
200+ ( "export function bar() {}" , Some ( serde_json:: json!( [ "both" ] ) ) ) , // { "sourceType": "module", "ecmaVersion": 6 },
201+ ( "export default function baz() {}" , Some ( serde_json:: json!( [ "both" ] ) ) ) , // { "sourceType": "module", "ecmaVersion": 6 },
202+ ( "exports.foo = () => {}" , Some ( serde_json:: json!( [ "both" ] ) ) ) , // { "ecmaVersion": 6 },
162203 ( "exports.foo = function(){}" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
163204 ( "module.exports = function foo(){}" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
164- ( "class C { method() { function foo() {} } }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
165- ( "class C { method() { var x; } }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
166- ( "class C { static { function foo() {} } }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
167- ( "class C { static { var x; } }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
168- ( "for (const x in {}) { let y = 5; }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
169- ( "for (const x of []) { let y = 5; }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
170- ( "for (const x = 1; a < 10; a++) { let y = 5; }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
205+ ( "class C { method() { function foo() {} } }" , Some ( serde_json:: json!( [ "both" ] ) ) ) , // { "ecmaVersion": 2022 },
206+ ( "class C { method() { var x; } }" , Some ( serde_json:: json!( [ "both" ] ) ) ) , // { "ecmaVersion": 2022 },
207+ ( "class C { static { function foo() {} } }" , Some ( serde_json:: json!( [ "both" ] ) ) ) , // { "ecmaVersion": 2022 },
208+ ( "class C { static { var x; } }" , Some ( serde_json:: json!( [ "both" ] ) ) ) , // { "ecmaVersion": 2022 },
209+ (
210+ "'use strict'
211+ if (test) { function doSomething() { } }" ,
212+ Some ( serde_json:: json!( [ "functions" , { "blockScopedFunctions" : "allow" } ] ) ) ,
213+ ) , // { "ecmaVersion": 2022 },
214+ (
215+ "'use strict'
216+ if (test) { function doSomething() { } }" ,
217+ Some ( serde_json:: json!( [ "functions" ] ) ) ,
218+ ) , // { "ecmaVersion": 2022 },
219+ (
220+ "function foo() {'use strict'
221+ if (test) { function doSomething() { } } }" ,
222+ Some ( serde_json:: json!( [ "functions" , { "blockScopedFunctions" : "allow" } ] ) ) ,
223+ ) , // { "ecmaVersion": 6 },
224+ (
225+ "function foo() { { function bar() { } } }" ,
226+ Some ( serde_json:: json!( [ "functions" , { "blockScopedFunctions" : "allow" } ] ) ) ,
227+ ) , // { "ecmaVersion": 2022, "sourceType": "module" },
228+ (
229+ "class C { method() { if(test) { function somethingElse() { } } } }" ,
230+ Some ( serde_json:: json!( [ "functions" , { "blockScopedFunctions" : "allow" } ] ) ) ,
231+ ) , // { "ecmaVersion": 2022 },
232+ (
233+ "const C = class { method() { if(test) { function somethingElse() { } } } }" ,
234+ Some ( serde_json:: json!( [ "functions" , { "blockScopedFunctions" : "allow" } ] ) ) ,
235+ ) , // { "ecmaVersion": 2022 }
171236 ] ;
172237
173238 let fail = vec ! [
174239 ( "if (test) { function doSomething() { } }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
175240 ( "if (foo) var a; " , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
176241 ( "if (foo) /* some comments */ var a; " , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
177242 ( "if (foo){ function f(){ if(bar){ var a; } } }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
178- ( "if (foo) function f(){ if(bar) var a; } " , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
243+ ( "if (foo) function f(){ if(bar) var a; }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
179244 ( "if (foo) { var fn = function(){} } " , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
180245 ( "if (foo) function f(){} " , None ) ,
181246 ( "function bar() { if (foo) function f(){}; }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
182247 ( "function bar() { if (foo) var a; }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
183- ( "if (foo){ var a; }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
248+ ( "if (foo) { var a; }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
184249 ( "function doSomething() { do { function somethingElse() { } } while (test); }" , None ) ,
185250 ( "(function() { if (test) { function doSomething() { } } }());" , None ) ,
186251 ( "while (test) { var foo; }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
@@ -192,26 +257,60 @@ fn test() {
192257 (
193258 "const doSomething = () => { if (test) { var foo = 42; } }" ,
194259 Some ( serde_json:: json!( [ "both" ] ) ) ,
195- ) ,
196- ( "class C { method() { if(test) { var foo; } } }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
260+ ) , // { "ecmaVersion": 6 },
261+ ( "class C { method() { if(test) { var foo; } } }" , Some ( serde_json:: json!( [ "both" ] ) ) ) , // { "ecmaVersion": 6 },
262+ ( "class C { static { if (test) { var foo; } } }" , Some ( serde_json:: json!( [ "both" ] ) ) ) , // { "ecmaVersion": 2022 },
197263 (
198264 "class C { static { if (test) { function foo() {} } } }" ,
199- Some ( serde_json:: json!( [ "both" ] ) ) ,
200- ) ,
201- ( "class C { static { if (test) { var foo; } } }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
265+ Some ( serde_json:: json!( [ "both" , { "blockScopedFunctions" : "disallow" } ] ) ) ,
266+ ) , // { "ecmaVersion": 2022 },
202267 (
203268 "class C { static { if (test) { if (anotherTest) { var foo; } } } }" ,
204269 Some ( serde_json:: json!( [ "both" ] ) ) ,
205- ) ,
206- ( "for (const x in {}) { var y = 5; }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
207- ( "for (const x of []) { var y = 5; }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
208- ( "for (const x = 1; a < 10; a++) { var y = 5; }" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
209- ( "for (const x in {}) var y = 5;" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
210- ( "for (const x of []) var y = 5;" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
211- ( "for (const x = 1; a < 10; a++) var y = 5;" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
212- ( "for (var x in {}) {}" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
213- ( "for (var x of []) {}" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
214- ( "for (var x = 1; a < 10; a++) {}" , Some ( serde_json:: json!( [ "both" ] ) ) ) ,
270+ ) , // { "ecmaVersion": 2022 },
271+ (
272+ "if (test) { function doSomething() { } }" ,
273+ Some ( serde_json:: json!( [ "both" , { "blockScopedFunctions" : "allow" } ] ) ) ,
274+ ) , // { "ecmaVersion": 5 },
275+ (
276+ "if (test) { function doSomething() { } }" ,
277+ Some ( serde_json:: json!( [ "both" , { "blockScopedFunctions" : "disallow" } ] ) ) ,
278+ ) , // { "ecmaVersion": 2022 },
279+ (
280+ "'use strict'
281+ if (test) { function doSomething() { } }" ,
282+ Some ( serde_json:: json!( [ "both" , { "blockScopedFunctions" : "disallow" } ] ) ) ,
283+ ) , // { "ecmaVersion": 2022 },
284+ (
285+ "'use strict'
286+ if (test) { function doSomething() { } }" ,
287+ Some ( serde_json:: json!( [ "both" , { "blockScopedFunctions" : "disallow" } ] ) ) ,
288+ ) , // { "ecmaVersion": 5 },
289+ (
290+ "'use strict'
291+ if (test) { function doSomething() { } }" ,
292+ Some ( serde_json:: json!( [ "both" , { "blockScopedFunctions" : "allow" } ] ) ) ,
293+ ) , // { "ecmaVersion": 5 },
294+ (
295+ "function foo() {'use strict'
296+ { function bar() { } } }" ,
297+ Some ( serde_json:: json!( [ "both" , { "blockScopedFunctions" : "disallow" } ] ) ) ,
298+ ) , // { "ecmaVersion": 2022 },
299+ (
300+ "function foo() {'use strict'
301+ { function bar() { } } }" ,
302+ Some ( serde_json:: json!( [ "both" , { "blockScopedFunctions" : "disallow" } ] ) ) ,
303+ ) , // { "ecmaVersion": 5 },
304+ (
305+ "function doSomething() { 'use strict'
306+ do { function somethingElse() { } } while (test); }" ,
307+ Some ( serde_json:: json!( [ "both" , { "blockScopedFunctions" : "disallow" } ] ) ) ,
308+ ) , // { "ecmaVersion": 5 },
309+ (
310+ "{ function foo () {'use strict'
311+ console.log('foo called'); } }" ,
312+ Some ( serde_json:: json!( [ "both" ] ) ) ,
313+ ) , // { "ecmaVersion": 2022 }
215314 ] ;
216315
217316 Tester :: new ( NoInnerDeclarations :: NAME , NoInnerDeclarations :: PLUGIN , pass, fail)
0 commit comments