@typespec/compiler 1.16.0 → 1.17.0-dev.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/dist/manifest.js +2 -2
  2. package/dist/src/core/binder.d.ts +1 -1
  3. package/dist/src/core/binder.d.ts.map +1 -1
  4. package/dist/src/core/binder.js +33 -0
  5. package/dist/src/core/binder.js.map +1 -1
  6. package/dist/src/core/checker.d.ts.map +1 -1
  7. package/dist/src/core/checker.js +168 -53
  8. package/dist/src/core/checker.js.map +1 -1
  9. package/dist/src/core/features.d.ts +3 -0
  10. package/dist/src/core/features.d.ts.map +1 -1
  11. package/dist/src/core/features.js +3 -0
  12. package/dist/src/core/features.js.map +1 -1
  13. package/dist/src/core/helpers/type-name-utils.js +19 -3
  14. package/dist/src/core/helpers/type-name-utils.js.map +1 -1
  15. package/dist/src/core/inspector/node.js +6 -1
  16. package/dist/src/core/inspector/node.js.map +1 -1
  17. package/dist/src/core/messages.d.ts +38 -2
  18. package/dist/src/core/messages.d.ts.map +1 -1
  19. package/dist/src/core/messages.js +12 -0
  20. package/dist/src/core/messages.js.map +1 -1
  21. package/dist/src/core/modifiers.d.ts +9 -2
  22. package/dist/src/core/modifiers.d.ts.map +1 -1
  23. package/dist/src/core/modifiers.js +8 -0
  24. package/dist/src/core/modifiers.js.map +1 -1
  25. package/dist/src/core/name-resolver.d.ts.map +1 -1
  26. package/dist/src/core/name-resolver.js +30 -4
  27. package/dist/src/core/name-resolver.js.map +1 -1
  28. package/dist/src/core/parser.d.ts.map +1 -1
  29. package/dist/src/core/parser.js +257 -23
  30. package/dist/src/core/parser.js.map +1 -1
  31. package/dist/src/core/scanner.d.ts +5 -0
  32. package/dist/src/core/scanner.d.ts.map +1 -1
  33. package/dist/src/core/scanner.js +10 -0
  34. package/dist/src/core/scanner.js.map +1 -1
  35. package/dist/src/core/type-utils.d.ts.map +1 -1
  36. package/dist/src/core/type-utils.js +3 -0
  37. package/dist/src/core/type-utils.js.map +1 -1
  38. package/dist/src/core/types.d.ts +111 -14
  39. package/dist/src/core/types.d.ts.map +1 -1
  40. package/dist/src/core/types.js +4 -0
  41. package/dist/src/core/types.js.map +1 -1
  42. package/dist/src/formatter/print/comment-handler.js +11 -1
  43. package/dist/src/formatter/print/comment-handler.js.map +1 -1
  44. package/dist/src/formatter/print/printer.d.ts +2 -2
  45. package/dist/src/formatter/print/printer.d.ts.map +1 -1
  46. package/dist/src/formatter/print/printer.js +106 -15
  47. package/dist/src/formatter/print/printer.js.map +1 -1
  48. package/dist/src/init/init.d.ts.map +1 -1
  49. package/dist/src/init/init.js +3 -1
  50. package/dist/src/init/init.js.map +1 -1
  51. package/dist/src/init/prompts.d.ts +1 -1
  52. package/dist/src/init/prompts.d.ts.map +1 -1
  53. package/dist/src/init/prompts.js +1 -1
  54. package/dist/src/init/prompts.js.map +1 -1
  55. package/dist/src/init/scaffold.d.ts.map +1 -1
  56. package/dist/src/init/scaffold.js +31 -3
  57. package/dist/src/init/scaffold.js.map +1 -1
  58. package/dist/src/init/template-source/in-memory-template-source.d.ts +1 -1
  59. package/dist/src/init/template-source/in-memory-template-source.d.ts.map +1 -1
  60. package/dist/src/init/template-source/in-memory-template-source.js +2 -1
  61. package/dist/src/init/template-source/in-memory-template-source.js.map +1 -1
  62. package/dist/src/init/template-source/types.d.ts +1 -0
  63. package/dist/src/init/template-source/types.d.ts.map +1 -1
  64. package/dist/src/init/template-source/types.js +10 -1
  65. package/dist/src/init/template-source/types.js.map +1 -1
  66. package/dist/src/init/template-source/uri-template-source.d.ts +1 -1
  67. package/dist/src/init/template-source/uri-template-source.d.ts.map +1 -1
  68. package/dist/src/init/template-source/uri-template-source.js +23 -2
  69. package/dist/src/init/template-source/uri-template-source.js.map +1 -1
  70. package/dist/src/module-resolver/utils.d.ts.map +1 -1
  71. package/dist/src/module-resolver/utils.js +6 -2
  72. package/dist/src/module-resolver/utils.js.map +1 -1
  73. package/dist/src/server/classify.d.ts.map +1 -1
  74. package/dist/src/server/classify.js +12 -4
  75. package/dist/src/server/classify.js.map +1 -1
  76. package/dist/src/server/completion.js +14 -1
  77. package/dist/src/server/completion.js.map +1 -1
  78. package/dist/src/server/tmlanguage.d.ts.map +1 -1
  79. package/dist/src/server/tmlanguage.js +69 -1
  80. package/dist/src/server/tmlanguage.js.map +1 -1
  81. package/dist/src/server/type-signature.js +4 -1
  82. package/dist/src/server/type-signature.js.map +1 -1
  83. package/dist/src/testing/tester.d.ts +11 -0
  84. package/dist/src/testing/tester.d.ts.map +1 -1
  85. package/dist/src/testing/tester.js +28 -3
  86. package/dist/src/testing/tester.js.map +1 -1
  87. package/dist/src/typekit/kits/enum.d.ts +14 -1
  88. package/dist/src/typekit/kits/enum.d.ts.map +1 -1
  89. package/dist/src/typekit/kits/enum.js +2 -1
  90. package/dist/src/typekit/kits/enum.js.map +1 -1
  91. package/dist/src/typekit/kits/model.d.ts +12 -1
  92. package/dist/src/typekit/kits/model.d.ts.map +1 -1
  93. package/dist/src/typekit/kits/model.js +3 -2
  94. package/dist/src/typekit/kits/model.js.map +1 -1
  95. package/dist/src/typekit/kits/union.d.ts +12 -1
  96. package/dist/src/typekit/kits/union.d.ts.map +1 -1
  97. package/dist/src/typekit/kits/union.js +3 -3
  98. package/dist/src/typekit/kits/union.js.map +1 -1
  99. package/dist/src/typekit/utils.d.ts.map +1 -1
  100. package/dist/src/typekit/utils.js +17 -0
  101. package/dist/src/typekit/utils.js.map +1 -1
  102. package/dist/typespec.tmLanguage +209 -37
  103. package/package.json +12 -9
