use existing validate_assignment logic

pull/10428/head
Rich Harris 3 years ago
parent 39d7c3e5f6
commit 5bfceb9f95

@ -189,30 +189,12 @@ function get_delegated_event(event_name, handler, context) {
return non_hoistable;
}
const is_runes = context.state.analysis.runes;
// Validate that we don't have any each bindings that are mutated.
if (is_runes && binding !== null && binding.kind === 'each') {
for (const { path } of binding.references) {
const last = path.at(-1);
// If the last reference node is an update or assigment expression to the binding
// then we know the binding was mutated directly rather than part of another node path.
if (
last != null &&
(last.type === 'AssignmentExpression' || last.type === 'UpdateExpression')
) {
error(last, 'invalid-each-mutation');
}
}
}
if (
binding !== null &&
// Bail-out if the the binding is a rest param
(binding.declaration_kind === 'rest_param' ||
// Bail-out if we reference anything from the EachBlock (for now) that mutates in non-runes mode,
(((!is_runes && binding.kind === 'each') ||
(((!context.state.analysis.runes && binding.kind === 'each') ||
// or any normal not reactive bindings that are mutated.
binding.kind === 'normal' ||
// or any reactive imports (those are rewritten) (can only happen in legacy mode)

@ -334,6 +334,9 @@ function is_tag_valid_with_parent(tag, parent_tag) {
* @type {import('zimmerframe').Visitors<import('#compiler').SvelteNode, import('./types.js').AnalysisState>}
*/
const validation = {
AssignmentExpression(node, context) {
validate_assignment(node, node.left, context.state);
},
BindDirective(node, context) {
validate_no_const_assignment(node, node.expression, context.state.scope, true);
@ -655,6 +658,9 @@ const validation = {
error(child, 'invalid-title-content');
}
},
UpdateExpression(node, context) {
validate_assignment(node, node.argument, context.state);
},
ExpressionTag(node, context) {
if (!node.parent) return;
if (context.state.parent_element) {
@ -904,24 +910,28 @@ function validate_no_const_assignment(node, argument, scope, is_binding) {
function validate_assignment(node, argument, state) {
validate_no_const_assignment(node, argument, state.scope, false);
let left = /** @type {import('estree').Expression | import('estree').Super} */ (argument);
if (left.type === 'Identifier') {
const binding = state.scope.get(left.name);
if (state.analysis.runes && argument.type === 'Identifier') {
const binding = state.scope.get(argument.name);
if (binding?.kind === 'derived') {
error(node, 'invalid-derived-assignment');
}
if (binding?.kind === 'each') {
error(node, 'invalid-each-mutation');
}
}
let object = /** @type {import('estree').Expression | import('estree').Super} */ (argument);
/** @type {import('estree').Expression | import('estree').PrivateIdentifier | null} */
let property = null;
while (left.type === 'MemberExpression') {
property = left.property;
left = left.object;
while (object.type === 'MemberExpression') {
property = object.property;
object = object.object;
}
if (left.type === 'ThisExpression' && property?.type === 'PrivateIdentifier') {
if (object.type === 'ThisExpression' && property?.type === 'PrivateIdentifier') {
if (state.private_derived_state.includes(property.name)) {
error(node, 'invalid-derived-assignment');
}
@ -929,14 +939,6 @@ function validate_assignment(node, argument, state) {
}
export const validation_runes = merge(validation, a11y_validators, {
AssignmentExpression(node, { state, path }) {
const parent = path.at(-1);
if (parent && parent.type === 'ConstTag') return;
validate_assignment(node, node.left, state);
},
UpdateExpression(node, { state }) {
validate_assignment(node, node.argument, state);
},
LabeledStatement(node, { path }) {
if (node.label.name !== '$' || path.at(-1)?.type !== 'Program') return;
error(node, 'invalid-legacy-reactive-statement');

Loading…
Cancel
Save