function validate_mutation(node, context, expression) {
	let left = /** @type {Expression | Super} */ (
		node.type === 'AssignmentExpression' ? node.left : node.argument
	);

	if (!dev || left.type !== 'MemberExpression' || is_ignored(node, 'ownership_invalid_mutation')) {
		return expression;
	}

	const name = object(left);
	if (!name) return expression;

	const binding = context.state.scope.get(name.name);
	if (binding?.kind !== 'prop' && binding?.kind !== 'bindable_prop') return expression;

	const state = /** @type {ComponentClientTransformState} */ (context.state);
	state.analysis.needs_mutation_validation = true;

	/** @type {Array<Identifier | Literal | Expression>} */
	const path = [];

	while (left.type === 'MemberExpression') {
		if (left.property.type === 'Literal') {
			path.unshift(left.property);
		} else if (left.property.type === 'Identifier') {
			const transform = Object.hasOwn(context.state.transform, left.property.name)
				? context.state.transform[left.property.name]
				: null;
			if (left.computed) {
				path.unshift(transform?.read ? transform.read(left.property) : left.property);
			} else {
				path.unshift(b.literal(left.property.name));
			}
		} else {
			return expression;
		}

		left = left.object;
	}

	path.unshift(b.literal(name.name));

	const loc = locator(/** @type {number} */ (left.start));

	return b.call(
		'$$ownership_validator.mutation',
		b.literal(binding.prop_alias),
		b.array(path),
		expression,
		loc && b.literal(loc.line),
		loc && b.literal(loc.column)
	);
}
In dev mode validate mutations to props
@param {AssignmentExpression | UpdateExpression} node
@param {Context} context
@param {Expression} expression