From 7a71c5864239538da1be54ffc031d31b3c3097dc Mon Sep 17 00:00:00 2001 From: Jan Ochwat Date: Sun, 3 Nov 2024 22:48:49 +0100 Subject: [PATCH 1/2] fix(scope-manager): fix asserted increments not being marked as write references --- .../tests/eslint-rules/prefer-const.test.ts | 12 ++++ .../src/referencer/Referencer.ts | 15 ++++- .../increment/angle-bracket-increment.ts | 2 + .../increment/angle-bracket-increment.ts.shot | 62 +++++++++++++++++++ .../type-assertion/increment/as-increment.ts | 2 + .../increment/as-increment.ts.shot | 62 +++++++++++++++++++ .../increment/non-null-increment.ts | 2 + .../increment/non-null-increment.ts.shot | 62 +++++++++++++++++++ 8 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 packages/scope-manager/tests/fixtures/type-assertion/increment/angle-bracket-increment.ts create mode 100644 packages/scope-manager/tests/fixtures/type-assertion/increment/angle-bracket-increment.ts.shot create mode 100644 packages/scope-manager/tests/fixtures/type-assertion/increment/as-increment.ts create mode 100644 packages/scope-manager/tests/fixtures/type-assertion/increment/as-increment.ts.shot create mode 100644 packages/scope-manager/tests/fixtures/type-assertion/increment/non-null-increment.ts create mode 100644 packages/scope-manager/tests/fixtures/type-assertion/increment/non-null-increment.ts.shot diff --git a/packages/eslint-plugin/tests/eslint-rules/prefer-const.test.ts b/packages/eslint-plugin/tests/eslint-rules/prefer-const.test.ts index 9bd707aedf20..574c0707c064 100644 --- a/packages/eslint-plugin/tests/eslint-rules/prefer-const.test.ts +++ b/packages/eslint-plugin/tests/eslint-rules/prefer-const.test.ts @@ -21,5 +21,17 @@ let x: number | undefined = 1; let x: number | undefined = 1; (x as number) += 1; `, + ` +let x: number | undefined = 1; +x!++; + `, + ` +let x: number | undefined = 1; +(x)++; + `, + ` +let x: number | undefined = 1; +(x as number)++; + `, ], }); diff --git a/packages/scope-manager/src/referencer/Referencer.ts b/packages/scope-manager/src/referencer/Referencer.ts index 1cf0868c7452..7947588dcfdf 100644 --- a/packages/scope-manager/src/referencer/Referencer.ts +++ b/packages/scope-manager/src/referencer/Referencer.ts @@ -752,8 +752,19 @@ class Referencer extends Visitor { } protected UpdateExpression(node: TSESTree.UpdateExpression): void { - if (PatternVisitor.isPattern(node.argument)) { - this.visitPattern(node.argument, pattern => { + let argument = node.argument; + switch (argument.type) { + case AST_NODE_TYPES.TSAsExpression: + case AST_NODE_TYPES.TSTypeAssertion: + // explicitly visit the type annotation + this.visitType(argument.typeAnnotation); + // intentional fallthrough + case AST_NODE_TYPES.TSNonNullExpression: + // unwrap the expression + argument = argument.expression; + } + if (PatternVisitor.isPattern(argument)) { + this.visitPattern(argument, pattern => { this.currentScope().referenceValue( pattern, ReferenceFlag.ReadWrite, diff --git a/packages/scope-manager/tests/fixtures/type-assertion/increment/angle-bracket-increment.ts b/packages/scope-manager/tests/fixtures/type-assertion/increment/angle-bracket-increment.ts new file mode 100644 index 000000000000..a1227c1c1aad --- /dev/null +++ b/packages/scope-manager/tests/fixtures/type-assertion/increment/angle-bracket-increment.ts @@ -0,0 +1,2 @@ +let x: number | undefined = 1; +(x)++; diff --git a/packages/scope-manager/tests/fixtures/type-assertion/increment/angle-bracket-increment.ts.shot b/packages/scope-manager/tests/fixtures/type-assertion/increment/angle-bracket-increment.ts.shot new file mode 100644 index 000000000000..c85d3ddb0422 --- /dev/null +++ b/packages/scope-manager/tests/fixtures/type-assertion/increment/angle-bracket-increment.ts.shot @@ -0,0 +1,62 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`type-assertion increment angle-bracket-increment 1`] = ` +ScopeManager { + variables: [ + ImplicitGlobalConstTypeVariable, + Variable$2 { + defs: [ + VariableDefinition$1 { + name: Identifier<"x">, + node: VariableDeclarator$1, + }, + ], + name: "x", + references: [ + Reference$1 { + identifier: Identifier<"x">, + init: true, + isRead: false, + isTypeReference: false, + isValueReference: true, + isWrite: true, + resolved: Variable$2, + writeExpr: Literal$2, + }, + Reference$2 { + identifier: Identifier<"x">, + init: false, + isRead: true, + isTypeReference: false, + isValueReference: true, + isWrite: true, + resolved: Variable$2, + writeExpr: null, + }, + ], + isValueVariable: true, + isTypeVariable: false, + }, + ], + scopes: [ + GlobalScope$1 { + block: Program$3, + isStrict: false, + references: [ + Reference$1, + Reference$2, + ], + set: Map { + "const" => ImplicitGlobalConstTypeVariable, + "x" => Variable$2, + }, + type: "global", + upper: null, + variables: [ + ImplicitGlobalConstTypeVariable, + Variable$2, + ], + }, + ], +} +`; diff --git a/packages/scope-manager/tests/fixtures/type-assertion/increment/as-increment.ts b/packages/scope-manager/tests/fixtures/type-assertion/increment/as-increment.ts new file mode 100644 index 000000000000..4f19cee47715 --- /dev/null +++ b/packages/scope-manager/tests/fixtures/type-assertion/increment/as-increment.ts @@ -0,0 +1,2 @@ +let x: number | undefined = 1; +(x as number)++; diff --git a/packages/scope-manager/tests/fixtures/type-assertion/increment/as-increment.ts.shot b/packages/scope-manager/tests/fixtures/type-assertion/increment/as-increment.ts.shot new file mode 100644 index 000000000000..f3437982f7d6 --- /dev/null +++ b/packages/scope-manager/tests/fixtures/type-assertion/increment/as-increment.ts.shot @@ -0,0 +1,62 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`type-assertion increment as-increment 1`] = ` +ScopeManager { + variables: [ + ImplicitGlobalConstTypeVariable, + Variable$2 { + defs: [ + VariableDefinition$1 { + name: Identifier<"x">, + node: VariableDeclarator$1, + }, + ], + name: "x", + references: [ + Reference$1 { + identifier: Identifier<"x">, + init: true, + isRead: false, + isTypeReference: false, + isValueReference: true, + isWrite: true, + resolved: Variable$2, + writeExpr: Literal$2, + }, + Reference$2 { + identifier: Identifier<"x">, + init: false, + isRead: true, + isTypeReference: false, + isValueReference: true, + isWrite: true, + resolved: Variable$2, + writeExpr: null, + }, + ], + isValueVariable: true, + isTypeVariable: false, + }, + ], + scopes: [ + GlobalScope$1 { + block: Program$3, + isStrict: false, + references: [ + Reference$1, + Reference$2, + ], + set: Map { + "const" => ImplicitGlobalConstTypeVariable, + "x" => Variable$2, + }, + type: "global", + upper: null, + variables: [ + ImplicitGlobalConstTypeVariable, + Variable$2, + ], + }, + ], +} +`; diff --git a/packages/scope-manager/tests/fixtures/type-assertion/increment/non-null-increment.ts b/packages/scope-manager/tests/fixtures/type-assertion/increment/non-null-increment.ts new file mode 100644 index 000000000000..3b088705a342 --- /dev/null +++ b/packages/scope-manager/tests/fixtures/type-assertion/increment/non-null-increment.ts @@ -0,0 +1,2 @@ +let x: number | undefined = 1; +x!++; diff --git a/packages/scope-manager/tests/fixtures/type-assertion/increment/non-null-increment.ts.shot b/packages/scope-manager/tests/fixtures/type-assertion/increment/non-null-increment.ts.shot new file mode 100644 index 000000000000..e37ac428db14 --- /dev/null +++ b/packages/scope-manager/tests/fixtures/type-assertion/increment/non-null-increment.ts.shot @@ -0,0 +1,62 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`type-assertion increment non-null-increment 1`] = ` +ScopeManager { + variables: [ + ImplicitGlobalConstTypeVariable, + Variable$2 { + defs: [ + VariableDefinition$1 { + name: Identifier<"x">, + node: VariableDeclarator$1, + }, + ], + name: "x", + references: [ + Reference$1 { + identifier: Identifier<"x">, + init: true, + isRead: false, + isTypeReference: false, + isValueReference: true, + isWrite: true, + resolved: Variable$2, + writeExpr: Literal$2, + }, + Reference$2 { + identifier: Identifier<"x">, + init: false, + isRead: true, + isTypeReference: false, + isValueReference: true, + isWrite: true, + resolved: Variable$2, + writeExpr: null, + }, + ], + isValueVariable: true, + isTypeVariable: false, + }, + ], + scopes: [ + GlobalScope$1 { + block: Program$3, + isStrict: false, + references: [ + Reference$1, + Reference$2, + ], + set: Map { + "const" => ImplicitGlobalConstTypeVariable, + "x" => Variable$2, + }, + type: "global", + upper: null, + variables: [ + ImplicitGlobalConstTypeVariable, + Variable$2, + ], + }, + ], +} +`; From edf6a58708d700bffb2351560b9d8645f21dfb46 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sun, 10 Nov 2024 11:12:32 -0500 Subject: [PATCH 2/2] visitExpressionTarget --- .../src/referencer/Referencer.ts | 40 +++++++++---------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/packages/scope-manager/src/referencer/Referencer.ts b/packages/scope-manager/src/referencer/Referencer.ts index 7947588dcfdf..06c19b67e455 100644 --- a/packages/scope-manager/src/referencer/Referencer.ts +++ b/packages/scope-manager/src/referencer/Referencer.ts @@ -325,17 +325,7 @@ class Referencer extends Visitor { } protected AssignmentExpression(node: TSESTree.AssignmentExpression): void { - let left = node.left; - switch (left.type) { - case AST_NODE_TYPES.TSAsExpression: - case AST_NODE_TYPES.TSTypeAssertion: - // explicitly visit the type annotation - this.visitType(left.typeAnnotation); - // intentional fallthrough - case AST_NODE_TYPES.TSNonNullExpression: - // unwrap the expression - left = left.expression; - } + const left = this.visitExpressionTarget(node.left); if (PatternVisitor.isPattern(left)) { if (node.operator === '=') { @@ -752,17 +742,8 @@ class Referencer extends Visitor { } protected UpdateExpression(node: TSESTree.UpdateExpression): void { - let argument = node.argument; - switch (argument.type) { - case AST_NODE_TYPES.TSAsExpression: - case AST_NODE_TYPES.TSTypeAssertion: - // explicitly visit the type annotation - this.visitType(argument.typeAnnotation); - // intentional fallthrough - case AST_NODE_TYPES.TSNonNullExpression: - // unwrap the expression - argument = argument.expression; - } + const argument = this.visitExpressionTarget(node.argument); + if (PatternVisitor.isPattern(argument)) { this.visitPattern(argument, pattern => { this.currentScope().referenceValue( @@ -822,6 +803,21 @@ class Referencer extends Visitor { this.close(node); } + + private visitExpressionTarget(left: TSESTree.Node) { + switch (left.type) { + case AST_NODE_TYPES.TSAsExpression: + case AST_NODE_TYPES.TSTypeAssertion: + // explicitly visit the type annotation + this.visitType(left.typeAnnotation); + // intentional fallthrough + case AST_NODE_TYPES.TSNonNullExpression: + // unwrap the expression + left = left.expression; + } + + return left; + } } export { Referencer, type ReferencerOptions };