From 9d8b4c479c98623e4198aa07639321929a8a876f Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Sat, 5 Sep 2020 14:58:13 -0700 Subject: [PATCH] fix(scope-manager): support rest function type parameters (#2491) Fixes #2449 --- .../tests/rules/no-unused-vars.test.ts | 8 ++ .../src/referencer/TypeVisitor.ts | 12 ++- .../function/params/array-pattern.ts | 1 + .../function/params/array-pattern.ts.shot | 96 +++++++++++++++++++ .../function/params/object-pattern.ts | 1 + .../function/params/object-pattern.ts.shot | 96 +++++++++++++++++++ .../function/params/rest-element.ts | 1 + .../function/params/rest-element.ts.shot | 96 +++++++++++++++++++ 8 files changed, 310 insertions(+), 1 deletion(-) create mode 100644 packages/scope-manager/tests/fixtures/type-declaration/function/params/array-pattern.ts create mode 100644 packages/scope-manager/tests/fixtures/type-declaration/function/params/array-pattern.ts.shot create mode 100644 packages/scope-manager/tests/fixtures/type-declaration/function/params/object-pattern.ts create mode 100644 packages/scope-manager/tests/fixtures/type-declaration/function/params/object-pattern.ts.shot create mode 100644 packages/scope-manager/tests/fixtures/type-declaration/function/params/rest-element.ts create mode 100644 packages/scope-manager/tests/fixtures/type-declaration/function/params/rest-element.ts.shot diff --git a/packages/eslint-plugin/tests/rules/no-unused-vars.test.ts b/packages/eslint-plugin/tests/rules/no-unused-vars.test.ts index 323d85b94a6..5165ecda28b 100644 --- a/packages/eslint-plugin/tests/rules/no-unused-vars.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unused-vars.test.ts @@ -767,6 +767,14 @@ export abstract class Foo { protected abstract readonly type: FooType; } `, + // https://github.com/typescript-eslint/typescript-eslint/issues/2449 + ` +export type F = (...a: A) => unknown; + `, + ` +import { Foo } from './bar'; +export type F = (...a: Foo) => unknown; + `, ], invalid: [ diff --git a/packages/scope-manager/src/referencer/TypeVisitor.ts b/packages/scope-manager/src/referencer/TypeVisitor.ts index 49f0ac12743..a43d0948659 100644 --- a/packages/scope-manager/src/referencer/TypeVisitor.ts +++ b/packages/scope-manager/src/referencer/TypeVisitor.ts @@ -33,6 +33,7 @@ class TypeVisitor extends Visitor { this.visit(node.typeParameters); for (const param of node.params) { + let didVisitAnnotation = false; this.visitPattern(param, (pattern, info) => { // a parameter name creates a value type variable which can be referenced later via typeof arg this.#referencer @@ -41,8 +42,17 @@ class TypeVisitor extends Visitor { pattern, new ParameterDefinition(pattern, node, info.rest), ); - this.visit(pattern.typeAnnotation); + + if (pattern.typeAnnotation) { + this.visit(pattern.typeAnnotation); + didVisitAnnotation = true; + } }); + + // there are a few special cases where the type annotation is owned by the parameter, not the pattern + if (!didVisitAnnotation && 'typeAnnotation' in param) { + this.visit(param.typeAnnotation); + } } this.visit(node.returnType); diff --git a/packages/scope-manager/tests/fixtures/type-declaration/function/params/array-pattern.ts b/packages/scope-manager/tests/fixtures/type-declaration/function/params/array-pattern.ts new file mode 100644 index 00000000000..27da21ea7ad --- /dev/null +++ b/packages/scope-manager/tests/fixtures/type-declaration/function/params/array-pattern.ts @@ -0,0 +1 @@ +type Fn = ([a]: A) => unknown; diff --git a/packages/scope-manager/tests/fixtures/type-declaration/function/params/array-pattern.ts.shot b/packages/scope-manager/tests/fixtures/type-declaration/function/params/array-pattern.ts.shot new file mode 100644 index 00000000000..409f49b66b9 --- /dev/null +++ b/packages/scope-manager/tests/fixtures/type-declaration/function/params/array-pattern.ts.shot @@ -0,0 +1,96 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`type-declaration function params array-pattern 1`] = ` +ScopeManager { + variables: Array [ + Variable$1 { + defs: Array [ + TypeDefinition$1 { + name: Identifier<"Fn">, + node: TSTypeAliasDeclaration$1, + }, + ], + name: "Fn", + references: Array [], + isValueVariable: false, + isTypeVariable: true, + }, + Variable$2 { + defs: Array [ + TypeDefinition$2 { + name: Identifier<"A">, + node: TSTypeParameter$2, + }, + ], + name: "A", + references: Array [ + Reference$1 { + identifier: Identifier<"A">, + isRead: true, + isTypeReference: true, + isValueReference: false, + isWrite: false, + resolved: Variable$2, + }, + ], + isValueVariable: false, + isTypeVariable: true, + }, + Variable$3 { + defs: Array [ + ParameterDefinition$3 { + name: Identifier<"a">, + node: TSFunctionType$3, + }, + ], + name: "a", + references: Array [], + isValueVariable: true, + isTypeVariable: false, + }, + ], + scopes: Array [ + GlobalScope$1 { + block: Program$4, + isStrict: false, + references: Array [], + set: Map { + "Fn" => Variable$1, + }, + type: "global", + upper: null, + variables: Array [ + Variable$1, + ], + }, + TypeScope$2 { + block: TSTypeAliasDeclaration$1, + isStrict: true, + references: Array [], + set: Map { + "A" => Variable$2, + }, + type: "type", + upper: GlobalScope$1, + variables: Array [ + Variable$2, + ], + }, + FunctionTypeScope$3 { + block: TSFunctionType$3, + isStrict: true, + references: Array [ + Reference$1, + ], + set: Map { + "a" => Variable$3, + }, + type: "functionType", + upper: TypeScope$2, + variables: Array [ + Variable$3, + ], + }, + ], +} +`; diff --git a/packages/scope-manager/tests/fixtures/type-declaration/function/params/object-pattern.ts b/packages/scope-manager/tests/fixtures/type-declaration/function/params/object-pattern.ts new file mode 100644 index 00000000000..f262383a8d8 --- /dev/null +++ b/packages/scope-manager/tests/fixtures/type-declaration/function/params/object-pattern.ts @@ -0,0 +1 @@ +type Fn = ({ a }: A) => unknown; diff --git a/packages/scope-manager/tests/fixtures/type-declaration/function/params/object-pattern.ts.shot b/packages/scope-manager/tests/fixtures/type-declaration/function/params/object-pattern.ts.shot new file mode 100644 index 00000000000..44d53bab98e --- /dev/null +++ b/packages/scope-manager/tests/fixtures/type-declaration/function/params/object-pattern.ts.shot @@ -0,0 +1,96 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`type-declaration function params object-pattern 1`] = ` +ScopeManager { + variables: Array [ + Variable$1 { + defs: Array [ + TypeDefinition$1 { + name: Identifier<"Fn">, + node: TSTypeAliasDeclaration$1, + }, + ], + name: "Fn", + references: Array [], + isValueVariable: false, + isTypeVariable: true, + }, + Variable$2 { + defs: Array [ + TypeDefinition$2 { + name: Identifier<"A">, + node: TSTypeParameter$2, + }, + ], + name: "A", + references: Array [ + Reference$1 { + identifier: Identifier<"A">, + isRead: true, + isTypeReference: true, + isValueReference: false, + isWrite: false, + resolved: Variable$2, + }, + ], + isValueVariable: false, + isTypeVariable: true, + }, + Variable$3 { + defs: Array [ + ParameterDefinition$3 { + name: Identifier<"a">, + node: TSFunctionType$3, + }, + ], + name: "a", + references: Array [], + isValueVariable: true, + isTypeVariable: false, + }, + ], + scopes: Array [ + GlobalScope$1 { + block: Program$4, + isStrict: false, + references: Array [], + set: Map { + "Fn" => Variable$1, + }, + type: "global", + upper: null, + variables: Array [ + Variable$1, + ], + }, + TypeScope$2 { + block: TSTypeAliasDeclaration$1, + isStrict: true, + references: Array [], + set: Map { + "A" => Variable$2, + }, + type: "type", + upper: GlobalScope$1, + variables: Array [ + Variable$2, + ], + }, + FunctionTypeScope$3 { + block: TSFunctionType$3, + isStrict: true, + references: Array [ + Reference$1, + ], + set: Map { + "a" => Variable$3, + }, + type: "functionType", + upper: TypeScope$2, + variables: Array [ + Variable$3, + ], + }, + ], +} +`; diff --git a/packages/scope-manager/tests/fixtures/type-declaration/function/params/rest-element.ts b/packages/scope-manager/tests/fixtures/type-declaration/function/params/rest-element.ts new file mode 100644 index 00000000000..da11f73c40c --- /dev/null +++ b/packages/scope-manager/tests/fixtures/type-declaration/function/params/rest-element.ts @@ -0,0 +1 @@ +type Fn = (...a: A) => unknown; diff --git a/packages/scope-manager/tests/fixtures/type-declaration/function/params/rest-element.ts.shot b/packages/scope-manager/tests/fixtures/type-declaration/function/params/rest-element.ts.shot new file mode 100644 index 00000000000..6b69e7667d7 --- /dev/null +++ b/packages/scope-manager/tests/fixtures/type-declaration/function/params/rest-element.ts.shot @@ -0,0 +1,96 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`type-declaration function params rest-element 1`] = ` +ScopeManager { + variables: Array [ + Variable$1 { + defs: Array [ + TypeDefinition$1 { + name: Identifier<"Fn">, + node: TSTypeAliasDeclaration$1, + }, + ], + name: "Fn", + references: Array [], + isValueVariable: false, + isTypeVariable: true, + }, + Variable$2 { + defs: Array [ + TypeDefinition$2 { + name: Identifier<"A">, + node: TSTypeParameter$2, + }, + ], + name: "A", + references: Array [ + Reference$1 { + identifier: Identifier<"A">, + isRead: true, + isTypeReference: true, + isValueReference: false, + isWrite: false, + resolved: Variable$2, + }, + ], + isValueVariable: false, + isTypeVariable: true, + }, + Variable$3 { + defs: Array [ + ParameterDefinition$3 { + name: Identifier<"a">, + node: TSFunctionType$3, + }, + ], + name: "a", + references: Array [], + isValueVariable: true, + isTypeVariable: false, + }, + ], + scopes: Array [ + GlobalScope$1 { + block: Program$4, + isStrict: false, + references: Array [], + set: Map { + "Fn" => Variable$1, + }, + type: "global", + upper: null, + variables: Array [ + Variable$1, + ], + }, + TypeScope$2 { + block: TSTypeAliasDeclaration$1, + isStrict: true, + references: Array [], + set: Map { + "A" => Variable$2, + }, + type: "type", + upper: GlobalScope$1, + variables: Array [ + Variable$2, + ], + }, + FunctionTypeScope$3 { + block: TSFunctionType$3, + isStrict: true, + references: Array [ + Reference$1, + ], + set: Map { + "a" => Variable$3, + }, + type: "functionType", + upper: TypeScope$2, + variables: Array [ + Variable$3, + ], + }, + ], +} +`;