Repository: sveltejs/svelte
File: packages/svelte/src/compiler/phases/2-analyze/visitors/ClassBody.js (JavaScript)

Code before:

	context.state.analysis.classes.set(node, state_fields);

	/** @type {MethodDefinition | null} */
	let constructor = null;

	/**
	 * @param {PropertyDefinition | AssignmentExpression} node
	 * @param {Expression | PrivateIdentifier} key
	 * @param {Expression | null | undefined} value
	 */
	function handle(node, key, value) {
		const name = get_name(key);
		if (name === null) return;

		const rune = get_rune(value, context.state.scope);

		if (rune && is_state_creation_rune(rune)) {
			if (state_fields.has(name)) {
				e.state_field_duplicate(node, name);
			}

			const _key =
				(node.type === 'AssignmentExpression' || !node.static ? '' : '@') +
				(key.type === 'PrivateIdentifier' ? '#' : '') +
				name;
			const field = fields.get(_key);

			// if there's already a method or assigned field, error
			if (field && !(field.length === 1 && field[0] === 'prop')) {
				e.duplicate_class_field(node, _key);
			}

			state_fields.set(name, {
				node,
				type: rune,
				// @ts-expect-error for public state this is filled out in a moment
				key: key.type === 'PrivateIdentifier' ? key : null,
				value: /** @type {CallExpression} */ (value)
			});
		}
	}

	for (const child of node.body) {
		if (child.type === 'PropertyDefinition' && !child.computed && !child.static) {
			handle(child, child.key, child.value);
			const key = (child.key.type === 'PrivateIdentifier' ? '#' : '') + get_name(child.key);
			const field = fields.get(key);
			if (!field) {
				fields.set(key, [child.value ? 'assigned_prop' : 'prop']);
				continue;

Review comment (bug):
AssignmentExpression?
Btw, I looked and `get_name()` already prepends `#`, so no need to add it here.
But it doesn't seem to handle [dynamic fields](https://svelte.dev/playground/d875318270e444efb8a4e7b095375885?version=5.37.1#H4sIAAAAAAAAA22PzWrDQAyEX0WIHlooNf052Umh79BbNoSNLTfCm_ViKWmL8bt3Zce3niR9aIaZEaM_E5bYXFLg2isd6uBFDi1TaPARWw4kWO5G1N9kjwYyv8k-UnqSKwU1dvRC__G6j0pRsw1upB446btTF50GUmj7HrbgME-H1Uq7mXUODcyJ4PPE8QtGA04X1Z1oTnz__FAtdJfxPvOXCooCzixiEm5BTwRXP7A_BgKLCCwzFNv9bTczmIsvdhbidbXuzPhtviYXN8VaJOaCSj-KpQ4Xmvb58hy-OTZYtj4ITX_cgH4eZAEAAA==), which seems to have wrong AST.

Code after:

	context.state.analysis.classes.set(node, state_fields);

	/** @type {MethodDefinition | null} */
	let constructor = null;

	/**
	 * @param {PropertyDefinition | AssignmentExpression} node
	 * @param {Expression | PrivateIdentifier} key
	 * @param {Expression | null | undefined} value
	 */
	function handle(node, key, value) {
		const name = get_name(key);
		if (name === null) return;

		const rune = get_rune(value, context.state.scope);

		if (rune && is_state_creation_rune(rune)) {
			if (state_fields.has(name)) {
				e.state_field_duplicate(node, name);
			}

			const _key = (node.type === 'AssignmentExpression' || !node.static ? '' : '@') + name;
			const field = fields.get(_key);

			// if there's already a method or assigned field, error
			if (field && !(field.length === 1 && field[0] === 'prop')) {
				e.duplicate_class_field(node, _key);
			}

			state_fields.set(name, {
				node,
				type: rune,
				// @ts-expect-error for public state this is filled out in a moment
				key: key.type === 'PrivateIdentifier' ? key : null,
				value: /** @type {CallExpression} */ (value)
			});
		}
	}

	for (const child of node.body) {
		if (child.type === 'PropertyDefinition' && !child.computed && !child.static) {
			handle(child, child.key, child.value);
			const key = /** @type {string} */ (get_name(child.key));
			const field = fields.get(key);
			if (!field) {
				fields.set(key, [child.value ? 'assigned_prop' : 'prop']);
				continue;
			}
			e.duplicate_class_field(child, key);
		}