Skip to content

Commit 525137e

Browse files
committed
fix(linter): add missing options to no-inner-declarations (#12661)
fixes #11691
1 parent e741634 commit 525137e

File tree

2 files changed

+189
-83
lines changed

2 files changed

+189
-83
lines changed

crates/oxc_linter/src/rules/eslint/no_inner_declarations.rs

Lines changed: 135 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ fn no_inner_declarations_diagnostic(decl_type: &str, body: &str, span: Span) ->
1414
#[derive(Debug, Default, Clone)]
1515
pub 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+
2838
declare_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)

crates/oxc_linter/src/snapshots/eslint_no_inner_declarations.snap

Lines changed: 54 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@ source: crates/oxc_linter/src/tester.rs
3838

3939
⚠ eslint(no-inner-declarations): Variable or `function` declarations are not allowed in nested blocks
4040
╭─[no_inner_declarations.tsx:1:10]
41-
1 │ if (foo) function f(){ if(bar) var a; }
41+
1 │ if (foo) function f(){ if(bar) var a; }
4242
· ────────
4343
╰────
4444
help: Move function declaration to program root
4545

4646
⚠ eslint(no-inner-declarations): Variable or `function` declarations are not allowed in nested blocks
4747
╭─[no_inner_declarations.tsx:1:32]
48-
1 │ if (foo) function f(){ if(bar) var a; }
48+
1 │ if (foo) function f(){ if(bar) var a; }
4949
· ───
5050
╰────
5151
help: Move variable declaration to function body root
@@ -79,9 +79,9 @@ source: crates/oxc_linter/src/tester.rs
7979
help: Move variable declaration to function body root
8080

8181
⚠ eslint(no-inner-declarations): Variable or `function` declarations are not allowed in nested blocks
82-
╭─[no_inner_declarations.tsx:1:11]
83-
1 │ if (foo){ var a; }
84-
· ───
82+
╭─[no_inner_declarations.tsx:1:12]
83+
1 │ if (foo) { var a; }
84+
· ───
8585
╰────
8686
help: Move variable declaration to program root
8787

@@ -136,17 +136,17 @@ source: crates/oxc_linter/src/tester.rs
136136

137137
⚠ eslint(no-inner-declarations): Variable or `function` declarations are not allowed in nested blocks
138138
╭─[no_inner_declarations.tsx:1:32]
139-
1 │ class C { static { if (test) { function foo() {} } } }
140-
· ────────
139+
1 │ class C { static { if (test) { var foo; } } }
140+
· ───
141141
╰────
142-
help: Move function declaration to class static block body root
142+
help: Move variable declaration to class static block body root
143143

144144
eslint(no-inner-declarations): Variable or `function` declarations are not allowed in nested blocks
145145
╭─[no_inner_declarations.tsx:1:32]
146-
1 │ class C { static { if (test) { var foo; } } }
147-
· ───
146+
1 │ class C { static { if (test) { function foo() {} } } }
147+
· ────────
148148
╰────
149-
help: Move variable declaration to class static block body root
149+
help: Move function declaration to class static block body root
150150

151151
⚠ eslint(no-inner-declarations): Variable or `function` declarations are not allowed in nested blocks
152152
╭─[no_inner_declarations.tsx:1:51]
@@ -156,64 +156,71 @@ source: crates/oxc_linter/src/tester.rs
156156
help: Move variable declaration to class static block body root
157157

158158
eslint(no-inner-declarations): Variable or `function` declarations are not allowed in nested blocks
159-
╭─[no_inner_declarations.tsx:1:23]
160-
1 │ for (const x in {}) { var y = 5; }
161-
· ───
159+
╭─[no_inner_declarations.tsx:1:13]
160+
1 │ if (test) { function doSomething() { } }
161+
· ────────
162162
╰────
163-
help: Move variable declaration to program root
163+
help: Move function declaration to program root
164164

