@@ -232,23 +232,67 @@ export function createSwitchBlock(
232232 const unknownBlocks : t . UnknownBlock [ ] = [ ] ;
233233 let collectedCases : t . SwitchBlockCase [ ] = [ ] ;
234234 let firstCaseStart : ParseSourceSpan | null = null ;
235+ let exhaustiveCheck : t . SwitchExhaustiveCheck | null = null ;
235236
236237 // Here we assume that all the blocks are valid given that we validated them above.
237238 for ( const node of ast . children ) {
238239 if ( ! ( node instanceof html . Block ) ) {
239240 continue ;
240241 }
241242
242- if ( ( node . name !== 'case' || node . parameters . length === 0 ) && node . name !== 'default' ) {
243+ if (
244+ ( node . name !== 'case' || node . parameters . length === 0 ) &&
245+ node . name !== 'default' &&
246+ node . name !== 'default never'
247+ ) {
243248 unknownBlocks . push ( new t . UnknownBlock ( node . name , node . sourceSpan , node . nameSpan ) ) ;
244249 continue ;
245250 }
246251
252+ if ( exhaustiveCheck !== null ) {
253+ errors . push (
254+ new ParseError (
255+ node . sourceSpan ,
256+ '@default block with "never" parameter must be the last case in a switch' ,
257+ ) ,
258+ ) ;
259+ }
260+
247261 const isCase = node . name === 'case' ;
248262 let expression : AST | null = null ;
249263
250264 if ( isCase ) {
251265 expression = parseBlockParameterToBinding ( node . parameters [ 0 ] , bindingParser ) ;
266+ } else if ( node . name === 'default never' ) {
267+ if (
268+ node . children . length > 0 ||
269+ ( node . endSourceSpan !== null &&
270+ node . endSourceSpan . start . offset !== node . endSourceSpan . end . offset )
271+ ) {
272+ errors . push (
273+ new ParseError (
274+ node . sourceSpan ,
275+ '@default block with "never" parameter cannot have a body' ,
276+ ) ,
277+ ) ;
278+ }
279+
280+ if ( collectedCases . length > 0 ) {
281+ errors . push (
282+ new ParseError (
283+ node . sourceSpan ,
284+ 'A @case block with no body cannot be followed by a @default block with "never" parameter' ,
285+ ) ,
286+ ) ;
287+ }
288+
289+ exhaustiveCheck = new t . SwitchExhaustiveCheck (
290+ node . sourceSpan ,
291+ node . startSourceSpan ,
292+ node . endSourceSpan ,
293+ node . nameSpan ,
294+ ) ;
295+ continue ;
252296 }
253297
254298 const switchCase = new t . SwitchBlockCase (
@@ -300,6 +344,7 @@ export function createSwitchBlock(
300344 primaryExpression ,
301345 groups ,
302346 unknownBlocks ,
347+ exhaustiveCheck ,
303348 ast . sourceSpan ,
304349 ast . startSourceSpan ,
305350 ast . endSourceSpan ,
@@ -570,14 +615,24 @@ function validateSwitchBlock(ast: html.Block): ParseError[] {
570615 continue ;
571616 }
572617
573- if ( ! ( node instanceof html . Block ) || ( node . name !== 'case' && node . name !== 'default' ) ) {
618+ if (
619+ ! ( node instanceof html . Block ) ||
620+ ( node . name !== 'case' && node . name !== 'default' && node . name !== 'default never' )
621+ ) {
574622 errors . push (
575623 new ParseError ( node . sourceSpan , '@switch block can only contain @case and @default blocks' ) ,
576624 ) ;
577625 continue ;
578626 }
579627
580- if ( node . name === 'default' ) {
628+ if ( node . name === 'default never' ) {
629+ if ( hasDefault ) {
630+ errors . push (
631+ new ParseError ( node . startSourceSpan , '@switch block can only have one @default block' ) ,
632+ ) ;
633+ }
634+ hasDefault = true ;
635+ } else if ( node . name === 'default' ) {
581636 if ( hasDefault ) {
582637 errors . push (
583638 new ParseError ( node . startSourceSpan , '@switch block can only have one @default block' ) ,
0 commit comments