diff --git a/CHANGELOG.md b/CHANGELOG.md index 00ea564045..10e23686ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Svelte changelog +## Unreleased + +* Disallow attribute/prop names from matching two-way-bound names or `{shorthand}` attribute/prop names ([#4325](https://github.com/sveltejs/svelte/issues/4325)) + ## 3.18.1 * Fix code generation error with adjacent inline and block comments ([#4312](https://github.com/sveltejs/svelte/issues/4312)) diff --git a/src/compiler/parse/state/tag.ts b/src/compiler/parse/state/tag.ts index c7e761afc2..40bb01de9b 100644 --- a/src/compiler/parse/state/tag.ts +++ b/src/compiler/parse/state/tag.ts @@ -288,6 +288,16 @@ function read_tag_name(parser: Parser) { function read_attribute(parser: Parser, unique_names: Set) { const start = parser.index; + function check_unique(name: string) { + if (unique_names.has(name)) { + parser.error({ + code: `duplicate-attribute`, + message: 'Attributes need to be unique' + }, start); + } + unique_names.add(name); + } + if (parser.eat('{')) { parser.allow_whitespace(); @@ -310,6 +320,8 @@ function read_attribute(parser: Parser, unique_names: Set) { parser.allow_whitespace(); parser.eat('}', true); + check_unique(name); + return { start, end: parser.index, @@ -341,17 +353,6 @@ function read_attribute(parser: Parser, unique_names: Set) { const colon_index = name.indexOf(':'); const type = colon_index !== -1 && get_directive_type(name.slice(0, colon_index)); - if (unique_names.has(name)) { - parser.error({ - code: `duplicate-attribute`, - message: 'Attributes need to be unique' - }, start); - } - - if (type !== "EventHandler") { - unique_names.add(name); - } - let value: any[] | true = true; if (parser.eat('=')) { parser.allow_whitespace(); @@ -367,6 +368,12 @@ function read_attribute(parser: Parser, unique_names: Set) { if (type) { const [directive_name, ...modifiers] = name.slice(colon_index + 1).split('|'); + if (type === 'Binding' && directive_name !== 'this') { + check_unique(directive_name); + } else if (type !== 'EventHandler') { + check_unique(name); + } + if (type === 'Ref') { parser.error({ code: `invalid-ref-directive`, @@ -410,6 +417,8 @@ function read_attribute(parser: Parser, unique_names: Set) { return directive; } + check_unique(name); + return { start, end, diff --git a/test/parser/samples/attribute-unique-binding-error/error.json b/test/parser/samples/attribute-unique-binding-error/error.json new file mode 100644 index 0000000000..dd14572149 --- /dev/null +++ b/test/parser/samples/attribute-unique-binding-error/error.json @@ -0,0 +1,10 @@ +{ + "code": "duplicate-attribute", + "message": "Attributes need to be unique", + "start": { + "line": 1, + "column": 17, + "character": 17 + }, + "pos": 17 +} diff --git a/test/parser/samples/attribute-unique-binding-error/input.svelte b/test/parser/samples/attribute-unique-binding-error/input.svelte new file mode 100644 index 0000000000..78f854bad6 --- /dev/null +++ b/test/parser/samples/attribute-unique-binding-error/input.svelte @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/parser/samples/attribute-unique-shorthand-error/error.json b/test/parser/samples/attribute-unique-shorthand-error/error.json new file mode 100644 index 0000000000..dd14572149 --- /dev/null +++ b/test/parser/samples/attribute-unique-shorthand-error/error.json @@ -0,0 +1,10 @@ +{ + "code": "duplicate-attribute", + "message": "Attributes need to be unique", + "start": { + "line": 1, + "column": 17, + "character": 17 + }, + "pos": 17 +} diff --git a/test/parser/samples/attribute-unique-shorthand-error/input.svelte b/test/parser/samples/attribute-unique-shorthand-error/input.svelte new file mode 100644 index 0000000000..d486eab558 --- /dev/null +++ b/test/parser/samples/attribute-unique-shorthand-error/input.svelte @@ -0,0 +1 @@ +
\ No newline at end of file