Skip to content

Commit d6c9859

Browse files
committed
unskip and fix test
1 parent c82ede5 commit d6c9859

File tree

9 files changed

+81
-31
lines changed

9 files changed

+81
-31
lines changed

documentation/docs/98-reference/.generated/compile-errors.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,12 @@ class Counter {
872872

873873
...but it can only happen once.
874874

875+
### state_field_invalid_assignment
876+
877+
```
878+
Cannot assign to a state field before its declaration
879+
```
880+
875881
### state_invalid_export
876882

877883
```

packages/svelte/messages/compile-errors/script.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,10 @@ class Counter {
236236

237237
...but it can only happen once.
238238

239+
## state_field_invalid_assignment
240+
241+
> Cannot assign to a state field before its declaration
242+
239243
## state_invalid_export
240244

241245
> Cannot export state from a module if it is reassigned. Either export a function returning the state value or only mutate the state value's properties

packages/svelte/src/compiler/errors.js

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -104,16 +104,6 @@ export function constant_binding(node, thing) {
104104
e(node, 'constant_binding', `Cannot bind to ${thing}\nhttps://svelte.dev/e/constant_binding`);
105105
}
106106

107-
/**
108-
* `%name%` has already been declared on this class
109-
* @param {null | number | NodeLike} node
110-
* @param {string} name
111-
* @returns {never}
112-
*/
113-
export function state_field_duplicate(node, name) {
114-
e(node, 'state_field_duplicate', `\`${name}\` has already been declared on this class\nhttps://svelte.dev/e/state_field_duplicate`);
115-
}
116-
117107
/**
118108
* `%name%` has already been declared
119109
* @param {null | number | NodeLike} node
@@ -471,6 +461,25 @@ export function snippet_parameter_assignment(node) {
471461
e(node, 'snippet_parameter_assignment', `Cannot reassign or bind to snippet parameter\nhttps://svelte.dev/e/snippet_parameter_assignment`);
472462
}
473463

464+
/**
465+
* `%name%` has already been declared on this class
466+
* @param {null | number | NodeLike} node
467+
* @param {string} name
468+
* @returns {never}
469+
*/
470+
export function state_field_duplicate(node, name) {
471+
e(node, 'state_field_duplicate', `\`${name}\` has already been declared on this class\nhttps://svelte.dev/e/state_field_duplicate`);
472+
}
473+
474+
/**
475+
* Cannot assign to a state field before its declaration
476+
* @param {null | number | NodeLike} node
477+
* @returns {never}
478+
*/
479+
export function state_field_invalid_assignment(node) {
480+
e(node, 'state_field_invalid_assignment', `Cannot assign to a state field before its declaration\nhttps://svelte.dev/e/state_field_invalid_assignment`);
481+
}
482+
474483
/**
475484
* Cannot export state from a module if it is reassigned. Either export a function returning the state value or only mutate the state value's properties
476485
* @param {null | number | NodeLike} node

packages/svelte/src/compiler/phases/2-analyze/visitors/AssignmentExpression.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { validate_assignment } from './shared/utils.js';
88
* @param {Context} context
99
*/
1010
export function AssignmentExpression(node, context) {
11-
validate_assignment(node, node.left, context.state);
11+
validate_assignment(node, node.left, context);
1212

1313
if (context.state.reactive_statement) {
1414
const id = node.left.type === 'MemberExpression' ? object(node.left) : node.left;

packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ export function BindDirective(node, context) {
158158
return;
159159
}
160160

161-
validate_assignment(node, node.expression, context.state);
161+
validate_assignment(node, node.expression, context);
162162

163163
const assignee = node.expression;
164164
const left = object(assignee);

packages/svelte/src/compiler/phases/2-analyze/visitors/UpdateExpression.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { validate_assignment } from './shared/utils.js';
88
* @param {Context} context
99
*/
1010
export function UpdateExpression(node, context) {
11-
validate_assignment(node, node.argument, context.state);
11+
validate_assignment(node, node.argument, context);
1212

1313
if (context.state.reactive_statement) {
1414
const id = node.argument.type === 'MemberExpression' ? object(node.argument) : node.argument;

packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,25 @@
44
/** @import { Scope } from '../../../scope' */
55
/** @import { NodeLike } from '../../../../errors.js' */
66
import * as e from '../../../../errors.js';
7-
import { extract_identifiers } from '../../../../utils/ast.js';
7+
import { extract_identifiers, get_parent } from '../../../../utils/ast.js';
88
import * as w from '../../../../warnings.js';
99
import * as b from '#compiler/builders';
1010
import { get_rune } from '../../../scope.js';
11+
import { get_name } from '../../../nodes.js';
1112

1213
/**
1314
* @param {AssignmentExpression | UpdateExpression | AST.BindDirective} node
1415
* @param {Pattern | Expression} argument
15-
* @param {AnalysisState} state
16+
* @param {Context} context
1617
*/
17-
export function validate_assignment(node, argument, state) {
18-
validate_no_const_assignment(node, argument, state.scope, node.type === 'BindDirective');
18+
export function validate_assignment(node, argument, context) {
19+
validate_no_const_assignment(node, argument, context.state.scope, node.type === 'BindDirective');
1920

2021
if (argument.type === 'Identifier') {
21-
const binding = state.scope.get(argument.name);
22+
const binding = context.state.scope.get(argument.name);
2223

23-
if (state.analysis.runes) {
24-
if (binding?.node === state.analysis.props_id) {
24+
if (context.state.analysis.runes) {
25+
if (binding?.node === context.state.analysis.props_id) {
2526
e.constant_assignment(node, '$props.id()');
2627
}
2728

@@ -34,6 +35,41 @@ export function validate_assignment(node, argument, state) {
3435
e.snippet_parameter_assignment(node);
3536
}
3637
}
38+
39+
if (argument.type === 'MemberExpression' && argument.object.type === 'ThisExpression') {
40+
const name =
41+
argument.computed && argument.property.type !== 'Literal'
42+
? null
43+
: get_name(argument.property);
44+
45+
const field =
46+
name !== null &&
47+
context.state.state_fields &&
48+
Object.hasOwn(context.state.state_fields, name) &&
49+
context.state.state_fields[name];
50+
51+
// check we're not assigning to a state field before its declaration in the constructor
52+
if (field && field.node.type === 'AssignmentExpression' && node !== field.node) {
53+
let i = context.path.length;
54+
while (i--) {
55+
const parent = context.path[i];
56+
57+
if (
58+
parent.type === 'FunctionDeclaration' ||
59+
parent.type === 'FunctionExpression' ||
60+
parent.type === 'ArrowFunctionExpression'
61+
) {
62+
const grandparent = get_parent(context.path, i - 1);
63+
64+
if (grandparent.type === 'MethodDefinition' && grandparent.kind === 'constructor') {
65+
e.state_field_invalid_assignment(node);
66+
}
67+
68+
break;
69+
}
70+
}
71+
}
72+
}
3773
}
3874

3975
/**

packages/svelte/tests/validator/samples/class-state-constructor-9/_config.js

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
[
22
{
3-
"code": "state_field_duplicate",
4-
"message": "`count` has already been declared on this class",
3+
"code": "state_field_invalid_assignment",
4+
"message": "Cannot assign to a state field before its declaration",
55
"start": {
6-
"line": 7,
7-
"column": 2
6+
"line": 4,
7+
"column": 3
88
},
99
"end": {
10-
"line": 7,
11-
"column": 24
10+
"line": 4,
11+
"column": 18
1212
}
1313
}
1414
]

0 commit comments

Comments
 (0)