Skip to content

Commit

Permalink
fix(scope-manager): fix assertion assignments not being marked as wri…
Browse files Browse the repository at this point in the history
…te references (#2809)

Fixes #2804
  • Loading branch information
bradzacher committed Nov 24, 2020
1 parent 14bdc2e commit fa68492
Show file tree
Hide file tree
Showing 9 changed files with 253 additions and 6 deletions.
24 changes: 24 additions & 0 deletions packages/eslint-plugin/tests/eslint-rules/prefer-const.test.ts
@@ -0,0 +1,24 @@
import rule from 'eslint/lib/rules/prefer-const';
import { RuleTester } from '../RuleTester';

const ruleTester = new RuleTester({
parser: '@typescript-eslint/parser',
});

ruleTester.run('prefer-const', rule, {
valid: [
`
let x: number | undefined = 1;
x! += 1;
`,
`
let x: number | undefined = 1;
(<number>x) += 1;
`,
`
let x: number | undefined = 1;
(x as number) += 1;
`,
],
invalid: [],
});
21 changes: 20 additions & 1 deletion packages/eslint-plugin/typings/eslint-rules.d.ts
Expand Up @@ -797,7 +797,7 @@ declare module 'eslint/lib/rules/space-infix-ops' {
'missingSpace',
[
{
int32Hint: boolean;
int32Hint?: boolean;
},
],
{
Expand All @@ -812,6 +812,25 @@ declare module 'eslint/lib/rules/space-infix-ops' {
export = rule;
}

declare module 'eslint/lib/rules/prefer-const' {
import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils';

const rule: TSESLint.RuleModule<
'useConst',
[
{
destructuring?: 'any' | 'all';
ignoreReadBeforeAssign?: boolean;
},
],
{
'Program:exit'(node: TSESTree.Program): void;
VariableDeclaration(node: TSESTree.VariableDeclaration): void;
}
>;
export = rule;
}

declare module 'eslint/lib/rules/utils/ast-utils' {
import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils';

Expand Down
22 changes: 17 additions & 5 deletions packages/scope-manager/src/referencer/Referencer.ts
Expand Up @@ -386,10 +386,22 @@ class Referencer extends Visitor {
}

protected AssignmentExpression(node: TSESTree.AssignmentExpression): void {
if (PatternVisitor.isPattern(node.left)) {
let left = node.left;
switch (left.type) {
case AST_NODE_TYPES.TSAsExpression:
case AST_NODE_TYPES.TSTypeAssertion:
// explicitly visit the type annotation
this.visit(left.typeAnnotation);
// intentional fallthrough
case AST_NODE_TYPES.TSNonNullExpression:
// unwrap the expression
left = left.expression;
}

if (PatternVisitor.isPattern(left)) {
if (node.operator === '=') {
this.visitPattern(
node.left,
left,
(pattern, info) => {
const maybeImplicitGlobal = !this.currentScope().isStrict
? {
Expand All @@ -413,15 +425,15 @@ class Referencer extends Visitor {
},
{ processRightHandNodes: true },
);
} else if (node.left.type === AST_NODE_TYPES.Identifier) {
} else if (left.type === AST_NODE_TYPES.Identifier) {
this.currentScope().referenceValue(
node.left,
left,
ReferenceFlag.ReadWrite,
node.right,
);
}
} else {
this.visit(node.left);
this.visit(left);
}
this.visit(node.right);
}
Expand Down
@@ -0,0 +1,2 @@
let x: number | undefined = 1;
(<number>x) += 1;
@@ -0,0 +1,62 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`type-assertion assignment angle-bracket-assignment 1`] = `
ScopeManager {
variables: Array [
ImplicitGlobalConstTypeVariable,
Variable$2 {
defs: Array [
VariableDefinition$1 {
name: Identifier<"x">,
node: VariableDeclarator$1,
},
],
name: "x",
references: Array [
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: Literal$3,
},
],
isValueVariable: true,
isTypeVariable: false,
},
],
scopes: Array [
GlobalScope$1 {
block: Program$4,
isStrict: false,
references: Array [
Reference$1,
Reference$2,
],
set: Map {
"const" => ImplicitGlobalConstTypeVariable,
"x" => Variable$2,
},
type: "global",
upper: null,
variables: Array [
ImplicitGlobalConstTypeVariable,
Variable$2,
],
},
],
}
`;
@@ -0,0 +1,2 @@
let x: number | undefined = 1;
(x as number) += 1;
@@ -0,0 +1,62 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`type-assertion assignment as-assignment 1`] = `
ScopeManager {
variables: Array [
ImplicitGlobalConstTypeVariable,
Variable$2 {
defs: Array [
VariableDefinition$1 {
name: Identifier<"x">,
node: VariableDeclarator$1,
},
],
name: "x",
references: Array [
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: Literal$3,
},
],
isValueVariable: true,
isTypeVariable: false,
},
],
scopes: Array [
GlobalScope$1 {
block: Program$4,
isStrict: false,
references: Array [
Reference$1,
Reference$2,
],
set: Map {
"const" => ImplicitGlobalConstTypeVariable,
"x" => Variable$2,
},
type: "global",
upper: null,
variables: Array [
ImplicitGlobalConstTypeVariable,
Variable$2,
],
},
],
}
`;
@@ -0,0 +1,2 @@
let x: number | undefined = 1;
x! += 1;
@@ -0,0 +1,62 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`type-assertion assignment non-null-assignment 1`] = `
ScopeManager {
variables: Array [
ImplicitGlobalConstTypeVariable,
Variable$2 {
defs: Array [
VariableDefinition$1 {
name: Identifier<"x">,
node: VariableDeclarator$1,
},
],
name: "x",
references: Array [
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: Literal$3,
},
],
isValueVariable: true,
isTypeVariable: false,
},
],
scopes: Array [
GlobalScope$1 {
block: Program$4,
isStrict: false,
references: Array [
Reference$1,
Reference$2,
],
set: Map {
"const" => ImplicitGlobalConstTypeVariable,
"x" => Variable$2,
},
type: "global",
upper: null,
variables: Array [
ImplicitGlobalConstTypeVariable,
Variable$2,
],
},
],
}
`;

0 comments on commit fa68492

Please sign in to comment.