warn on assignment to const (#4960)

* warn on assignment to const

* fix formatting and switch to error

* check most local scopes first

* fix logic and add more tests

* more formatting

* Fix broken test

* use find_owner instead

Co-authored-by: tanhauhau <lhtan93@gmail.com>
pull/7942/head
Billy Levin 2 years ago committed by GitHub
parent ab1285a4f8
commit 88ed9931f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -792,6 +792,42 @@ export default class Component {
scope = map.get(node);
}
let deep = false;
let names: string[] | undefined;
if (node.type === 'AssignmentExpression') {
deep = node.left.type === 'MemberExpression';
names = deep
? [get_object(node.left).name]
: extract_names(node.left);
} else if (node.type === 'UpdateExpression') {
deep = node.argument.type === 'MemberExpression';
const { name } = get_object(node.argument);
names = [name];
}
if (names) {
names.forEach(name => {
let current_scope = scope;
let declaration;
while (current_scope) {
if (current_scope.declarations.has(name)) {
declaration = current_scope.declarations.get(name);
break;
}
current_scope = current_scope.parent;
}
if (declaration && declaration.kind === 'const' && !deep) {
component.error(node as any, {
code: 'assignment-to-const',
message: 'You are assigning to a const'
});
}
});
}
if (node.type === 'ImportDeclaration') {
component.extract_imports(node);
// TODO: to use actual remove

@ -128,6 +128,7 @@ export default class Expression {
deep = node.left.type === 'MemberExpression';
names = extract_names(deep ? get_object(node.left) : node.left);
} else if (node.type === 'UpdateExpression') {
deep = node.argument.type === 'MemberExpression';
names = extract_names(get_object(node.argument));
}
}
@ -149,7 +150,26 @@ export default class Expression {
component.add_reference(node, name);
const variable = component.var_lookup.get(name);
if (variable) variable[deep ? 'mutated' : 'reassigned'] = true;
if (variable) {
variable[deep ? 'mutated' : 'reassigned'] = true;
}
const declaration: any = scope.find_owner(name)?.declarations.get(name);
if (declaration) {
if (declaration.kind === 'const' && !deep) {
component.error(node, {
code: 'assignment-to-const',
message: 'You are assigning to a const'
});
}
} else if (variable && variable.writable === false && !deep) {
component.error(node, {
code: 'assignment-to-const',
message: 'You are assigning to a const'
});
}
}
});
}

@ -73,7 +73,7 @@ let world1 = 'world';
let world2 = 'world';
function instance($$self, $$props, $$invalidate) {
const world3 = 'world';
let world3 = 'world';
function foo() {
$$invalidate(0, world3 = 'svelte');

@ -1,7 +1,7 @@
<script>
let world1 = 'world';
let world2 = 'world';
const world3 = 'world';
let world3 = 'world';
function foo() {
world3 = 'svelte';
}

@ -0,0 +1,17 @@
[
{
"code": "assignment-to-const",
"message": "You are assigning to a const",
"start": {
"line": 13,
"column": 24,
"character": 282
},
"end": {
"line": 13,
"column": 35,
"character": 293
},
"pos": 282
}
]

@ -0,0 +1,15 @@
<script>
const immutable = 0;
const obj1 = { prop: true };
const obj2 = { prop: 0 }
</script>
<!-- should not error -->
<button on:click={() => obj1.prop = false}>click</button>
<button on:click={() => obj2.prop++}>click</button>
<!-- should error -->
<button on:click={() => immutable++}>click</button>

@ -0,0 +1,17 @@
[
{
"code": "assignment-to-const",
"message": "You are assigning to a const",
"start": {
"line": 14,
"column": 3,
"character": 172
},
"end": {
"line": 14,
"column": 10,
"character": 179
},
"pos": 172
}
]

@ -0,0 +1,20 @@
<script>
const foo = 'hello';
function shouldNotError() {
let foo = 0;
function inner() {
foo = 1;
}
}
function shouldError() {
function inner() {
foo = 1;
}
}
</script>
<button on:click={shouldNotError}>click</button>
<button on:click={shouldError}>click</button>

@ -0,0 +1,17 @@
[
{
"code": "assignment-to-const",
"message": "You are assigning to a const",
"start": {
"line": 17,
"column": 2,
"character": 189
},
"end": {
"line": 17,
"column": 9,
"character": 196
},
"pos": 189
}
]

@ -0,0 +1,21 @@
<script>
const foo = 'hello';
</script>
<button on:click={() => {
let foo = 0;
function inner() {
foo = 1;
}
}}>
click
</button>
<button on:click={() => {
function inner() {
foo = 1;
}
}}>
click
</button>

@ -0,0 +1,17 @@
[
{
"code": "assignment-to-const",
"message": "You are assigning to a const",
"start": {
"line": 16,
"column": 2,
"character": 225
},
"end": {
"line": 16,
"column": 18,
"character": 241
},
"pos": 225
}
]

@ -0,0 +1,24 @@
<script>
const immutable = false;
const obj1 = { prop: true };
const obj2 = { prop: 0 };
function shouldNotError() {
obj1.prop = false;
}
function shouldNotError2() {
obj2.prop++;
}
function shouldError() {
immutable = true
}
</script>
<button on:click={shouldNotError}>click</button>
<button on:click={shouldNotError2}>click</button>
<button on:click={shouldError}>click</button>
Loading…
Cancel
Save