Skip to content

Commit 6c2b41c

Browse files
DonIsaaccamc314
andauthored
fix(linter/consistent-function-scoping): allow functions in TS modules/namespaces (#11681)
## What This PR Does Fixes a false positive in `unicorn/consistent-function-scoping` on non-exported functions in TypeScript `module` and `namespace` nodes. TypeScript modules and namespaces are valid ways to encapsulate code. ```ts export namespace Foo { // public-facing API export function doThing() { let x = privateUtility(); return x + 2; } // private function only available to functions within this namespace. // this used to be reported. function privateUtility() { return 1; } } ``` --------- Co-authored-by: Cameron Clark <cameron.clark@hey.com>
1 parent 2ca1c70 commit 6c2b41c

File tree

2 files changed

+64
-4
lines changed

2 files changed

+64
-4
lines changed

crates/oxc_linter/src/rules/unicorn/consistent_function_scoping.rs

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,10 @@ impl Rule for ConsistentFunctionScoping {
161161
// The bar function scope id is 1. In order to ignore this rule,
162162
// its parent's scope id (in this case `foo`'s scope id is 0 and is equal to root scope id)
163163
// should be considered.
164-
if parent_scope_id == ctx.scoping().root_scope_id() {
164+
//
165+
// We also allow functions declared in TS module/namespace blocks.
166+
let flags = ctx.scoping().scope_flags(parent_scope_id);
167+
if flags.intersects(ScopeFlags::Top | ScopeFlags::TsModuleBlock) {
165168
return;
166169
}
167170
}
@@ -195,9 +198,10 @@ impl Rule for ConsistentFunctionScoping {
195198
_ => return,
196199
};
197200

198-
// if the function is declared at the root scope, we don't need to check anything
199-
if ctx.scoping().symbol_scope_id(function_declaration_symbol_id)
200-
== ctx.scoping().root_scope_id()
201+
// if the function is declared at the root scope or in a TS
202+
// module/namespace block, we don't need to check anything
203+
let scope = ctx.scoping().symbol_scope_id(function_declaration_symbol_id);
204+
if ctx.scoping().scope_flags(scope).intersects(ScopeFlags::Top | ScopeFlags::TsModuleBlock)
201205
{
202206
return;
203207
}
@@ -584,6 +588,51 @@ fn test() {
584588
None,
585589
),
586590
("if(f) function f(){}", None),
591+
(
592+
"
593+
export namespace Foo {
594+
export function somePublicFn() {
595+
return somePrivateFn();
596+
}
597+
function somePrivateFn() {
598+
return 'private';
599+
}
600+
}
601+
",
602+
None,
603+
),
604+
(
605+
"
606+
export namespace Foo {
607+
export function somePublicFn() {
608+
return private1() + private2();
609+
}
610+
const private1 = function private1() {
611+
return 'private1';
612+
}
613+
const private2 = () => {
614+
return 'private2';
615+
}
616+
}
617+
",
618+
None,
619+
),
620+
(
621+
"
622+
declare namespace Foo {
623+
function foo(): void;
624+
}
625+
",
626+
None,
627+
),
628+
(
629+
"
630+
declare module 'some-package' {
631+
function foo(): void;
632+
}
633+
",
634+
None,
635+
),
587636
];
588637

589638
let fail = vec![
@@ -824,6 +873,10 @@ fn test() {
824873
("function foo() { const bar = async () => {} }", None),
825874
("function doFoo() { const doBar = function(bar) { return bar; }; }", None),
826875
("function outer() { const inner = function inner() {}; }", None),
876+
(
877+
"export namespace Foo { export function outer() { const inner = function inner() {}; } }",
878+
None,
879+
),
827880
];
828881

829882
Tester::new(ConsistentFunctionScoping::NAME, ConsistentFunctionScoping::PLUGIN, pass, fail)

crates/oxc_linter/src/snapshots/unicorn_consistent_function_scoping.snap

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,3 +410,10 @@ source: crates/oxc_linter/src/tester.rs
410410
· ─────
411411
╰────
412412
help: Move this function to the outer scope.
413+
414+
⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope.
415+
╭─[consistent_function_scoping.tsx:1:73]
416+
1 │ export namespace Foo { export function outer() { const inner = function inner() {}; } }
417+
· ─────
418+
╰────
419+
help: Move this function to the outer scope.

0 commit comments

Comments
 (0)