From 03e9ad4b3ef217b8b7d6a208fc54e7fb314e3d17 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Sat, 21 Dec 2019 12:14:22 +0800 Subject: [PATCH] feat better error message for else,elseif,then,catch with unclosed tag --- src/compiler/parse/index.ts | 9 ++++++ src/compiler/parse/state/mustache.ts | 30 ++++++++++++++++++- src/compiler/parse/utils/node.ts | 30 +++++++++++++++++++ .../error-catch-before-closing/error.json | 6 ++++ .../error-catch-before-closing/input.svelte | 4 +++ .../error-else-before-closing-2/error.json | 6 ++++ .../error-else-before-closing-2/input.svelte | 4 +++ .../error-else-before-closing-3/error.json | 6 ++++ .../error-else-before-closing-3/input.svelte | 2 ++ .../error-else-before-closing-2/error.json | 6 ++++ .../error-else-before-closing-2/input.svelte | 4 +++ .../error-else-before-closing-3/error.json | 6 ++++ .../error-else-before-closing-3/input.svelte | 2 ++ .../error-else-before-closing/error.json | 6 ++++ .../error-else-before-closing/input.svelte | 4 +++ .../error-else-if-before-closing-2/error.json | 6 ++++ .../input.svelte | 4 +++ .../error-else-if-before-closing/error.json | 6 ++++ .../error-else-if-before-closing/input.svelte | 4 +++ .../error-else-if-without-if/error.json | 6 ++++ .../error-else-if-without-if/input.svelte | 4 +++ .../error-then-before-closing/error.json | 6 ++++ .../error-then-before-closing/input.svelte | 4 +++ 23 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 src/compiler/parse/utils/node.ts create mode 100644 test/parser/samples/error-catch-before-closing/error.json create mode 100644 test/parser/samples/error-catch-before-closing/input.svelte create mode 100644 test/parser/samples/error-else-before-closing-2/error.json create mode 100644 test/parser/samples/error-else-before-closing-2/input.svelte create mode 100644 test/parser/samples/error-else-before-closing-3/error.json create mode 100644 test/parser/samples/error-else-before-closing-3/input.svelte create mode 100644 test/parser/samples/error-else-before-closing/error-else-before-closing-2/error.json create mode 100644 test/parser/samples/error-else-before-closing/error-else-before-closing-2/input.svelte create mode 100644 test/parser/samples/error-else-before-closing/error-else-before-closing-3/error.json create mode 100644 test/parser/samples/error-else-before-closing/error-else-before-closing-3/input.svelte create mode 100644 test/parser/samples/error-else-before-closing/error.json create mode 100644 test/parser/samples/error-else-before-closing/input.svelte create mode 100644 test/parser/samples/error-else-if-before-closing-2/error.json create mode 100644 test/parser/samples/error-else-if-before-closing-2/input.svelte create mode 100644 test/parser/samples/error-else-if-before-closing/error.json create mode 100644 test/parser/samples/error-else-if-before-closing/input.svelte create mode 100644 test/parser/samples/error-else-if-without-if/error.json create mode 100644 test/parser/samples/error-else-if-without-if/input.svelte create mode 100644 test/parser/samples/error-then-before-closing/error.json create mode 100644 test/parser/samples/error-then-before-closing/input.svelte diff --git a/src/compiler/parse/index.ts b/src/compiler/parse/index.ts index e5523746980f..fed39f6bfd01 100644 --- a/src/compiler/parse/index.ts +++ b/src/compiler/parse/index.ts @@ -82,6 +82,15 @@ export class Parser { return this.stack[this.stack.length - 1]; } + find_in_stack(fn) { + for (let i=this.stack.length -1; i>=0; i--) { + if (fn(this.stack[i])) { + return true; + } + } + return false; + } + acorn_error(err: any) { this.error({ code: `parse-error`, diff --git a/src/compiler/parse/state/mustache.ts b/src/compiler/parse/state/mustache.ts index 140e722b1027..a0241fe8b9f1 100644 --- a/src/compiler/parse/state/mustache.ts +++ b/src/compiler/parse/state/mustache.ts @@ -3,6 +3,7 @@ import read_expression from '../read/expression'; import { closing_tag_omitted } from '../utils/html'; import { whitespace } from '../../utils/patterns'; import { trim_start, trim_end } from '../../utils/trim'; +import { to_string } from '../utils/node'; import { Parser } from '../index'; import { TemplateNode } from '../../interfaces'; @@ -106,11 +107,19 @@ export default function mustache(parser: Parser) { // :else if if (parser.eat('if')) { const block = parser.current(); - if (block.type !== 'IfBlock') + if (block.type !== 'IfBlock') { + if (parser.find_in_stack(block => block.type === 'IfBlock')) { + parser.error({ + code: 'unclosed-open-tag', + message: `Expect to close ${to_string(block)} before {:else if ...} block` + }); + } + parser.error({ code: `invalid-elseif-placement`, message: 'Cannot have an {:else if ...} block outside an {#if ...} block' }); + } parser.require_whitespace(); @@ -142,6 +151,13 @@ export default function mustache(parser: Parser) { else { const block = parser.current(); if (block.type !== 'IfBlock' && block.type !== 'EachBlock') { + if (parser.find_in_stack(block => block.type === 'IfBlock' || block.type === 'EachBlock')) { + parser.error({ + code: 'unclosed-open-tag', + message: `Expect to close ${to_string(block)} before {:else} block` + }); + } + parser.error({ code: `invalid-else-placement`, message: 'Cannot have an {:else} block outside an {#if ...} or {#each ...} block' @@ -166,6 +182,12 @@ export default function mustache(parser: Parser) { if (is_then) { if (block.type !== 'PendingBlock') { + if (parser.find_in_stack(block => block.type === 'PendingBlock')) { + parser.error({ + code: 'unclosed-open-tag', + message: `Expect to close ${to_string(block)} before {:then} block` + }); + } parser.error({ code: `invalid-then-placement`, message: 'Cannot have an {:then} block outside an {#await ...} block' @@ -173,6 +195,12 @@ export default function mustache(parser: Parser) { } } else { if (block.type !== 'ThenBlock' && block.type !== 'PendingBlock') { + if (parser.find_in_stack(block => block.type === 'ThenBlock' || block.type === 'PendingBlock')) { + parser.error({ + code: 'unclosed-open-tag', + message: `Expect to close ${to_string(block)} before {:catch} block` + }); + } parser.error({ code: `invalid-catch-placement`, message: 'Cannot have an {:catch} block outside an {#await ...} block' diff --git a/src/compiler/parse/utils/node.ts b/src/compiler/parse/utils/node.ts new file mode 100644 index 000000000000..0d39529b5941 --- /dev/null +++ b/src/compiler/parse/utils/node.ts @@ -0,0 +1,30 @@ +import { TemplateNode } from '../../interfaces'; + +export function to_string(node: TemplateNode) { + switch (node.type) { + case 'IfBlock': + return '{#if} block'; + case 'ThenBlock': + return '{:then} block'; + case 'ElseBlock': + return '{:else} block'; + case 'PendingBlock': + case 'AwaitBlock': + return '{#await} block'; + case 'CatchBlock': + return '{:catch} block'; + case 'EachBlock': + return '{#each} block'; + case 'RawMustacheTag': + return '{@html} block'; + case 'DebugTag': + return '{@debug} block'; + case 'Element': + case 'InlineComponent': + case 'Slot': + case 'Title': + return `<${node.name}> tag`; + default: + return node.type; + } +} diff --git a/test/parser/samples/error-catch-before-closing/error.json b/test/parser/samples/error-catch-before-closing/error.json new file mode 100644 index 000000000000..6bc5ebc7fe84 --- /dev/null +++ b/test/parser/samples/error-catch-before-closing/error.json @@ -0,0 +1,6 @@ +{ + "code": "unclosed-open-tag", + "message": "Expect to close {#each} block before {:catch} block", + "start": { "line": 3, "column": 9, "character": 44 }, + "pos": 44 +} diff --git a/test/parser/samples/error-catch-before-closing/input.svelte b/test/parser/samples/error-catch-before-closing/input.svelte new file mode 100644 index 000000000000..9f4cf29f2682 --- /dev/null +++ b/test/parser/samples/error-catch-before-closing/input.svelte @@ -0,0 +1,4 @@ +{#await true} + {#each foo as bar} + {:catch f} +{/await} \ No newline at end of file diff --git a/test/parser/samples/error-else-before-closing-2/error.json b/test/parser/samples/error-else-before-closing-2/error.json new file mode 100644 index 000000000000..c15b47bbb9ed --- /dev/null +++ b/test/parser/samples/error-else-before-closing-2/error.json @@ -0,0 +1,6 @@ +{ + "code": "unclosed-open-tag", + "message": "Expect to close {#await} block before {:else} block", + "start": { "line": 3, "column": 8, "character": 32 }, + "pos": 32 +} diff --git a/test/parser/samples/error-else-before-closing-2/input.svelte b/test/parser/samples/error-else-before-closing-2/input.svelte new file mode 100644 index 000000000000..f4098bc521bd --- /dev/null +++ b/test/parser/samples/error-else-before-closing-2/input.svelte @@ -0,0 +1,4 @@ +{#if true} + {#await p} + {:else} +{/if} \ No newline at end of file diff --git a/test/parser/samples/error-else-before-closing-3/error.json b/test/parser/samples/error-else-before-closing-3/error.json new file mode 100644 index 000000000000..e5ec210c759d --- /dev/null +++ b/test/parser/samples/error-else-before-closing-3/error.json @@ -0,0 +1,6 @@ +{ + "code": "invalid-else-placement", + "message": "Cannot have an {:else} block outside an {#if ...} or {#each ...} block", + "start": { "line": 2, "column": 6, "character": 11 }, + "pos": 11 +} diff --git a/test/parser/samples/error-else-before-closing-3/input.svelte b/test/parser/samples/error-else-before-closing-3/input.svelte new file mode 100644 index 000000000000..fb434f26a3c1 --- /dev/null +++ b/test/parser/samples/error-else-before-closing-3/input.svelte @@ -0,0 +1,2 @@ +
  • +{:else} \ No newline at end of file diff --git a/test/parser/samples/error-else-before-closing/error-else-before-closing-2/error.json b/test/parser/samples/error-else-before-closing/error-else-before-closing-2/error.json new file mode 100644 index 000000000000..c15b47bbb9ed --- /dev/null +++ b/test/parser/samples/error-else-before-closing/error-else-before-closing-2/error.json @@ -0,0 +1,6 @@ +{ + "code": "unclosed-open-tag", + "message": "Expect to close {#await} block before {:else} block", + "start": { "line": 3, "column": 8, "character": 32 }, + "pos": 32 +} diff --git a/test/parser/samples/error-else-before-closing/error-else-before-closing-2/input.svelte b/test/parser/samples/error-else-before-closing/error-else-before-closing-2/input.svelte new file mode 100644 index 000000000000..f4098bc521bd --- /dev/null +++ b/test/parser/samples/error-else-before-closing/error-else-before-closing-2/input.svelte @@ -0,0 +1,4 @@ +{#if true} + {#await p} + {:else} +{/if} \ No newline at end of file diff --git a/test/parser/samples/error-else-before-closing/error-else-before-closing-3/error.json b/test/parser/samples/error-else-before-closing/error-else-before-closing-3/error.json new file mode 100644 index 000000000000..e5ec210c759d --- /dev/null +++ b/test/parser/samples/error-else-before-closing/error-else-before-closing-3/error.json @@ -0,0 +1,6 @@ +{ + "code": "invalid-else-placement", + "message": "Cannot have an {:else} block outside an {#if ...} or {#each ...} block", + "start": { "line": 2, "column": 6, "character": 11 }, + "pos": 11 +} diff --git a/test/parser/samples/error-else-before-closing/error-else-before-closing-3/input.svelte b/test/parser/samples/error-else-before-closing/error-else-before-closing-3/input.svelte new file mode 100644 index 000000000000..fb434f26a3c1 --- /dev/null +++ b/test/parser/samples/error-else-before-closing/error-else-before-closing-3/input.svelte @@ -0,0 +1,2 @@ +
  • +{:else} \ No newline at end of file diff --git a/test/parser/samples/error-else-before-closing/error.json b/test/parser/samples/error-else-before-closing/error.json new file mode 100644 index 000000000000..c3a8c74cf06f --- /dev/null +++ b/test/parser/samples/error-else-before-closing/error.json @@ -0,0 +1,6 @@ +{ + "code": "unclosed-open-tag", + "message": "Expect to close
  • tag before {:else} block", + "start": { "line": 3, "column": 8, "character": 26 }, + "pos": 26 +} diff --git a/test/parser/samples/error-else-before-closing/input.svelte b/test/parser/samples/error-else-before-closing/input.svelte new file mode 100644 index 000000000000..51c23b2a30a2 --- /dev/null +++ b/test/parser/samples/error-else-before-closing/input.svelte @@ -0,0 +1,4 @@ +{#if true} +
  • + {:else} +{/if} \ No newline at end of file diff --git a/test/parser/samples/error-else-if-before-closing-2/error.json b/test/parser/samples/error-else-if-before-closing-2/error.json new file mode 100644 index 000000000000..717d51044270 --- /dev/null +++ b/test/parser/samples/error-else-if-before-closing-2/error.json @@ -0,0 +1,6 @@ +{ + "code": "unclosed-open-tag", + "message": "Expect to close

    tag before {:else if ...} block", + "start": { "line": 3, "column": 11, "character": 28 }, + "pos": 28 +} diff --git a/test/parser/samples/error-else-if-before-closing-2/input.svelte b/test/parser/samples/error-else-if-before-closing-2/input.svelte new file mode 100644 index 000000000000..03fb7726cba9 --- /dev/null +++ b/test/parser/samples/error-else-if-before-closing-2/input.svelte @@ -0,0 +1,4 @@ +{#if true} +

    + {:else if false} +{/if} \ No newline at end of file diff --git a/test/parser/samples/error-else-if-before-closing/error.json b/test/parser/samples/error-else-if-before-closing/error.json new file mode 100644 index 000000000000..3d9120fc0f90 --- /dev/null +++ b/test/parser/samples/error-else-if-before-closing/error.json @@ -0,0 +1,6 @@ +{ + "code": "unclosed-open-tag", + "message": "Expect to close {#await} block before {:else if ...} block", + "start": { "line": 3, "column": 11, "character": 37 }, + "pos": 37 +} diff --git a/test/parser/samples/error-else-if-before-closing/input.svelte b/test/parser/samples/error-else-if-before-closing/input.svelte new file mode 100644 index 000000000000..b6684a9496e0 --- /dev/null +++ b/test/parser/samples/error-else-if-before-closing/input.svelte @@ -0,0 +1,4 @@ +{#if true} + {#await foo} + {:else if false} +{/if} \ No newline at end of file diff --git a/test/parser/samples/error-else-if-without-if/error.json b/test/parser/samples/error-else-if-without-if/error.json new file mode 100644 index 000000000000..1ff8aa45b8ca --- /dev/null +++ b/test/parser/samples/error-else-if-without-if/error.json @@ -0,0 +1,6 @@ +{ + "code": "invalid-elseif-placement", + "message": "Cannot have an {:else if ...} block outside an {#if ...} block", + "start": { "line": 3, "column": 11, "character": 36 }, + "pos": 36 +} diff --git a/test/parser/samples/error-else-if-without-if/input.svelte b/test/parser/samples/error-else-if-without-if/input.svelte new file mode 100644 index 000000000000..084cf7434637 --- /dev/null +++ b/test/parser/samples/error-else-if-without-if/input.svelte @@ -0,0 +1,4 @@ +{#await foo} +{:then bar} + {:else if} +{/await} \ No newline at end of file diff --git a/test/parser/samples/error-then-before-closing/error.json b/test/parser/samples/error-then-before-closing/error.json new file mode 100644 index 000000000000..22e08f2bd622 --- /dev/null +++ b/test/parser/samples/error-then-before-closing/error.json @@ -0,0 +1,6 @@ +{ + "code": "unclosed-open-tag", + "message": "Expect to close

  • tag before {:then} block", + "start": { "line": 3, "column": 8, "character": 29 }, + "pos": 29 +} diff --git a/test/parser/samples/error-then-before-closing/input.svelte b/test/parser/samples/error-then-before-closing/input.svelte new file mode 100644 index 000000000000..720b292fe733 --- /dev/null +++ b/test/parser/samples/error-then-before-closing/input.svelte @@ -0,0 +1,4 @@ +{#await true} +
  • + {:then f} +{/await} \ No newline at end of file