From 35bb8ddac1b46397f6447c1a6e1e4e1774dd7957 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Thu, 1 Sep 2022 16:41:41 +0930 Subject: [PATCH] fix(scope-manager): correct handling for class static blocks (#5580) --- .../no-unused-vars/no-unused-vars.test.ts | 21 +++ .../src/referencer/ClassVisitor.ts | 10 +- .../class/declaration/static-external-ref.ts | 7 + .../declaration/static-external-ref.ts.shot | 117 +++++++++++++++ .../declaration/static-with-constructor.ts | 10 ++ .../static-with-constructor.ts.shot | 137 ++++++++++++++++++ 6 files changed, 297 insertions(+), 5 deletions(-) create mode 100644 packages/scope-manager/tests/fixtures/class/declaration/static-external-ref.ts create mode 100644 packages/scope-manager/tests/fixtures/class/declaration/static-external-ref.ts.shot create mode 100644 packages/scope-manager/tests/fixtures/class/declaration/static-with-constructor.ts create mode 100644 packages/scope-manager/tests/fixtures/class/declaration/static-with-constructor.ts.shot diff --git a/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts b/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts index 5fac3eeed71..715d2e94385 100644 --- a/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts @@ -1018,6 +1018,27 @@ export class TestClass { `, parserOptions: withMetaParserOptions, }, + // https://github.com/typescript-eslint/typescript-eslint/issues/5577 + ` +function foo() {} + +export class Foo { + constructor() { + foo(); + } +} + `, + ` +function foo() {} + +export class Foo { + static {} + + constructor() { + foo(); + } +} + `, ], invalid: [ diff --git a/packages/scope-manager/src/referencer/ClassVisitor.ts b/packages/scope-manager/src/referencer/ClassVisitor.ts index 47e0bdef41c..84c97fc445a 100644 --- a/packages/scope-manager/src/referencer/ClassVisitor.ts +++ b/packages/scope-manager/src/referencer/ClassVisitor.ts @@ -322,10 +322,6 @@ class ClassVisitor extends Visitor { this.visitType(node); } - protected visitStaticBlock(node: TSESTree.StaticBlock): void { - this.#referencer.scopeManager.nestClassStaticBlockScope(node); - } - ///////////////////// // Visit selectors // ///////////////////// @@ -365,7 +361,11 @@ class ClassVisitor extends Visitor { } protected StaticBlock(node: TSESTree.StaticBlock): void { - this.visitStaticBlock(node); + this.#referencer.scopeManager.nestClassStaticBlockScope(node); + + node.body.forEach(b => this.visit(b)); + + this.#referencer.close(node); } } diff --git a/packages/scope-manager/tests/fixtures/class/declaration/static-external-ref.ts b/packages/scope-manager/tests/fixtures/class/declaration/static-external-ref.ts new file mode 100644 index 00000000000..d2237dc62b5 --- /dev/null +++ b/packages/scope-manager/tests/fixtures/class/declaration/static-external-ref.ts @@ -0,0 +1,7 @@ +function f() {} + +class A { + static { + f(); + } +} diff --git a/packages/scope-manager/tests/fixtures/class/declaration/static-external-ref.ts.shot b/packages/scope-manager/tests/fixtures/class/declaration/static-external-ref.ts.shot new file mode 100644 index 00000000000..0ef13dfe6d5 --- /dev/null +++ b/packages/scope-manager/tests/fixtures/class/declaration/static-external-ref.ts.shot @@ -0,0 +1,117 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`class declaration static-external-ref 1`] = ` +ScopeManager { + variables: Array [ + ImplicitGlobalConstTypeVariable, + Variable$2 { + defs: Array [ + FunctionNameDefinition$1 { + name: Identifier<"f">, + node: FunctionDeclaration$1, + }, + ], + name: "f", + references: Array [ + Reference$1 { + identifier: Identifier<"f">, + isRead: true, + isTypeReference: false, + isValueReference: true, + isWrite: false, + resolved: Variable$2, + }, + ], + isValueVariable: true, + isTypeVariable: false, + }, + Variable$3 { + defs: Array [], + name: "arguments", + references: Array [], + isValueVariable: true, + isTypeVariable: true, + }, + Variable$4 { + defs: Array [ + ClassNameDefinition$2 { + name: Identifier<"A">, + node: ClassDeclaration$2, + }, + ], + name: "A", + references: Array [], + isValueVariable: true, + isTypeVariable: true, + }, + Variable$5 { + defs: Array [ + ClassNameDefinition$3 { + name: Identifier<"A">, + node: ClassDeclaration$2, + }, + ], + name: "A", + references: Array [], + isValueVariable: true, + isTypeVariable: true, + }, + ], + scopes: Array [ + GlobalScope$1 { + block: Program$3, + isStrict: false, + references: Array [], + set: Map { + "const" => ImplicitGlobalConstTypeVariable, + "f" => Variable$2, + "A" => Variable$4, + }, + type: "global", + upper: null, + variables: Array [ + ImplicitGlobalConstTypeVariable, + Variable$2, + Variable$4, + ], + }, + FunctionScope$2 { + block: FunctionDeclaration$1, + isStrict: false, + references: Array [], + set: Map { + "arguments" => Variable$3, + }, + type: "function", + upper: GlobalScope$1, + variables: Array [ + Variable$3, + ], + }, + ClassScope$3 { + block: ClassDeclaration$2, + isStrict: true, + references: Array [], + set: Map { + "A" => Variable$5, + }, + type: "class", + upper: GlobalScope$1, + variables: Array [ + Variable$5, + ], + }, + ClassStaticBlockScope$4 { + block: StaticBlock$4, + isStrict: true, + references: Array [ + Reference$1, + ], + set: Map {}, + type: "class-static-block", + upper: ClassScope$3, + variables: Array [], + }, + ], +} +`; diff --git a/packages/scope-manager/tests/fixtures/class/declaration/static-with-constructor.ts b/packages/scope-manager/tests/fixtures/class/declaration/static-with-constructor.ts new file mode 100644 index 00000000000..ceb030c000c --- /dev/null +++ b/packages/scope-manager/tests/fixtures/class/declaration/static-with-constructor.ts @@ -0,0 +1,10 @@ +// https://github.com/typescript-eslint/typescript-eslint/issues/5577 +function f() {} + +class A { + static {} + + constructor() { + f(); + } +} diff --git a/packages/scope-manager/tests/fixtures/class/declaration/static-with-constructor.ts.shot b/packages/scope-manager/tests/fixtures/class/declaration/static-with-constructor.ts.shot new file mode 100644 index 00000000000..76d943c8df8 --- /dev/null +++ b/packages/scope-manager/tests/fixtures/class/declaration/static-with-constructor.ts.shot @@ -0,0 +1,137 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`class declaration static-with-constructor 1`] = ` +ScopeManager { + variables: Array [ + ImplicitGlobalConstTypeVariable, + Variable$2 { + defs: Array [ + FunctionNameDefinition$1 { + name: Identifier<"f">, + node: FunctionDeclaration$1, + }, + ], + name: "f", + references: Array [ + Reference$1 { + identifier: Identifier<"f">, + isRead: true, + isTypeReference: false, + isValueReference: true, + isWrite: false, + resolved: Variable$2, + }, + ], + isValueVariable: true, + isTypeVariable: false, + }, + Variable$3 { + defs: Array [], + name: "arguments", + references: Array [], + isValueVariable: true, + isTypeVariable: true, + }, + Variable$4 { + defs: Array [ + ClassNameDefinition$2 { + name: Identifier<"A">, + node: ClassDeclaration$2, + }, + ], + name: "A", + references: Array [], + isValueVariable: true, + isTypeVariable: true, + }, + Variable$5 { + defs: Array [ + ClassNameDefinition$3 { + name: Identifier<"A">, + node: ClassDeclaration$2, + }, + ], + name: "A", + references: Array [], + isValueVariable: true, + isTypeVariable: true, + }, + Variable$6 { + defs: Array [], + name: "arguments", + references: Array [], + isValueVariable: true, + isTypeVariable: true, + }, + ], + scopes: Array [ + GlobalScope$1 { + block: Program$3, + isStrict: false, + references: Array [], + set: Map { + "const" => ImplicitGlobalConstTypeVariable, + "f" => Variable$2, + "A" => Variable$4, + }, + type: "global", + upper: null, + variables: Array [ + ImplicitGlobalConstTypeVariable, + Variable$2, + Variable$4, + ], + }, + FunctionScope$2 { + block: FunctionDeclaration$1, + isStrict: false, + references: Array [], + set: Map { + "arguments" => Variable$3, + }, + type: "function", + upper: GlobalScope$1, + variables: Array [ + Variable$3, + ], + }, + ClassScope$3 { + block: ClassDeclaration$2, + isStrict: true, + references: Array [], + set: Map { + "A" => Variable$5, + }, + type: "class", + upper: GlobalScope$1, + variables: Array [ + Variable$5, + ], + }, + ClassStaticBlockScope$4 { + block: StaticBlock$4, + isStrict: true, + references: Array [], + set: Map {}, + type: "class-static-block", + upper: ClassScope$3, + variables: Array [], + }, + FunctionScope$5 { + block: FunctionExpression$5, + isStrict: true, + references: Array [ + Reference$1, + ], + set: Map { + "arguments" => Variable$6, + }, + type: "function", + upper: ClassScope$3, + variables: Array [ + Variable$6, + ], + }, + ], +} +`;