165165
⚠ eslint(no-inner-declarations): Variable or `function` declarations are not allowed in nested blocks
166-
╭─[no_inner_declarations.tsx:1:23]
167-
1for (const x of []) { var y = 5; }
168-
· ───
166+
╭─[no_inner_declarations.tsx:1:13]
167+
1 │ if (test) { function doSomething() { } }
168+
· ────────
169169
╰────
170-
help: Move variable declaration to program root
170+
help: Move function declaration to program root
171171

172172
⚠ eslint(no-inner-declarations): Variable or `function` declarations are not allowed in nested blocks
173-
╭─[no_inner_declarations.tsx:1:34]
174-
1for (const x = 1; a < 10; a++) { var y = 5; }
175-
· ───
173+
╭─[no_inner_declarations.tsx:2:17]
174+
1 │ 'use strict'
175+
2 │ if (test) { function doSomething() { } }
176+
· ────────
176177
╰────
177-
help: Move variable declaration to program root
178+
help: Move function declaration to program root
178179

179180
⚠ eslint(no-inner-declarations): Variable or `function` declarations are not allowed in nested blocks
180-
╭─[no_inner_declarations.tsx:1:21]
181-
1for (const x in {}) var y = 5;
182-
· ───
181+
╭─[no_inner_declarations.tsx:2:17]
182+
1 │ 'use strict'
183+
2 │ if (test) { function doSomething() { } }
184+
· ────────
183185
╰────
184-
help: Move variable declaration to program root
186+
help: Move function declaration to program root
185187

186188
⚠ eslint(no-inner-declarations): Variable or `function` declarations are not allowed in nested blocks
187-
╭─[no_inner_declarations.tsx:1:21]
188-
1for (const x of []) var y = 5;
189-
· ───
189+
╭─[no_inner_declarations.tsx:2:17]
190+
1 │ 'use strict'
191+
2 │ if (test) { function doSomething() { } }
192+
· ────────
190193
╰────
191-
help: Move variable declaration to program root
194+
help: Move function declaration to program root
192195

193196
⚠ eslint(no-inner-declarations): Variable or `function` declarations are not allowed in nested blocks
194-
╭─[no_inner_declarations.tsx:1:32]
195-
1for (const x = 1; a < 10; a++) var y = 5;
196-
· ───
197+
╭─[no_inner_declarations.tsx:2:7]
198+
1 │ function foo() {'use strict'
199+
2 │ { function bar() { } } }
200+
· ────────
197201
╰────
198-
help: Move variable declaration to program root
202+
help: Move function declaration to function body root
199203

200204
⚠ eslint(no-inner-declarations): Variable or `function` declarations are not allowed in nested blocks
201-
╭─[no_inner_declarations.tsx:1:6]
202-
1for (var x in {}) {}
203-
· ───
205+
╭─[no_inner_declarations.tsx:2:7]
206+
1 │ function foo() {'use strict'
207+
2 │ { function bar() { } } }
208+
· ────────
204209
╰────
205-
help: Move variable declaration to program root
210+
help: Move function declaration to function body root
206211

207212
⚠ eslint(no-inner-declarations): Variable or `function` declarations are not allowed in nested blocks
208-
╭─[no_inner_declarations.tsx:1:6]
209-
1for (var x of []) {}
210-
· ───
213+
╭─[no_inner_declarations.tsx:2:10]
214+
1 │ function doSomething() { 'use strict'
215+
2do { function somethingElse() { } } while (test); }
216+
· ────────
211217
╰────
212-
help: Move variable declaration to program root
218+
help: Move function declaration to function body root
213219

214220
⚠ eslint(no-inner-declarations): Variable or `function` declarations are not allowed in nested blocks
215-
╭─[no_inner_declarations.tsx:1:6]
216-
1for (var x = 1; a < 10; a++) {}
217-
· ───
221+
╭─[no_inner_declarations.tsx:1:3]
222+
1 │ { function foo () {'use strict'
223+
· ────────
224+
2console.log('foo called'); } }
218225
╰────
219-
help: Move variable declaration to program root
226+
help: Move function declaration to program root

0 commit comments

Comments
 (0)