diff --git a/src/compiler/parse/index.ts b/src/compiler/parse/index.ts index a809eeebeb..b10bab35f3 100644 --- a/src/compiler/parse/index.ts +++ b/src/compiler/parse/index.ts @@ -8,6 +8,12 @@ import error from '../utils/error'; type ParserState = (parser: Parser) => (ParserState | void); +interface LastAutoCloseTag { + tag: string; + reason: string; + depth: number; +} + export class Parser { readonly template: string; readonly filename?: string; @@ -20,6 +26,7 @@ export class Parser { css: Style[] = []; js: Script[] = []; meta_tags = {}; + last_auto_closed_tag?: LastAutoCloseTag; constructor(template: string, options: ParserOptions) { if (typeof template !== 'string') { diff --git a/src/compiler/parse/state/tag.ts b/src/compiler/parse/state/tag.ts index ce8a0a9aa7..c04429ab91 100644 --- a/src/compiler/parse/state/tag.ts +++ b/src/compiler/parse/state/tag.ts @@ -133,11 +133,19 @@ export default function tag(parser: Parser) { // close any elements that don't have their own closing tags, e.g.

while (parent.name !== name) { - if (parent.type !== 'Element') + if (parent.type !== 'Element') { + if (parser.last_auto_closed_tag && parser.last_auto_closed_tag.tag === name) { + parser.error({ + code: `invalid-closing-tag`, + message: `<${parser.last_auto_closed_tag.tag}> cannot have child element <${parser.last_auto_closed_tag.reason}>`, + }, start); + } + parser.error({ code: `invalid-closing-tag`, message: ` attempted to close an element that was not open` }, start); + } parent.end = start; parser.stack.pop(); @@ -148,10 +156,19 @@ export default function tag(parser: Parser) { parent.end = parser.index; parser.stack.pop(); + if (parser.last_auto_closed_tag && parser.stack.length < parser.last_auto_closed_tag.depth) { + parser.last_auto_closed_tag = null; + } + return; } else if (closing_tag_omitted(parent.name, name)) { parent.end = start; parser.stack.pop(); + parser.last_auto_closed_tag ={ + tag: parent.name, + reason: name, + depth: parser.stack.length, + }; } const unique_names: Set = new Set(); diff --git a/test/parser/samples/error-unmatched-closing-tag-autoclose-2/error.json b/test/parser/samples/error-unmatched-closing-tag-autoclose-2/error.json new file mode 100644 index 0000000000..d24296bd96 --- /dev/null +++ b/test/parser/samples/error-unmatched-closing-tag-autoclose-2/error.json @@ -0,0 +1,10 @@ +{ + "code": "invalid-closing-tag", + "message": "

attempted to close an element that was not open", + "pos": 38, + "start": { + "character": 38, + "column": 0, + "line": 5 + } +} diff --git a/test/parser/samples/error-unmatched-closing-tag-autoclose-2/input.svelte b/test/parser/samples/error-unmatched-closing-tag-autoclose-2/input.svelte new file mode 100644 index 0000000000..5182577921 --- /dev/null +++ b/test/parser/samples/error-unmatched-closing-tag-autoclose-2/input.svelte @@ -0,0 +1,5 @@ +
+

+

pre tag
+
+

\ No newline at end of file diff --git a/test/parser/samples/error-unmatched-closing-tag-autoclose/error.json b/test/parser/samples/error-unmatched-closing-tag-autoclose/error.json new file mode 100644 index 0000000000..cfd7fd8aa8 --- /dev/null +++ b/test/parser/samples/error-unmatched-closing-tag-autoclose/error.json @@ -0,0 +1,10 @@ +{ + "code": "invalid-closing-tag", + "message": "

cannot have child element

",
+	"pos": 24,
+	"start": {
+		"character": 24,
+		"column": 0,
+		"line": 3
+	}
+}
diff --git a/test/parser/samples/error-unmatched-closing-tag-autoclose/input.svelte b/test/parser/samples/error-unmatched-closing-tag-autoclose/input.svelte
new file mode 100644
index 0000000000..0bfd609736
--- /dev/null
+++ b/test/parser/samples/error-unmatched-closing-tag-autoclose/input.svelte
@@ -0,0 +1,3 @@
+

+

pre tag
+

\ No newline at end of file