@@ -3,7 +3,7 @@ import { codePointBefore, isIdentifierContinue, trim } from "./charcode.js";
3
3
  import { compilerAssert } from "./diagnostics.js";
4
4
  import { createDiagnostic } from "./messages.js";
5
5
  import { modifiersToFlags } from "./modifiers.js";
6
- import { createScanner, isComment, isKeyword, isModifier, isPunctuation, isReservedKeyword, isStatementKeyword, isTrivia, skipContinuousIdentifier, skipTrivia, skipTriviaBackward, Token, TokenDisplay, TokenFlags, } from "./scanner.js";
6
+ import { createScanner, isComment, isDeclarationExpressionStatementKeyword, isKeyword, isModifier, isPunctuation, isReservedKeyword, isStatementKeyword, isTrivia, skipContinuousIdentifier, skipTrivia, skipTriviaBackward, Token, TokenDisplay, TokenFlags, } from "./scanner.js";
7
7
  import { IdentifierKind, ModifierFlags, NodeFlags, SyntaxKind } from "./types.js";
8
8
  /**
9
9
  * The fixed set of options for each of the kinds of delimited lists in TypeSpec.
@@ -26,10 +26,12 @@ var ListKind;
26
26
  ListKind.DecoratorArguments = {
27
27
  ...ListKind.OperationParameters,
28
28
  invalidAnnotationTarget: "expression",
29
+ allowDeclarationExpression: true,
29
30
  };
30
31
  ListKind.FunctionArguments = {
31
32
  ...ListKind.OperationParameters,
32
33
  invalidAnnotationTarget: "expression",
34
+ allowDeclarationExpression: true,
33
35
  };
34
36
  ListKind.ModelProperties = {
35
37
  ...PropertiesBase,
@@ -91,12 +93,14 @@ var ListKind;
91
93
  };
92
94
  ListKind.TemplateArguments = {
93
95
  ...ListKind.TemplateParameters,
96
+ allowDeclarationExpression: true,
94
97
  };
95
98
  ListKind.CallArguments = {
96
99
  ...ExpresionsBase,
97
100
  allowEmpty: true,
98
101
  open: Token.OpenParen,
99
102
  close: Token.CloseParen,
103
+ allowDeclarationExpression: true,
100
104
  };
101
105
  ListKind.Heritage = {
102
106
  ...ExpresionsBase,
@@ -109,6 +113,7 @@ var ListKind;
109
113
  allowEmpty: true,
110
114
  open: Token.OpenBracket,
111
115
  close: Token.CloseBracket,
116
+ allowDeclarationExpression: true,
112
117
  };
113
118
  ListKind.ArrayLiteral = {
114
119
  ...ExpresionsBase,
@@ -466,6 +471,25 @@ function createParser(code, options = {}) {
466
471
  }
467
472
  return undefined;
468
473
  }
474
+ function parseUnionDeclarationExpression(pos, decorators = []) {
475
+ parseExpected(Token.UnionKeyword);
476
+ const id = parseOptionalDeclarationExpressionIdentifier();
477
+ const { items: templateParameters, range: templateParametersRange } = parseTemplateParameterList();
478
+ const optionalExtends = parseOptionalUnionExtends();
479
+ const { items: options } = parseList(ListKind.UnionVariants, parseUnionVariant);
480
+ return {
481
+ kind: SyntaxKind.UnionDeclarationExpression,
482
+ id,
483
+ templateParameters,
484
+ templateParametersRange,
485
+ extends: optionalExtends,
486
+ decorators,
487
+ modifiers: [],
488
+ modifierFlags: ModifierFlags.None,
489
+ options,
490
+ ...finishNode(pos),
491
+ };
492
+ }
469
493
  function parseIdOrValueForVariant() {
470
494
  const nextToken = token();
471
495
  let id;
@@ -611,35 +635,74 @@ function createParser(code, options = {}) {
611
635
  parseExpected(Token.ModelKeyword);
612
636
  const id = parseIdentifier();
613
637
  const { items: templateParameters, range: templateParametersRange } = parseTemplateParameterList();
638
+ const { extends: optionalExtends, is: optionalIs, properties, bodyRange } = parseModelBody();
639
+ return {
640
+ kind: SyntaxKind.ModelStatement,
641
+ id,
642
+ extends: optionalExtends,
643
+ is: optionalIs,
644
+ templateParameters,
645
+ templateParametersRange,
646
+ decorators,
647
+ properties,
648
+ bodyRange,
649
+ modifiers,
650
+ modifierFlags: modifiersToFlags(modifiers),
651
+ ...finishNode(pos),
652
+ };
653
+ }
654
+ function parseModelDeclarationExpression(pos, decorators = []) {
655
+ parseExpected(Token.ModelKeyword);
656
+ const id = parseOptionalDeclarationExpressionIdentifier();
657
+ const { items: templateParameters, range: templateParametersRange } = parseTemplateParameterList();
658
+ const { extends: optionalExtends, is: optionalIs, properties, bodyRange, } = parseModelBody(true);
659
+ return {
660
+ kind: SyntaxKind.ModelDeclarationExpression,
661
+ id,
662
+ extends: optionalExtends,
663
+ is: optionalIs,
664
+ templateParameters,
665
+ templateParametersRange,
666
+ decorators,
667
+ properties,
668
+ bodyRange,
669
+ modifiers: [],
670
+ modifierFlags: ModifierFlags.None,
671
+ ...finishNode(pos),
672
+ };
673
+ }
674
+ function parseModelBody(inExpressionPosition = false) {
614
675
  expectTokenIsOneOf(Token.OpenBrace, Token.Equals, Token.ExtendsKeyword, Token.IsKeyword);
615
676
  const optionalExtends = parseOptionalModelExtends();
616
677
  const optionalIs = optionalExtends ? undefined : parseOptionalModelIs();
617
678
  let propDetail = createEmptyList();
618
679
  if (optionalIs) {
619
- const tok = expectTokenIsOneOf(Token.Semicolon, Token.OpenBrace);
620
- if (tok === Token.Semicolon) {
621
- nextToken();
680
+ if (inExpressionPosition) {
681
+ // In expression position there is no `;` terminator (it belongs to the enclosing
682
+ // construct): only parse a `{ ... }` body when present, otherwise the model has no
683
+ // own properties.
684
+ if (token() === Token.OpenBrace) {
685
+ propDetail = parseList(ListKind.ModelProperties, parseModelPropertyOrSpread);
686
+ }
622
687
  }
623
688
  else {
624
- propDetail = parseList(ListKind.ModelProperties, parseModelPropertyOrSpread);
689
+ const tok = expectTokenIsOneOf(Token.Semicolon, Token.OpenBrace);
690
+ if (tok === Token.Semicolon) {
691
+ nextToken();
692
+ }
693
+ else {
694
+ propDetail = parseList(ListKind.ModelProperties, parseModelPropertyOrSpread);
695
+ }
625
696
  }
626
697
  }
627
698
  else {
628
699
  propDetail = parseList(ListKind.ModelProperties, parseModelPropertyOrSpread);
629
700
  }
630
701
  return {
631
- kind: SyntaxKind.ModelStatement,
632
- id,
633
702
  extends: optionalExtends,
634
703
  is: optionalIs,
635
- templateParameters,
636
- templateParametersRange,
637
- decorators,
638
704
  properties: propDetail.items,
639
705
  bodyRange: propDetail.range,
640
- modifiers,
641
- modifierFlags: modifiersToFlags(modifiers),
642
- ...finishNode(pos),
643
706
  };
644
707
  }
645
708
  function parseOptionalModelExtends() {
@@ -789,13 +852,38 @@ function createParser(code, options = {}) {
789
852
  ...finishNode(pos),
790
853
  };
791
854
  }
855
+ function parseScalarDeclarationExpression(pos, decorators = []) {
856
+ parseExpected(Token.ScalarKeyword);
857
+ const id = parseOptionalDeclarationExpressionIdentifier();
858
+ const { items: templateParameters, range: templateParametersRange } = parseTemplateParameterList();
859
+ const optionalExtends = parseOptionalScalarExtends();
860
+ const { items: members, range: bodyRange } = parseScalarMembers(true);
861
+ return {
862
+ kind: SyntaxKind.ScalarDeclarationExpression,
863
+ id,
864
+ templateParameters,
865
+ templateParametersRange,
866
+ extends: optionalExtends,
867
+ members,
868
+ bodyRange,
869
+ decorators,
870
+ modifiers: [],
871
+ modifierFlags: ModifierFlags.None,
872
+ ...finishNode(pos),
873
+ };
874
+ }
792
875
  function parseOptionalScalarExtends() {
793
876
  if (parseOptional(Token.ExtendsKeyword)) {
794
877
  return parseReferenceExpression();
795
878
  }
796
879
  return undefined;
797
880
  }
798
- function parseScalarMembers() {
881
+ function parseScalarMembers(inExpressionPosition = false) {
882
+ // In expression position there is no `;` terminator: only parse a `{ ... }` body
883
+ // when present, otherwise the scalar has no members.
884
+ if (inExpressionPosition && token() !== Token.OpenBrace) {
885
+ return createEmptyList();
886
+ }
799
887
  if (token() === Token.Semicolon) {
800
888
  nextToken();
801
889
  return createEmptyList();
@@ -830,6 +918,20 @@ function createParser(code, options = {}) {
830
918
  ...finishNode(pos),
831
919
  };
832
920
  }
921
+ function parseEnumDeclarationExpression(pos, decorators = []) {
922
+ parseExpected(Token.EnumKeyword);
923
+ const id = parseOptionalDeclarationExpressionIdentifier();
924
+ const { items: members } = parseList(ListKind.EnumMembers, parseEnumMemberOrSpread);
925
+ return {
926
+ kind: SyntaxKind.EnumDeclarationExpression,
927
+ id,
928
+ decorators,
929
+ modifiers: [],
930
+ modifierFlags: ModifierFlags.None,
931
+ members,
932
+ ...finishNode(pos),
933
+ };
934
+ }
833
935
  function parseEnumMemberOrSpread(pos, decorators) {
834
936
  return token() === Token.Ellipsis
835
937
  ? parseEnumSpreadMember(pos, decorators)
@@ -1277,6 +1379,35 @@ function createParser(code, options = {}) {
1277
1379
  }
1278
1380
  return base;
1279
1381
  }
1382
+ /**
1383
+ * Parse a declaration used in expression position (`model`/`enum`/`union`/`scalar`).
1384
+ * {@link pos} should point at the start of the node (the first decorator's `@` when
1385
+ * {@link decorators} are provided, otherwise the keyword). A leading doc comment
1386
+ * ({@link docs}) is attached to the node so it is applied just like an `@doc` decorator.
1387
+ */
1388
+ function parseDeclarationExpression(pos, decorators = [], docs = []) {
1389
+ let node;
1390
+ switch (token()) {
1391
+ case Token.ModelKeyword:
1392
+ node = parseModelDeclarationExpression(pos, decorators);
1393
+ break;
1394
+ case Token.EnumKeyword:
1395
+ node = parseEnumDeclarationExpression(pos, decorators);
1396
+ break;
1397
+ case Token.UnionKeyword:
1398
+ node = parseUnionDeclarationExpression(pos, decorators);
1399
+ break;
1400
+ case Token.ScalarKeyword:
1401
+ node = parseScalarDeclarationExpression(pos, decorators);
1402
+ break;
1403
+ default:
1404
+ compilerAssert(false, "Expected a declaration expression keyword.");
1405
+ }
1406
+ if (docs.length > 0) {
1407
+ mutate(node).docs = docs;
1408
+ }
1409
+ return node;
1410
+ }
1280
1411
  function parsePrimaryExpression() {
1281
1412
  while (true) {
1282
1413
  switch (token()) {
@@ -1297,14 +1428,36 @@ function createParser(code, options = {}) {
1297
1428
  return parseNumericLiteral();
1298
1429
  case Token.OpenBrace:
1299
1430
  return parseModelExpression();
1431
+ case Token.ModelKeyword:
1432
+ case Token.EnumKeyword:
1433
+ case Token.UnionKeyword:
1434
+ case Token.ScalarKeyword: {
1435
+ // A leading doc comment applies to the declaration expression, just like `@doc`.
1436
+ // `pos` starts at the doc comment so the resulting node span includes it.
1437
+ const [pos, docs] = parseDocList();
1438
+ return parseDeclarationExpression(pos, [], docs);
1439
+ }
1300
1440
  case Token.OpenBracket:
1301
1441
  return parseTupleExpression();
1302
1442
  case Token.OpenParen:
1303
1443
  return parseParenthesizedExpression();
1304
- case Token.At:
1444
+ case Token.At: {
1445
+ // Capture a doc comment that precedes the decorators before they are parsed
1446
+ // (parsing decorators advances the scanner and clears the pending doc range).
1447
+ const [docPos, docs] = parseDocList();
1448
+ // `pos` starts at the doc comment (if any) or the `@` so the resulting node
1449
+ // span includes the docs and decorators.
1450
+ const pos = docs.length > 0 ? docPos : tokenPos();
1305
1451
  const decorators = parseDecoratorList();
1452
+ // Decorators are only valid in expression position when they decorate a
1453
+ // declaration expression (the keyword forms below). `pos` starts at the `@`
1454
+ // so the resulting node span includes the decorators.
1455
+ if (isDeclarationExpressionStatementKeyword(token())) {
1456
+ return parseDeclarationExpression(pos, decorators, docs);
1457
+ }
1306
1458
  reportInvalidDecorators(decorators, "expression");
1307
1459
  continue;
1460
+ }
1308
1461
  case Token.Hash:
1309
1462
  const directives = parseDirectiveList();
1310
1463
  reportInvalidDirective(directives, "expression");
@@ -1533,6 +1686,13 @@ function createParser(code, options = {}) {
1533
1686
  ...finishNode(pos),
1534
1687
  };
1535
1688
  }
1689
+ /**
1690
+ * Parse the optional identifier of a declaration used in expression position. The name
1691
+ * is only parsed when actually present; an anonymous declaration expression has no `id`.
1692
+ */
1693
+ function parseOptionalDeclarationExpressionIdentifier() {
1694
+ return token() === Token.Identifier ? parseIdentifier() : undefined;
1695
+ }
1536
1696
  function parseIdentifier(options) {
1537
1697
  if (isKeyword(token())) {
1538
1698
  if (!(isModifier(token()) && options?.allowReservedIdentifier)) {
@@ -1721,15 +1881,26 @@ function createParser(code, options = {}) {
1721
1881
  if (docRanges.length === 0 || options.docs === false) {
1722
1882
  return [tokenPos(), []];
1723
1883
  }
1884
+ return [docRanges[0].pos, parseDocRanges(docRanges)];
1885
+ }
1886
+ /**
1887
+ * Parse the given doc comment ranges into doc nodes. Unlike {@link parseDocList} this does
1888
+ * not read the pending {@link docRanges}, so it can be used to attach doc comments that were
1889
+ * captured earlier (e.g. before a list element's decorators were parsed).
1890
+ */
1891
+ function parseDocRanges(ranges) {
1892
+ if (ranges.length === 0 || options.docs === false) {
1893
+ return [];
1894
+ }
1724
1895
  const docs = [];
1725
- for (const range of docRanges) {
1896
+ for (const range of ranges) {
1726
1897
  const doc = parseRange(ParseMode.Doc, innerDocRange(range), () => parseDoc(range));
1727
1898
  docs.push(doc);
1728
1899
  if (range.comment) {
1729
1900
  mutate(range.comment).parsedAsDocs = true;
1730
1901
  }
1731
1902
  }
1732
- return [docRanges[0].pos, docs];
1903
+ return docs;
1733
1904
  }
1734
1905
  function parseDoc(range) {
1735
1906
  const content = [];
@@ -2065,14 +2236,28 @@ function createParser(code, options = {}) {
2065
2236
  }
2066
2237
  while (true) {
2067
2238
  const startingPos = tokenPos();
2239
+ // Capture any pending doc comment before `parseAnnotations` parses (and clears the
2240
+ // pending range for) decorators, so a doc comment preceding a decorated declaration
2241
+ // expression element can still be attached to it below.
2242
+ const pendingDocRanges = kind.allowDeclarationExpression ? docRanges : undefined;
2068
2243
  const { pos, docs, directives, decorators } = parseAnnotations({
2069
2244
  skipParsingDocNodes: Boolean(kind.invalidAnnotationTarget),
2070
2245
  });
2246
+ // A declaration expression element (e.g. `enum { a }`) can carry decorators (and a
2247
+ // doc comment) even in an expression list. Attach them to the declaration expression
2248
+ // instead of rejecting them as an invalid annotation target.
2249
+ const declarationExpressionWithDecorators = kind.allowDeclarationExpression &&
2250
+ decorators.length > 0 &&
2251
+ isDeclarationExpressionStatementKeyword(token());
2071
2252
  if (kind.invalidAnnotationTarget) {
2072
- reportInvalidDecorators(decorators, kind.invalidAnnotationTarget);
2073
2253
  reportInvalidDirective(directives, kind.invalidAnnotationTarget);
2254
+ if (!declarationExpressionWithDecorators) {
2255
+ reportInvalidDecorators(decorators, kind.invalidAnnotationTarget);
2256
+ }
2074
2257
  }
2075
- if (directives.length === 0 && decorators.length === 0 && atEndOfListWithError(kind)) {
2258
+ if (directives.length === 0 &&
2259
+ decorators.length === 0 &&
2260
+ atEndOfListWithError(kind, { atElementStart: true })) {
2076
2261
  // Error recovery: end surrounded list at statement keyword or end
2077
2262
  // of file. Note, however, that we must parse a missing element if
2078
2263
  // there were directives or decorators as we cannot drop those from
@@ -2083,7 +2268,14 @@ function createParser(code, options = {}) {
2083
2268
  break;
2084
2269
  }
2085
2270
  let item;
2086
- if (kind.invalidAnnotationTarget) {
2271
+ if (declarationExpressionWithDecorators) {
2272
+ const declDocs = pendingDocRanges ? parseDocRanges(pendingDocRanges) : [];
2273
+ // When a doc comment precedes the decorators, start the node span at the doc
2274
+ // comment so the doc nodes stay within the parent node's range.
2275
+ const declPos = declDocs.length > 0 ? pendingDocRanges[0].pos : pos;
2276
+ item = parseDeclarationExpression(declPos, decorators, declDocs);
2277
+ }
2278
+ else if (kind.invalidAnnotationTarget) {
2087
2279
  item = parseItem();
2088
2280
  }
2089
2281
  else {
@@ -2114,7 +2306,7 @@ function createParser(code, options = {}) {
2114
2306
  // reach the close token.
2115
2307
  break;
2116
2308
  }
2117
- else if (atEndOfListWithError(kind)) {
2309
+ else if (atEndOfListWithError(kind, { atElementStart: false })) {
2118
2310
  // Error recovery: If a list *is* surrounded by punctuation, then
2119
2311
  // the list ends at statement keyword or end-of-file under the
2120
2312
  // assumption that the closing delimiter is missing. This check is
@@ -2172,10 +2364,28 @@ function createParser(code, options = {}) {
2172
2364
  }
2173
2365
  return false;
2174
2366
  }
2175
- function atEndOfListWithError(kind) {
2367
+ /**
2368
+ * Whether the parser is at a position where the current list (when surrounded by
2369
+ * punctuation) should be ended under the assumption that its closing token is missing —
2370
+ * i.e. the current token is a statement keyword or end-of-file.
2371
+ *
2372
+ * @param atElementStart Whether we are about to parse a new element (`true`) or have just
2373
+ * parsed one and found neither a delimiter nor the close token (`false`). A
2374
+ * declaration-expression keyword (`model`/`enum`/`union`/`scalar`) is a valid element
2375
+ * start in lists that {@link ListKind.allowDeclarationExpression allow declaration
2376
+ * expressions}, so it must not end the list at the start of an element. After an element,
2377
+ * a declaration-expression keyword cannot start a new element (a delimiter is required
2378
+ * first), so it is treated like any other statement keyword and ends the list — restoring
2379
+ * error recovery for an unclosed delimiter (e.g. `@foo(model X {}` followed by a
2380
+ * statement).
2381
+ */
2382
+ function atEndOfListWithError(kind, { atElementStart }) {
2176
2383
  return (kind.close !== Token.None &&
2177
2384
  (isStatementKeyword(token()) || token() === Token.EndOfFile) &&
2178
- token() !== kind.allowedStatementKeyword);
2385
+ token() !== kind.allowedStatementKeyword &&
2386
+ !(atElementStart &&
2387
+ kind.allowDeclarationExpression &&
2388
+ isDeclarationExpressionStatementKeyword(token())));
2179
2389
  }
2180
2390
  function parseEmptyStatement(pos) {
2181
2391
  parseExpected(Token.Semicolon);
@@ -2402,6 +2612,13 @@ export function visitChildren(node, cb) {
2402
2612
  visitNode(cb, node.extends) ||
2403
2613
  visitNode(cb, node.is) ||
2404
2614
  visitEach(cb, node.properties));
2615
+ case SyntaxKind.ModelDeclarationExpression:
2616
+ return (visitEach(cb, node.decorators) ||
2617
+ visitNode(cb, node.id) ||
2618
+ visitEach(cb, node.templateParameters) ||
2619
+ visitNode(cb, node.extends) ||
2620
+ visitNode(cb, node.is) ||
2621
+ visitEach(cb, node.properties));
2405
2622
  case SyntaxKind.ScalarStatement:
2406
2623
  return (visitEach(cb, node.modifiers) ||
2407
2624
  visitEach(cb, node.decorators) ||
@@ -2409,6 +2626,12 @@ export function visitChildren(node, cb) {
2409
2626
  visitEach(cb, node.templateParameters) ||
2410
2627
  visitEach(cb, node.members) ||
2411
2628
  visitNode(cb, node.extends));
2629
+ case SyntaxKind.ScalarDeclarationExpression:
2630
+ return (visitEach(cb, node.decorators) ||
2631
+ visitNode(cb, node.id) ||
2632
+ visitEach(cb, node.templateParameters) ||
2633
+ visitEach(cb, node.members) ||
2634
+ visitNode(cb, node.extends));
2412
2635
  case SyntaxKind.ScalarConstructor:
2413
2636
  return visitNode(cb, node.id) || visitEach(cb, node.parameters);
2414
2637
  case SyntaxKind.UnionStatement:
@@ -2418,6 +2641,12 @@ export function visitChildren(node, cb) {
2418
2641
  visitEach(cb, node.templateParameters) ||
2419
2642
  visitNode(cb, node.extends) ||
2420
2643
  visitEach(cb, node.options));
2644
+ case SyntaxKind.UnionDeclarationExpression:
2645
+ return (visitEach(cb, node.decorators) ||
2646
+ visitNode(cb, node.id) ||
2647
+ visitEach(cb, node.templateParameters) ||
2648
+ visitNode(cb, node.extends) ||
2649
+ visitEach(cb, node.options));
2421
2650
  case SyntaxKind.UnionVariant:
2422
2651
  return visitEach(cb, node.decorators) || visitNode(cb, node.id) || visitNode(cb, node.value);
2423
2652
  case SyntaxKind.EnumStatement:
@@ -2425,6 +2654,8 @@ export function visitChildren(node, cb) {
2425
2654
  visitEach(cb, node.decorators) ||
2426
2655
  visitNode(cb, node.id) ||
2427
2656
  visitEach(cb, node.members));
2657
+ case SyntaxKind.EnumDeclarationExpression:
2658
+ return (visitEach(cb, node.decorators) || visitNode(cb, node.id) || visitEach(cb, node.members));
2428
2659
  case SyntaxKind.EnumMember:
2429
2660
  return visitEach(cb, node.decorators) || visitNode(cb, node.id) || visitNode(cb, node.value);
2430
2661
  case SyntaxKind.EnumSpreadMember:
@@ -2698,6 +2929,9 @@ export function getIdentifierContext(id) {
2698
2929
  case SyntaxKind.ModelStatement:
2699
2930
  kind = IdentifierKind.ModelStatementProperty;
2700
2931
  break;
2932
+ case SyntaxKind.ModelDeclarationExpression:
2933
+ kind = IdentifierKind.ModelStatementProperty;
2934
+ break;
2701
2935
  default:
2702
2936
  compilerAssert("false", "ModelProperty with unexpected parent kind.");
2703
2937
  kind =