better error messages for else, elseif, then, catch with unclosed tag (#4136)

pull/4395/head
Tan Li Hau 4 years ago committed by GitHub
parent cb67a53e51
commit 2195832ecc

@ -3,6 +3,7 @@
## Unreleased
* Fix binding to module-level variables ([#4086](https://github.com/sveltejs/svelte/issues/4086))
* Improve parsing error messages when there is a pending unclosed tag ([#4131](https://github.com/sveltejs/svelte/issues/4131))
* Disallow attribute/prop names from matching two-way-bound names or `{shorthand}` attribute/prop names ([#4325](https://github.com/sveltejs/svelte/issues/4325))
* Improve performance of `flush()` by not using `.shift()` ([#4356](https://github.com/sveltejs/svelte/pull/4356))
* Fix code generation error with precedence of arrow functions ([#4384](https://github.com/sveltejs/svelte/issues/4384))

@ -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,14 @@ export default function mustache(parser: Parser) {
// :else if
if (parser.eat('if')) {
const block = parser.current();
if (block.type !== 'IfBlock')
if (block.type !== 'IfBlock') {
parser.error({
code: `invalid-elseif-placement`,
message: 'Cannot have an {:else if ...} block outside an {#if ...} block'
message: parser.stack.some(block => block.type === 'IfBlock')
? `Expected to close ${to_string(block)} before seeing {:else if ...} block`
: `Cannot have an {:else if ...} block outside an {#if ...} block`
});
}
parser.require_whitespace();
@ -144,7 +148,9 @@ export default function mustache(parser: Parser) {
if (block.type !== 'IfBlock' && block.type !== 'EachBlock') {
parser.error({
code: `invalid-else-placement`,
message: 'Cannot have an {:else} block outside an {#if ...} or {#each ...} block'
message: parser.stack.some(block => block.type === 'IfBlock' || block.type === 'EachBlock')
? `Expected to close ${to_string(block)} before seeing {:else} block`
: `Cannot have an {:else} block outside an {#if ...} or {#each ...} block`
});
}
@ -168,14 +174,18 @@ export default function mustache(parser: Parser) {
if (block.type !== 'PendingBlock') {
parser.error({
code: `invalid-then-placement`,
message: 'Cannot have an {:then} block outside an {#await ...} block'
message: parser.stack.some(block => block.type === 'PendingBlock')
? `Expected to close ${to_string(block)} before seeing {:then} block`
: `Cannot have an {:then} block outside an {#await ...} block`
});
}
} else {
if (block.type !== 'ThenBlock' && block.type !== 'PendingBlock') {
parser.error({
code: `invalid-catch-placement`,
message: 'Cannot have an {:catch} block outside an {#await ...} block'
message: parser.stack.some(block => block.type === 'ThenBlock' || block.type === 'PendingBlock')
? `Expected to close ${to_string(block)} before seeing {:catch} block`
: `Cannot have an {:catch} block outside an {#await ...} block`
});
}
}

@ -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;
}
}

@ -0,0 +1,6 @@
{
"code": "invalid-catch-placement",
"message": "Expected to close {#each} block before seeing {:catch} block",
"start": { "line": 3, "column": 7, "character": 41 },
"pos": 41
}

@ -0,0 +1,4 @@
{#await true}
{#each foo as bar}
{:catch f}
{/await}

@ -0,0 +1,6 @@
{
"code": "invalid-else-placement",
"message": "Expected to close {#await} block before seeing {:else} block",
"start": { "line": 3, "column": 6, "character": 29 },
"pos": 29
}

@ -0,0 +1,4 @@
{#if true}
{#await p}
{:else}
{/if}

@ -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
}

@ -0,0 +1,6 @@
{
"code": "invalid-else-placement",
"message": "Expected to close <li> tag before seeing {:else} block",
"start": { "line": 3, "column": 6, "character": 23 },
"pos": 23
}

@ -0,0 +1,6 @@
{
"code": "invalid-elseif-placement",
"message": "Expected to close <p> tag before seeing {:else if ...} block",
"start": { "line": 3, "column": 9, "character": 25 },
"pos": 25
}

@ -0,0 +1,4 @@
{#if true}
<p>
{:else if false}
{/if}

@ -0,0 +1,6 @@
{
"code": "invalid-elseif-placement",
"message": "Expected to close {#await} block before seeing {:else if ...} block",
"start": { "line": 3, "column": 9, "character": 34 },
"pos": 34
}

@ -0,0 +1,4 @@
{#if true}
{#await foo}
{:else if false}
{/if}

@ -0,0 +1,6 @@
{
"code": "invalid-elseif-placement",
"message": "Cannot have an {:else if ...} block outside an {#if ...} block",
"start": { "line": 3, "column": 10, "character": 35 },
"pos": 35
}

@ -0,0 +1,4 @@
{#await foo}
{:then bar}
{:else if}
{/await}

@ -0,0 +1,6 @@
{
"code": "invalid-then-placement",
"message": "Expected to close <li> tag before seeing {:then} block",
"start": { "line": 3, "column": 6, "character": 26 },
"pos": 26
}

@ -0,0 +1,4 @@
{#await true}
<li>
{:then f}
{/await}

@ -0,0 +1,19 @@
{#if true}
<input>
{:else}
{/if}
{#if true}
<br>
{:else}
{/if}
{#await true}
<input>
{:then f}
{/await}
{#await true}
<br>
{:then f}
{/await}

@ -0,0 +1,218 @@
{
"html": {
"start": 0,
"end": 148,
"type": "Fragment",
"children": [
{
"start": 0,
"end": 33,
"type": "IfBlock",
"expression": {
"type": "Literal",
"start": 5,
"end": 9,
"value": true,
"raw": "true"
},
"children": [
{
"start": 12,
"end": 19,
"type": "Element",
"name": "input",
"attributes": [],
"children": []
}
],
"else": {
"start": 27,
"end": 28,
"type": "ElseBlock",
"children": []
}
},
{
"start": 33,
"end": 35,
"type": "Text",
"raw": "\n\n",
"data": "\n\n"
},
{
"start": 35,
"end": 65,
"type": "IfBlock",
"expression": {
"type": "Literal",
"start": 40,
"end": 44,
"value": true,
"raw": "true"
},
"children": [
{
"start": 47,
"end": 51,
"type": "Element",
"name": "br",
"attributes": [],
"children": []
}
],
"else": {
"start": 59,
"end": 60,
"type": "ElseBlock",
"children": []
}
},
{
"start": 65,
"end": 67,
"type": "Text",
"raw": "\n\n",
"data": "\n\n"
},
{
"start": 67,
"end": 108,
"type": "AwaitBlock",
"expression": {
"type": "Literal",
"start": 75,
"end": 79,
"value": true,
"raw": "true"
},
"value": "f",
"error": null,
"pending": {
"start": 80,
"end": 90,
"type": "PendingBlock",
"children": [
{
"start": 80,
"end": 82,
"type": "Text",
"raw": "\n\t",
"data": "\n\t"
},
{
"start": 82,
"end": 89,
"type": "Element",
"name": "input",
"attributes": [],
"children": []
},
{
"start": 89,
"end": 90,
"type": "Text",
"raw": "\n",
"data": "\n"
}
],
"skip": false
},
"then": {
"start": 90,
"end": 100,
"type": "ThenBlock",
"children": [
{
"start": 99,
"end": 100,
"type": "Text",
"raw": "\n",
"data": "\n"
}
],
"skip": false
},
"catch": {
"start": null,
"end": null,
"type": "CatchBlock",
"children": [],
"skip": true
}
},
{
"start": 108,
"end": 110,
"type": "Text",
"raw": "\n\n",
"data": "\n\n"
},
{
"start": 110,
"end": 148,
"type": "AwaitBlock",
"expression": {
"type": "Literal",
"start": 118,
"end": 122,
"value": true,
"raw": "true"
},
"value": "f",
"error": null,
"pending": {
"start": 123,
"end": 130,
"type": "PendingBlock",
"children": [
{
"start": 123,
"end": 125,
"type": "Text",
"raw": "\n\t",
"data": "\n\t"
},
{
"start": 125,
"end": 129,
"type": "Element",
"name": "br",
"attributes": [],
"children": []
},
{
"start": 129,
"end": 130,
"type": "Text",
"raw": "\n",
"data": "\n"
}
],
"skip": false
},
"then": {
"start": 130,
"end": 140,
"type": "ThenBlock",
"children": [
{
"start": 139,
"end": 140,
"type": "Text",
"raw": "\n",
"data": "\n"
}
],
"skip": false
},
"catch": {
"start": null,
"end": null,
"type": "CatchBlock",
"children": [],
"skip": true
}
}
]
}
}
Loading…
Cancel
Save