@typespec/compiler 1.13.0-dev.0 → 1.13.0-dev.2

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 (58) hide show
  1. package/dist/manifest.js +2 -2
  2. package/dist/src/config/config-loader.d.ts.map +1 -1
  3. package/dist/src/config/config-loader.js +18 -1
  4. package/dist/src/config/config-loader.js.map +1 -1
  5. package/dist/src/config/config-schema.d.ts.map +1 -1
  6. package/dist/src/config/config-schema.js +9 -0
  7. package/dist/src/config/config-schema.js.map +1 -1
  8. package/dist/src/config/config-to-options.d.ts.map +1 -1
  9. package/dist/src/config/config-to-options.js +35 -0
  10. package/dist/src/config/config-to-options.js.map +1 -1
  11. package/dist/src/config/types.d.ts +18 -0
  12. package/dist/src/config/types.d.ts.map +1 -1
  13. package/dist/src/core/checker.d.ts.map +1 -1
  14. package/dist/src/core/checker.js +818 -44
  15. package/dist/src/core/checker.js.map +1 -1
  16. package/dist/src/core/entrypoint-resolution.d.ts.map +1 -1
  17. package/dist/src/core/entrypoint-resolution.js +7 -0
  18. package/dist/src/core/entrypoint-resolution.js.map +1 -1
  19. package/dist/src/core/helpers/string-template-utils.d.ts.map +1 -1
  20. package/dist/src/core/helpers/string-template-utils.js +1 -0
  21. package/dist/src/core/helpers/string-template-utils.js.map +1 -1
  22. package/dist/src/core/helpers/syntax-utils.d.ts +1 -0
  23. package/dist/src/core/helpers/syntax-utils.d.ts.map +1 -1
  24. package/dist/src/core/helpers/syntax-utils.js +3 -0
  25. package/dist/src/core/helpers/syntax-utils.js.map +1 -1
  26. package/dist/src/core/helpers/type-name-utils.d.ts.map +1 -1
  27. package/dist/src/core/helpers/type-name-utils.js +2 -0
  28. package/dist/src/core/helpers/type-name-utils.js.map +1 -1
  29. package/dist/src/core/messages.d.ts +56 -2
  30. package/dist/src/core/messages.d.ts.map +1 -1
  31. package/dist/src/core/messages.js +18 -0
  32. package/dist/src/core/messages.js.map +1 -1
  33. package/dist/src/core/name-resolver.d.ts +2 -0
  34. package/dist/src/core/name-resolver.d.ts.map +1 -1
  35. package/dist/src/core/name-resolver.js +35 -2
  36. package/dist/src/core/name-resolver.js.map +1 -1
  37. package/dist/src/core/semantic-walker.d.ts.map +1 -1
  38. package/dist/src/core/semantic-walker.js +14 -0
  39. package/dist/src/core/semantic-walker.js.map +1 -1
  40. package/dist/src/core/type-relation-checker.d.ts.map +1 -1
  41. package/dist/src/core/type-relation-checker.js +2 -1
  42. package/dist/src/core/type-relation-checker.js.map +1 -1
  43. package/dist/src/core/types.d.ts +14 -1
  44. package/dist/src/core/types.d.ts.map +1 -1
  45. package/dist/src/core/types.js.map +1 -1
  46. package/dist/src/init/scaffold.js +16 -11
  47. package/dist/src/init/scaffold.js.map +1 -1
  48. package/dist/src/server/entrypoint-resolver.d.ts.map +1 -1
  49. package/dist/src/server/entrypoint-resolver.js +13 -0
  50. package/dist/src/server/entrypoint-resolver.js.map +1 -1
  51. package/dist/src/server/serverlib.d.ts.map +1 -1
  52. package/dist/src/server/serverlib.js +1 -1
  53. package/dist/src/server/serverlib.js.map +1 -1
  54. package/dist/src/server/type-signature.js +17 -1
  55. package/dist/src/server/type-signature.js.map +1 -1
  56. package/package.json +1 -1
  57. package/templates/__snapshots__/rest/tspconfig.yaml +10 -8
  58. package/templates/scaffolding.json +3 -0
@@ -11,7 +11,7 @@ import { compilerAssert, createDiagnosticCollector, ignoreDiagnostics, reportDep
11
11
  import { validateInheritanceDiscriminatedUnions } from "./helpers/discriminator-utils.js";
12
12
  import { getLocationContext } from "./helpers/location-context.js";
13
13
  import { explainStringTemplateNotSerializable } from "./helpers/string-template-utils.js";
14
- import { typeReferenceToString } from "./helpers/syntax-utils.js";
14
+ import { printIdentifier, printMemberExpressionPath, typeReferenceToString, } from "./helpers/syntax-utils.js";
15
15
  import { getEntityName, getTypeName } from "./helpers/type-name-utils.js";
16
16
  import { marshalTypeForJs, unmarshalJsToValue } from "./js-marshaller.js";
17
17
  import { createDiagnostic } from "./messages.js";
@@ -163,6 +163,10 @@ export function createChecker(program, resolver) {
163
163
  * Tracking the template parameters used or not.
164
164
  */
165
165
  const templateParameterUsageMap = new Map();
166
+ const templateAccessSymbolCache = new Map();
167
+ const templateAccessCacheKeys = new WeakMap();
168
+ const symbolCacheIds = new WeakMap();
169
+ let nextSymbolCacheId = 1;
166
170
  const checker = {
167
171
  getTypeForNode,
168
172
  checkProgram,
@@ -344,7 +348,7 @@ export function createChecker(program, resolver) {
344
348
  }));
345
349
  return errorType;
346
350
  }
347
- if (entity.kind === "TemplateParameter") {
351
+ if (entity.kind === "TemplateParameter" || entity.kind === "TemplateParameterAccess") {
348
352
  if (entity.constraint?.valueType) {
349
353
  // means this template constraint will accept values
350
354
  reportCheckerDiagnostic(createDiagnostic({
@@ -380,7 +384,7 @@ export function createChecker(program, resolver) {
380
384
  // If a template parameter that can be a value is used where a value is expected,
381
385
  // synthesize a template value placeholder even when the template parameter is mapped
382
386
  // from an outer template declaration.
383
- if (entity.kind === "TemplateParameter" &&
387
+ if ((entity.kind === "TemplateParameter" || entity.kind === "TemplateParameterAccess") &&
384
388
  entity.constraint?.valueType &&
385
389
  entity.constraint.type === undefined) {
386
390
  // We must also observe that the template parameter is used here.
@@ -706,7 +710,12 @@ export function createChecker(program, resolver) {
706
710
  function visit(node) {
707
711
  const entity = checkNode(ctx, node);
708
712
  let hasError = false;
709
- if (entity !== null && "kind" in entity && entity.kind === "TemplateParameter") {
713
+ if (entity !== null &&
714
+ "kind" in entity &&
715
+ (entity.kind === "TemplateParameter" || entity.kind === "TemplateParameterAccess")) {
716
+ if (entity.kind === "TemplateParameterAccess") {
717
+ return entity;
718
+ }
710
719
  for (let i = index; i < templateParameters.length; i++) {
711
720
  if (entity.node?.symbol === templateParameters[i].symbol) {
712
721
  reportCheckerDiagnostic(createDiagnostic({ code: "invalid-template-default", target: node }));
@@ -1514,7 +1523,7 @@ export function createChecker(program, resolver) {
1514
1523
  const indexers = [];
1515
1524
  const modelOptions = options.filter((entry) => {
1516
1525
  const [optionNode, option] = entry;
1517
- if (option.kind === "TemplateParameter") {
1526
+ if (option.kind === "TemplateParameter" || option.kind === "TemplateParameterAccess") {
1518
1527
  return false;
1519
1528
  }
1520
1529
  if (option.kind !== "Model") {
@@ -1726,6 +1735,10 @@ export function createChecker(program, resolver) {
1726
1735
  const doc = paramDocs.get(name);
1727
1736
  if (doc) {
1728
1737
  docFromCommentForSym.set(memberSym, doc);
1738
+ const declarationSymbol = memberSym.node?.symbol;
1739
+ if (declarationSymbol && declarationSymbol !== memberSym) {
1740
+ docFromCommentForSym.set(declarationSymbol, doc);
1741
+ }
1729
1742
  }
1730
1743
  }
1731
1744
  }
@@ -1896,15 +1909,24 @@ export function createChecker(program, resolver) {
1896
1909
  compilerAssert(false, "Unreachable");
1897
1910
  }
1898
1911
  if (sym) {
1899
- if (sym.symbolSource) {
1900
- return [sym.symbolSource];
1901
- }
1902
- else {
1903
- return [sym];
1912
+ const relatedSource = getRelatedSymbolSource(sym);
1913
+ if (relatedSource) {
1914
+ return sym.flags & 4194304 /* SymbolFlags.LateBound */ ? [sym, relatedSource] : [relatedSource];
1904
1915
  }
1916
+ return [sym];
1905
1917
  }
1906
1918
  return undefined; //sym?.symbolSource ?? sym;
1907
1919
  }
1920
+ function getRelatedSymbolSource(sym) {
1921
+ if (sym.symbolSource) {
1922
+ return sym.symbolSource;
1923
+ }
1924
+ const type = sym.type;
1925
+ if (type && isType(type)) {
1926
+ return getTemplateAccessSymbolSource(type);
1927
+ }
1928
+ return undefined;
1929
+ }
1908
1930
  function getTemplateDeclarationsForArgument(ctx, node) {
1909
1931
  const ref = node.parent;
1910
1932
  let resolved = resolveTypeReferenceSym(ctx, ref, false);
@@ -2186,32 +2208,43 @@ export function createChecker(program, resolver) {
2186
2208
  }
2187
2209
  }
2188
2210
  else if (identifier.parent && identifier.parent.kind === SyntaxKind.MemberExpression) {
2189
- let base = resolver.getNodeLinks(identifier.parent.base).resolvedSymbol;
2190
- if (base) {
2191
- if (base.flags & 128 /* SymbolFlags.Alias */) {
2192
- base = getAliasedSymbol(CheckContext.DEFAULT, base);
2211
+ const memberExpression = identifier.parent;
2212
+ let baseType = getCompletionBaseType(memberExpression.base);
2213
+ let base = resolver.getNodeLinks(memberExpression.base).resolvedSymbol;
2214
+ if (base && base.flags & 128 /* SymbolFlags.Alias */) {
2215
+ base = getAliasedSymbol(CheckContext.DEFAULT, base);
2216
+ }
2217
+ if (!baseType && base) {
2218
+ baseType = getCompletionBaseTypeFromSymbol(base);
2219
+ }
2220
+ if (baseType && (!base || !!(base.flags & 1024 /* SymbolFlags.TemplateParameter */))) {
2221
+ if (memberExpression.selector === "::") {
2222
+ addMetaCompletionsForType(baseType);
2193
2223
  }
2194
- if (base) {
2195
- if (identifier.parent.selector === "::") {
2196
- if (base?.node === undefined && base?.declarations && base.declarations.length > 0) {
2197
- // Process meta properties separately, such as `::parameters`, `::returnType`
2198
- const nodeModels = base?.declarations[0];
2199
- if (nodeModels.kind === SyntaxKind.OperationStatement) {
2200
- const operation = nodeModels;
2201
- addCompletion("parameters", operation.symbol);
2202
- addCompletion("returnType", operation.symbol);
2203
- }
2204
- }
2205
- else if (base?.node?.kind === SyntaxKind.ModelProperty) {
2206
- // Process meta properties separately, such as `::type`
2207
- const metaProperty = base.node;
2208
- addCompletion("type", metaProperty.symbol);
2224
+ else {
2225
+ addMemberCompletionsForType(baseType);
2226
+ }
2227
+ }
2228
+ if (base) {
2229
+ if (memberExpression.selector === "::") {
2230
+ if (base?.node === undefined && base?.declarations && base.declarations.length > 0) {
2231
+ // Process meta properties separately, such as `::parameters`, `::returnType`
2232
+ const nodeModels = base?.declarations[0];
2233
+ if (nodeModels.kind === SyntaxKind.OperationStatement) {
2234
+ const operation = nodeModels;
2235
+ addCompletion("parameters", operation.symbol);
2236
+ addCompletion("returnType", operation.symbol);
2209
2237
  }
2210
2238
  }
2211
- else {
2212
- addCompletions(base.exports ?? base.members);
2239
+ else if (base?.node?.kind === SyntaxKind.ModelProperty) {
2240
+ // Process meta properties separately, such as `::type`
2241
+ const metaProperty = base.node;
2242
+ addCompletion("type", metaProperty.symbol);
2213
2243
  }
2214
2244
  }
2245
+ else {
2246
+ addCompletions(base.exports ?? base.members);
2247
+ }
2215
2248
  }
2216
2249
  }
2217
2250
  else {
@@ -2252,6 +2285,101 @@ export function createChecker(program, resolver) {
2252
2285
  }
2253
2286
  }
2254
2287
  return completions;
2288
+ /** Resolve a usable base type for member/meta-member completions. */
2289
+ function getCompletionBaseType(base) {
2290
+ const entity = getTypeOrValueForNode(base, CheckContext.DEFAULT);
2291
+ if (!entity || !isType(entity) || isErrorType(entity)) {
2292
+ if (base.kind === SyntaxKind.Identifier) {
2293
+ const scopedTemplateParameter = getTemplateParameterTypeFromScope(base);
2294
+ if (scopedTemplateParameter) {
2295
+ return resolveTemplateConstraintType(scopedTemplateParameter);
2296
+ }
2297
+ }
2298
+ const templateBase = probeTemplateAccessBaseEntity(base);
2299
+ return templateBase ? resolveTemplateConstraintType(templateBase) : undefined;
2300
+ }
2301
+ if (isTemplateAccessType(entity)) {
2302
+ return resolveTemplateConstraintType(entity);
2303
+ }
2304
+ return entity;
2305
+ }
2306
+ /** Resolve a completion base type from a symbol when node-based typing is unavailable. */
2307
+ function getCompletionBaseTypeFromSymbol(base) {
2308
+ if (base.flags & 4194304 /* SymbolFlags.LateBound */) {
2309
+ const lateBoundType = base.type;
2310
+ return lateBoundType && isType(lateBoundType)
2311
+ ? resolveCompletionType(lateBoundType)
2312
+ : undefined;
2313
+ }
2314
+ if (base.flags & 1024 /* SymbolFlags.TemplateParameter */) {
2315
+ const mapped = checkTemplateParameterDeclaration(CheckContext.DEFAULT, getSymNode(base));
2316
+ return isType(mapped) ? resolveCompletionType(mapped) : undefined;
2317
+ }
2318
+ return undefined;
2319
+ }
2320
+ /** Normalize template access types to their effective constraint for completion. */
2321
+ function resolveCompletionType(type) {
2322
+ return isTemplateAccessType(type) ? resolveTemplateConstraintType(type) : type;
2323
+ }
2324
+ /** Add member completions based on the resolved base type kind. */
2325
+ function addMemberCompletionsForType(baseType) {
2326
+ switch (baseType.kind) {
2327
+ case "Model":
2328
+ for (const property of walkPropertiesInherited(baseType)) {
2329
+ const ownerSymbol = baseType.node?.symbol;
2330
+ const propertySymbol = (ownerSymbol ? getMemberSymbol(ownerSymbol, property.name) : undefined) ??
2331
+ property.node?.symbol;
2332
+ if (propertySymbol) {
2333
+ addCompletion(property.name, propertySymbol);
2334
+ }
2335
+ }
2336
+ return;
2337
+ case "Interface":
2338
+ for (const [name, operation] of baseType.operations) {
2339
+ const operationSymbol = operation.node?.symbol;
2340
+ if (operationSymbol) {
2341
+ addCompletion(name, operationSymbol);
2342
+ }
2343
+ }
2344
+ return;
2345
+ case "Enum":
2346
+ for (const [name, member] of baseType.members) {
2347
+ const enumMemberSymbol = member.node?.symbol;
2348
+ if (enumMemberSymbol) {
2349
+ addCompletion(name, enumMemberSymbol);
2350
+ }
2351
+ }
2352
+ return;
2353
+ case "Union":
2354
+ for (const [name, variant] of baseType.variants) {
2355
+ if (typeof name === "string" && variant.node?.symbol) {
2356
+ addCompletion(name, variant.node.symbol);
2357
+ }
2358
+ }
2359
+ return;
2360
+ case "Scalar":
2361
+ for (const [name, constructor] of baseType.constructors) {
2362
+ const constructorSymbol = constructor.node?.symbol;
2363
+ if (constructorSymbol) {
2364
+ addCompletion(name, constructorSymbol);
2365
+ }
2366
+ }
2367
+ return;
2368
+ case "Namespace":
2369
+ addCompletions(baseType.node?.symbol?.exports);
2370
+ return;
2371
+ }
2372
+ }
2373
+ /** Add `::` meta-member completions for the resolved base type. */
2374
+ function addMetaCompletionsForType(baseType) {
2375
+ const baseSymbol = getTypeSymbol(baseType);
2376
+ if (!baseSymbol) {
2377
+ return;
2378
+ }
2379
+ for (const metaMemberName of resolver.getMetaMemberNames(baseSymbol)) {
2380
+ addCompletion(metaMemberName, baseSymbol);
2381
+ }
2382
+ }
2255
2383
  function addCompletions(table) {
2256
2384
  if (!table) {
2257
2385
  return;
@@ -2273,7 +2401,7 @@ export function createChecker(program, resolver) {
2273
2401
  }
2274
2402
  }
2275
2403
  function addCompletion(key, sym, options = {}) {
2276
- if (sym.symbolSource) {
2404
+ if (sym.symbolSource && !(sym.flags & 4194304 /* SymbolFlags.LateBound */)) {
2277
2405
  sym = sym.symbolSource;
2278
2406
  }
2279
2407
  if (!shouldAddCompletion(sym)) {
@@ -2330,7 +2458,7 @@ export function createChecker(program, resolver) {
2330
2458
  }
2331
2459
  resolvedOptions.locationContext ??= getLocationContext(program, node);
2332
2460
  const sym = resolveTypeReferenceSymInternal(ctx, node, resolvedOptions);
2333
- if (!resolvedOptions.resolveDeclarationOfTemplate) {
2461
+ if (ctx.mapper === undefined && !resolvedOptions.resolveDeclarationOfTemplate) {
2334
2462
  referenceSymCache.set(node, sym);
2335
2463
  }
2336
2464
  return sym;
@@ -2373,6 +2501,10 @@ export function createChecker(program, resolver) {
2373
2501
  if (!base) {
2374
2502
  return undefined;
2375
2503
  }
2504
+ const directTemplateAccessSym = tryResolveTemplateAccessSymbol(ctx, node, base);
2505
+ if (directTemplateAccessSym) {
2506
+ return directTemplateAccessSym;
2507
+ }
2376
2508
  // when resolving a type reference based on an alias, unwrap the alias.
2377
2509
  if (base.flags & 128 /* SymbolFlags.Alias */) {
2378
2510
  if (!options.resolveDeclarationOfTemplate && isTemplatedNode(getSymNode(base))) {
@@ -2419,12 +2551,498 @@ export function createChecker(program, resolver) {
2419
2551
  }
2420
2552
  base = baseSym;
2421
2553
  }
2422
- const sym = resolveMemberInContainer(base, node, options);
2554
+ const templateAccessSym = tryResolveTemplateAccessSymbol(ctx, node, base);
2555
+ if (templateAccessSym) {
2556
+ return templateAccessSym;
2557
+ }
2558
+ const sym = resolveMemberInContainer(ctx, base, node, options);
2423
2559
  checkSymbolAccess(options.locationContext, node, sym);
2424
2560
  return sym;
2425
2561
  }
2426
2562
  compilerAssert(false, `Unknown type reference kind "${SyntaxKind[node.kind]}"`, node);
2427
2563
  }
2564
+ /**
2565
+ * Resolve member/meta-member access rooted in a template parameter or template access chain.
2566
+ * Falls back to late-bound symbols when the concrete symbol cannot be safely determined.
2567
+ *
2568
+ * @param ctx Check context for mapper and usage observation.
2569
+ * @param node Member expression being resolved.
2570
+ * @param baseSym Resolved symbol for the member expression base.
2571
+ * @returns The resolved symbol for the template access, or `undefined` when not applicable.
2572
+ */
2573
+ function tryResolveTemplateAccessSymbol(ctx, node, baseSym) {
2574
+ const mappedSymbol = tryResolveMappedTemplateAccessSymbol(ctx, node, baseSym);
2575
+ if (mappedSymbol) {
2576
+ return mappedSymbol;
2577
+ }
2578
+ const baseEntity = getTemplateAccessBaseEntity(ctx, node.base, baseSym);
2579
+ if (!baseEntity) {
2580
+ return undefined;
2581
+ }
2582
+ observeTemplateAccessBase(ctx, baseEntity);
2583
+ const accessedType = resolveTemplateAccessType(ctx, node, baseEntity);
2584
+ const useCache = ctx.mapper === undefined;
2585
+ if (isErrorType(accessedType)) {
2586
+ return createTemplateAccessSymbol(baseEntity, node, errorType, useCache);
2587
+ }
2588
+ if (node.selector === "." &&
2589
+ baseEntity.kind === "TemplateParameterAccess" &&
2590
+ baseEntity.node.selector === "::") {
2591
+ return getPreferredTemplateAccessSymbol(node, accessedType);
2592
+ }
2593
+ if (ctx.mapper !== undefined) {
2594
+ return getPreferredTemplateAccessSymbol(node, accessedType);
2595
+ }
2596
+ return createTemplateAccessSymbol(baseEntity, node, accessedType, useCache);
2597
+ }
2598
+ /**
2599
+ * Resolve template access directly from a mapped template argument when available.
2600
+ *
2601
+ * @param ctx Check context containing the active template mapper.
2602
+ * @param node Member expression being resolved.
2603
+ * @param baseSym Base symbol for the access expression.
2604
+ * @returns A concrete or late-bound symbol for the mapped access, or `undefined`.
2605
+ */
2606
+ function tryResolveMappedTemplateAccessSymbol(ctx, node, baseSym) {
2607
+ if (!ctx.mapper || !(baseSym.flags & 1024 /* SymbolFlags.TemplateParameter */)) {
2608
+ return undefined;
2609
+ }
2610
+ const declared = checkTemplateParameterDeclaration(CheckContext.DEFAULT, getSymNode(baseSym));
2611
+ if (!isType(declared) || declared.kind !== "TemplateParameter") {
2612
+ return undefined;
2613
+ }
2614
+ const mapped = ctx.mapper.getMappedType(declared);
2615
+ if (!isType(mapped) || isTemplateAccessType(mapped)) {
2616
+ return undefined;
2617
+ }
2618
+ if (isUninstantiatedTemplateType(mapped)) {
2619
+ return undefined;
2620
+ }
2621
+ const resolvedType = node.selector === "."
2622
+ ? resolveMemberTypeFromConstraint(mapped, node.id.sv)
2623
+ : resolveMetaTypeFromConstraint(ctx, mapped, node);
2624
+ if (!resolvedType) {
2625
+ return undefined;
2626
+ }
2627
+ return getPreferredTemplateAccessSymbol(node, resolvedType);
2628
+ }
2629
+ /** Return true when a type declaration is templated but has not been instantiated. */
2630
+ function isUninstantiatedTemplateType(type) {
2631
+ if ("templateMapper" in type && type.templateMapper !== undefined) {
2632
+ return false;
2633
+ }
2634
+ const node = type.node;
2635
+ return Boolean(node && "templateParameters" in node && node.templateParameters.length > 0);
2636
+ }
2637
+ /** Return true when the resolved type should remain late-bound due to templating. */
2638
+ function shouldUseLateBoundTemplateAccessType(type) {
2639
+ return "templateMapper" in type && type.templateMapper !== undefined;
2640
+ }
2641
+ /**
2642
+ * Prefer canonical member symbols for resolved template access results when they already exist.
2643
+ * This preserves symbol identity for downstream cloning and doc lookups in instantiated flows.
2644
+ */
2645
+ function getPreferredTemplateAccessSymbol(node, type) {
2646
+ const canonicalMemberSymbol = getCanonicalTemplateAccessMemberSymbol(type);
2647
+ if (canonicalMemberSymbol) {
2648
+ return canonicalMemberSymbol;
2649
+ }
2650
+ if (!shouldUseLateBoundTemplateAccessType(type)) {
2651
+ return getTypeSymbol(type) ?? createLateBoundTypeSymbol(node, type);
2652
+ }
2653
+ return createLateBoundTypeSymbol(node, type);
2654
+ }
2655
+ /** Return the exact instantiated member symbol for a resolved type when one exists. */
2656
+ function getCanonicalTemplateAccessMemberSymbol(type) {
2657
+ switch (type.kind) {
2658
+ case "ModelProperty":
2659
+ return getExactInstantiatedMemberSymbol(type.model?.symbol, type.name, type);
2660
+ case "Operation":
2661
+ return getExactInstantiatedMemberSymbol(type.interface?.symbol, type.name, type);
2662
+ case "EnumMember":
2663
+ return getExactInstantiatedMemberSymbol(type.enum?.symbol, type.name, type);
2664
+ case "UnionVariant":
2665
+ return getExactInstantiatedMemberSymbol(type.union?.symbol, type.name, type);
2666
+ case "ScalarConstructor":
2667
+ return getExactInstantiatedMemberSymbol(type.scalar?.symbol, type.name, type);
2668
+ default:
2669
+ return undefined;
2670
+ }
2671
+ }
2672
+ function getExactInstantiatedMemberSymbol(containerSymbol, memberName, type) {
2673
+ if (!containerSymbol || typeof memberName !== "string") {
2674
+ return undefined;
2675
+ }
2676
+ const memberSymbol = getMemberSymbol(containerSymbol, memberName);
2677
+ return memberSymbol?.type === type ? memberSymbol : undefined;
2678
+ }
2679
+ /**
2680
+ * Resolve the template entity (parameter or access) that acts as the base for a member expression.
2681
+ */
2682
+ function getTemplateAccessBaseEntity(_ctx, baseNode, baseSym) {
2683
+ if (baseSym.flags & 4194304 /* SymbolFlags.LateBound */) {
2684
+ const lateBoundType = baseSym.type;
2685
+ if (lateBoundType && lateBoundType.kind === "TemplateParameterAccess") {
2686
+ return lateBoundType;
2687
+ }
2688
+ return undefined;
2689
+ }
2690
+ if (baseSym.flags & 1024 /* SymbolFlags.TemplateParameter */) {
2691
+ const baseSymbolNode = getSymNode(baseSym);
2692
+ const mapped = checkTemplateParameterDeclaration(CheckContext.DEFAULT, baseSymbolNode);
2693
+ return isType(mapped) && isTemplateAccessType(mapped) ? mapped : undefined;
2694
+ }
2695
+ if (baseNode.kind === SyntaxKind.Identifier) {
2696
+ const templateParameterType = getTemplateParameterTypeFromScope(baseNode);
2697
+ if (templateParameterType) {
2698
+ return templateParameterType;
2699
+ }
2700
+ return undefined;
2701
+ }
2702
+ return undefined;
2703
+ }
2704
+ /** Probe a node for a template access base without surfacing diagnostics. */
2705
+ function probeTemplateAccessBaseEntity(node) {
2706
+ const oldDiagnosticHook = onCheckerDiagnostic;
2707
+ onCheckerDiagnostic = () => { };
2708
+ const entity = checkTypeOrValueReference(CheckContext.DEFAULT, node, false);
2709
+ onCheckerDiagnostic = oldDiagnosticHook;
2710
+ return isType(entity) && isTemplateAccessType(entity) ? entity : undefined;
2711
+ }
2712
+ /** Resolve a template parameter type from lexical scope by identifier name. */
2713
+ function getTemplateParameterTypeFromScope(identifier) {
2714
+ const declaration = findTemplateParameterDeclarationInScope(identifier, identifier.sv);
2715
+ if (!declaration) {
2716
+ return undefined;
2717
+ }
2718
+ const mapped = checkTemplateParameterDeclaration(CheckContext.DEFAULT, declaration);
2719
+ return isType(mapped) && isTemplateAccessType(mapped) ? mapped : undefined;
2720
+ }
2721
+ /** Find the closest template parameter declaration matching the given name. */
2722
+ function findTemplateParameterDeclarationInScope(node, name) {
2723
+ let current = node.parent;
2724
+ while (current) {
2725
+ if ("templateParameters" in current && current.templateParameters) {
2726
+ const declaration = current.templateParameters.find((x) => x.id.sv === name);
2727
+ if (declaration) {
2728
+ return declaration;
2729
+ }
2730
+ }
2731
+ current = current.parent;
2732
+ }
2733
+ return undefined;
2734
+ }
2735
+ /**
2736
+ * Resolve the resulting type for a template parameter access expression.
2737
+ *
2738
+ * @param ctx Check context for mapper-aware resolution.
2739
+ * @param node Access expression (`.` or `::`) being resolved.
2740
+ * @param baseEntity Template parameter or prior template access chain.
2741
+ * @returns The resolved member/meta-member type, or `errorType` when not guaranteed.
2742
+ */
2743
+ function resolveTemplateAccessType(ctx, node, baseEntity) {
2744
+ const baseType = resolveTemplateAccessBaseType(ctx, baseEntity);
2745
+ if (!baseType) {
2746
+ if (hasErrorTemplateConstraint(baseEntity)) {
2747
+ return errorType;
2748
+ }
2749
+ reportTemplateAccessNotGuaranteed(node, baseEntity);
2750
+ return errorType;
2751
+ }
2752
+ const resolvedType = node.selector === "."
2753
+ ? resolveMemberTypeFromConstraint(baseType, node.id.sv)
2754
+ : resolveMetaTypeFromConstraint(ctx, baseType, node);
2755
+ if (!resolvedType) {
2756
+ reportTemplateAccessNotGuaranteed(node, baseType);
2757
+ return errorType;
2758
+ }
2759
+ return resolvedType;
2760
+ }
2761
+ /**
2762
+ * Resolve the concrete base type that a template access should evaluate against.
2763
+ *
2764
+ * @param ctx Check context containing optional template mapper.
2765
+ * @param baseEntity Template parameter/access entity.
2766
+ * @returns The mapped or constrained base type, if determinable.
2767
+ */
2768
+ function resolveTemplateAccessBaseType(ctx, baseEntity) {
2769
+ if (ctx.mapper && baseEntity.kind === "TemplateParameterAccess") {
2770
+ const mappedBaseType = resolveTemplateAccessBaseType(ctx, baseEntity.base);
2771
+ if (!mappedBaseType) {
2772
+ return undefined;
2773
+ }
2774
+ return baseEntity.node.selector === "."
2775
+ ? resolveMemberTypeFromConstraint(mappedBaseType, baseEntity.node.id.sv)
2776
+ : resolveMetaTypeFromConstraint(ctx, mappedBaseType, baseEntity.node);
2777
+ }
2778
+ if (ctx.mapper && baseEntity.kind === "TemplateParameter") {
2779
+ const mapped = ctx.mapper.getMappedType(baseEntity);
2780
+ if (isType(mapped)) {
2781
+ if (isTemplateAccessType(mapped)) {
2782
+ return resolveTemplateConstraintType(mapped);
2783
+ }
2784
+ if (isUninstantiatedTemplateType(mapped)) {
2785
+ return resolveTemplateConstraintType(baseEntity);
2786
+ }
2787
+ return mapped;
2788
+ }
2789
+ }
2790
+ return resolveTemplateConstraintType(baseEntity);
2791
+ }
2792
+ /**
2793
+ * Resolve the terminal non-template constraint type for a template access chain.
2794
+ *
2795
+ * @param templateType Template parameter or access node.
2796
+ * @returns The terminal constrained type, or `undefined` when missing/invalid/cyclic.
2797
+ */
2798
+ function resolveTemplateConstraintType(templateType) {
2799
+ const visited = new Set();
2800
+ let current = templateType;
2801
+ while (true) {
2802
+ if (visited.has(current)) {
2803
+ return undefined;
2804
+ }
2805
+ visited.add(current);
2806
+ const constraintType = current.constraint?.type;
2807
+ if (!constraintType || isErrorType(constraintType)) {
2808
+ return undefined;
2809
+ }
2810
+ if (!isTemplateAccessType(constraintType)) {
2811
+ return constraintType;
2812
+ }
2813
+ current = constraintType;
2814
+ }
2815
+ }
2816
+ /** Return true when a template access chain includes an error constraint. */
2817
+ function hasErrorTemplateConstraint(templateType) {
2818
+ let current = templateType;
2819
+ while (true) {
2820
+ const constraintType = current.constraint?.type;
2821
+ if (constraintType && isErrorType(constraintType)) {
2822
+ return true;
2823
+ }
2824
+ if (current.kind !== "TemplateParameterAccess") {
2825
+ return false;
2826
+ }
2827
+ current = current.base;
2828
+ }
2829
+ }
2830
+ /** Track template parameter usage for a template access base chain. */
2831
+ function observeTemplateAccessBase(ctx, base) {
2832
+ const root = getTemplateAccessRoot(base);
2833
+ ctx.observeTemplateParameter(root);
2834
+ templateParameterUsageMap.set(root.node, true);
2835
+ }
2836
+ /** Return the root template parameter for a template access chain. */
2837
+ function getTemplateAccessRoot(base) {
2838
+ let current = base;
2839
+ while (current.kind === "TemplateParameterAccess") {
2840
+ current = current.base;
2841
+ }
2842
+ return current;
2843
+ }
2844
+ /** Resolve `.` access from a constrained type by kind-specific member lookup. */
2845
+ function resolveMemberTypeFromConstraint(constraintType, memberName) {
2846
+ const canonicalMemberType = getCanonicalResolvedMemberType(constraintType, memberName);
2847
+ if (canonicalMemberType) {
2848
+ return canonicalMemberType;
2849
+ }
2850
+ switch (constraintType.kind) {
2851
+ case "Model":
2852
+ for (const property of walkPropertiesInherited(constraintType)) {
2853
+ if (property.name === memberName) {
2854
+ return property;
2855
+ }
2856
+ }
2857
+ return undefined;
2858
+ case "Interface":
2859
+ return constraintType.operations.get(memberName);
2860
+ case "Enum":
2861
+ return constraintType.members.get(memberName);
2862
+ case "Union":
2863
+ return constraintType.variants.get(memberName);
2864
+ case "Scalar":
2865
+ return constraintType.constructors.get(memberName);
2866
+ default:
2867
+ return undefined;
2868
+ }
2869
+ }
2870
+ function getCanonicalResolvedMemberSymbol(containerType, memberName) {
2871
+ switch (containerType.kind) {
2872
+ case "Model":
2873
+ case "Interface":
2874
+ case "Enum":
2875
+ case "Union":
2876
+ case "Scalar":
2877
+ break;
2878
+ default:
2879
+ return undefined;
2880
+ }
2881
+ const containerSymbol = containerType.symbol;
2882
+ if (containerSymbol === undefined || !containerSymbol.members) {
2883
+ return undefined;
2884
+ }
2885
+ let memberSymbol = getMemberSymbol(containerSymbol, memberName);
2886
+ if (!memberSymbol?.type && containerSymbol.flags & 4194304 /* SymbolFlags.LateBound */) {
2887
+ lateBindMembers(containerType);
2888
+ memberSymbol = getMemberSymbol(containerSymbol, memberName);
2889
+ }
2890
+ return memberSymbol;
2891
+ }
2892
+ function getCanonicalResolvedMemberType(containerType, memberName) {
2893
+ const memberType = getCanonicalResolvedMemberSymbol(containerType, memberName)?.type;
2894
+ return memberType && isType(memberType) ? memberType : undefined;
2895
+ }
2896
+ /**
2897
+ * Resolve `::` meta-member access from a constrained type.
2898
+ *
2899
+ * @param ctx Check context used for symbol-to-entity evaluation.
2900
+ * @param constraintType Base constrained type.
2901
+ * @param node Meta-member expression node.
2902
+ * @returns The resolved meta-member type, `unknownType` for projection-only cases, or `undefined`.
2903
+ */
2904
+ function resolveMetaTypeFromConstraint(ctx, constraintType, node) {
2905
+ if (constraintType.kind === "ModelProperty" && node.id.sv === "type") {
2906
+ return constraintType.type;
2907
+ }
2908
+ if (constraintType.kind === "Operation") {
2909
+ switch (node.id.sv) {
2910
+ case "parameters":
2911
+ return constraintType.parameters;
2912
+ case "returnType":
2913
+ return constraintType.returnType;
2914
+ }
2915
+ }
2916
+ const constraintSymbol = getTypeSymbol(constraintType);
2917
+ if (!constraintSymbol) {
2918
+ return undefined;
2919
+ }
2920
+ const metaMemberNames = resolver.getMetaMemberNames(constraintSymbol);
2921
+ if (!metaMemberNames.includes(node.id.sv)) {
2922
+ return undefined;
2923
+ }
2924
+ if (isReflectionMetaProjectionSymbol(constraintSymbol)) {
2925
+ // Reflection model symbols expose meta-member names by projection, but their
2926
+ // underlying nodes are not concrete ModelProperty/Operation nodes.
2927
+ return unknownType;
2928
+ }
2929
+ const resolved = resolver.resolveMetaMemberByName(constraintSymbol, node.id.sv);
2930
+ if (resolved.resolutionResult & ResolutionResultFlags.Resolved && resolved.resolvedSymbol) {
2931
+ const entity = checkTypeOrValueReferenceSymbol(ctx, resolved.resolvedSymbol, node, false);
2932
+ if (entity === null) {
2933
+ return undefined;
2934
+ }
2935
+ if (entity.entityKind === "Indeterminate") {
2936
+ return entity.type;
2937
+ }
2938
+ return isType(entity) ? entity : undefined;
2939
+ }
2940
+ return unknownType;
2941
+ }
2942
+ /** Return true for TypeSpec.Reflection model symbols backed by projection metadata. */
2943
+ function isReflectionMetaProjectionSymbol(sym) {
2944
+ return (sym.node?.kind === SyntaxKind.ModelStatement &&
2945
+ sym.parent?.name === "Reflection" &&
2946
+ sym.parent?.parent?.name === "TypeSpec");
2947
+ }
2948
+ /** Report an invalid-ref diagnostic for unsupported template member/meta-member access. */
2949
+ function reportTemplateAccessNotGuaranteed(node, baseType) {
2950
+ reportCheckerDiagnostic(createDiagnostic({
2951
+ code: "invalid-ref",
2952
+ messageId: node.selector === "." ? "member" : "metaProperty",
2953
+ format: { kind: getTemplateAccessKindName(baseType), id: node.id.sv },
2954
+ target: node,
2955
+ }));
2956
+ }
2957
+ /** Get the diagnostic kind label used when template access resolution fails. */
2958
+ function getTemplateAccessKindName(type) {
2959
+ switch (type.kind) {
2960
+ case "Model":
2961
+ case "ModelProperty":
2962
+ case "Enum":
2963
+ case "Interface":
2964
+ case "Union":
2965
+ case "Operation":
2966
+ case "Scalar":
2967
+ case "TemplateParameter":
2968
+ case "TemplateParameterAccess":
2969
+ return type.kind;
2970
+ default:
2971
+ return "Type";
2972
+ }
2973
+ }
2974
+ /** Type guard for template parameters and template parameter access types. */
2975
+ function isTemplateAccessType(type) {
2976
+ return type.kind === "TemplateParameter" || type.kind === "TemplateParameterAccess";
2977
+ }
2978
+ /**
2979
+ * Create (or retrieve from cache) a late-bound symbol representing template access.
2980
+ *
2981
+ * @param base Template parameter/access base.
2982
+ * @param node Member expression node.
2983
+ * @param constraintType Resolved constraint for the access result.
2984
+ * @param useCache Whether to reuse/access symbol cache.
2985
+ * @returns A symbol whose type is `TemplateParameterAccess`.
2986
+ */
2987
+ function createTemplateAccessSymbol(base, node, constraintType, useCache = true) {
2988
+ const cacheKey = getTemplateAccessCacheKey(base, node);
2989
+ if (useCache) {
2990
+ const existing = templateAccessSymbolCache.get(cacheKey);
2991
+ if (existing) {
2992
+ return existing;
2993
+ }
2994
+ }
2995
+ const constraint = {
2996
+ entityKind: "MixedParameterConstraint",
2997
+ node,
2998
+ type: constraintType,
2999
+ };
3000
+ const type = createAndFinishType({
3001
+ kind: "TemplateParameterAccess",
3002
+ node,
3003
+ base,
3004
+ path: printMemberExpressionPath(getTemplateAccessPath(base), node.selector, node.id.sv),
3005
+ constraint,
3006
+ });
3007
+ templateAccessCacheKeys.set(type, cacheKey);
3008
+ const symbol = createSymbol(node, node.id.sv, 4194304 /* SymbolFlags.LateBound */);
3009
+ mutate(symbol).type = type;
3010
+ if (useCache) {
3011
+ templateAccessSymbolCache.set(cacheKey, symbol);
3012
+ }
3013
+ return symbol;
3014
+ }
3015
+ /** Compute the user-facing access path for a template access chain. */
3016
+ function getTemplateAccessPath(base) {
3017
+ return base.kind === "TemplateParameterAccess" ? base.path : printIdentifier(base.node.id.sv);
3018
+ }
3019
+ /** Build a stable cache key for a template access symbol/type chain. */
3020
+ function getTemplateAccessCacheKey(base, node) {
3021
+ let baseKey;
3022
+ if (base.kind === "TemplateParameterAccess") {
3023
+ const cacheKey = templateAccessCacheKeys.get(base);
3024
+ compilerAssert(cacheKey, "Expected template access cache key");
3025
+ baseKey = cacheKey;
3026
+ }
3027
+ else {
3028
+ baseKey = `tp:${getSymbolCacheId(base.node.symbol)}`;
3029
+ }
3030
+ return `${baseKey}${node.selector}${node.id.sv}`;
3031
+ }
3032
+ /** Resolve the merged symbol associated with a type, when one exists. */
3033
+ function getTypeSymbol(type) {
3034
+ return type.node?.symbol ? getMergedSymbol(type.node.symbol) : undefined;
3035
+ }
3036
+ /** Get a stable numeric id for a symbol used in template access cache keys. */
3037
+ function getSymbolCacheId(sym) {
3038
+ const existing = symbolCacheIds.get(sym);
3039
+ if (existing !== undefined) {
3040
+ return existing;
3041
+ }
3042
+ const id = nextSymbolCacheId++;
3043
+ symbolCacheIds.set(sym, id);
3044
+ return id;
3045
+ }
2428
3046
  function checkSymbolAccess(sourceLocation, node, symbol) {
2429
3047
  if (!symbol)
2430
3048
  return;
@@ -2468,7 +3086,11 @@ export function createChecker(program, resolver) {
2468
3086
  target: node,
2469
3087
  }));
2470
3088
  }
2471
- function resolveMemberInContainer(base, node, options) {
3089
+ function resolveMemberInContainer(ctx, base, node, options) {
3090
+ const symbolFromType = resolveMemberOnSymbolType(ctx, base, node);
3091
+ if (symbolFromType) {
3092
+ return symbolFromType;
3093
+ }
2472
3094
  const { finalSymbol: sym, resolvedSymbol: nextSym } = resolver.resolveMemberExpressionForSym(base, node, options);
2473
3095
  const symbol = nextSym ?? sym;
2474
3096
  if (symbol) {
@@ -2540,6 +3162,100 @@ export function createChecker(program, resolver) {
2540
3162
  return "Type";
2541
3163
  }
2542
3164
  }
3165
+ /**
3166
+ * Resolve a member/meta-member access from the base symbol's resolved type.
3167
+ *
3168
+ * @param ctx Check context.
3169
+ * @param base Base symbol.
3170
+ * @param node Member expression to resolve.
3171
+ * @returns Concrete symbol, late-bound symbol, or `undefined` when unresolved.
3172
+ */
3173
+ function resolveMemberOnSymbolType(ctx, base, node) {
3174
+ const baseType = getMemberResolutionType(ctx, base);
3175
+ if (!baseType) {
3176
+ return undefined;
3177
+ }
3178
+ const resolvedBaseType = isTemplateAccessType(baseType)
3179
+ ? resolveTemplateAccessBaseType(ctx, baseType)
3180
+ : baseType;
3181
+ if (!resolvedBaseType) {
3182
+ return undefined;
3183
+ }
3184
+ if (node.selector === ".") {
3185
+ const table = base.exports ?? base.members;
3186
+ if (table) {
3187
+ const directMember = resolver.getAugmentedSymbolTable(table).get(node.id.sv);
3188
+ if (directMember) {
3189
+ return directMember;
3190
+ }
3191
+ }
3192
+ const canonicalMember = getCanonicalResolvedMemberSymbol(resolvedBaseType, node.id.sv);
3193
+ if (canonicalMember) {
3194
+ return canonicalMember;
3195
+ }
3196
+ }
3197
+ const resolvedType = node.selector === "."
3198
+ ? resolveMemberTypeFromConstraint(resolvedBaseType, node.id.sv)
3199
+ : resolveMetaTypeFromConstraint(ctx, resolvedBaseType, node);
3200
+ if (!resolvedType) {
3201
+ return undefined;
3202
+ }
3203
+ if (node.selector === "::" &&
3204
+ node.id.sv === "type" &&
3205
+ resolvedType.kind === "TemplateParameterAccess" &&
3206
+ resolvedType.base.kind === "TemplateParameterAccess" &&
3207
+ resolvedType.base.node.selector === ".") {
3208
+ const sourceProperty = resolveTemplateConstraintType(resolvedType.base);
3209
+ if (sourceProperty) {
3210
+ return getTypeSymbol(sourceProperty) ?? createLateBoundTypeSymbol(node, sourceProperty);
3211
+ }
3212
+ }
3213
+ return getPreferredTemplateAccessSymbol(node, resolvedType);
3214
+ }
3215
+ /** Resolve the effective type used for member lookup on a base symbol. */
3216
+ function getMemberResolutionType(_ctx, base) {
3217
+ if (base.flags & 4194304 /* SymbolFlags.LateBound */) {
3218
+ return base.type && isType(base.type) ? base.type : undefined;
3219
+ }
3220
+ if (base.flags & 65536 /* SymbolFlags.Member */) {
3221
+ return base.type && isType(base.type) ? base.type : undefined;
3222
+ }
3223
+ return undefined;
3224
+ }
3225
+ /** Create a late-bound symbol carrying a precomputed type for member resolution. */
3226
+ function createLateBoundTypeSymbol(node, type) {
3227
+ const symbol = createSymbol(node, node.id.sv, 4194304 /* SymbolFlags.LateBound */);
3228
+ mutate(symbol).type = type;
3229
+ const symbolSource = getTemplateAccessSymbolSource(type);
3230
+ if (symbolSource) {
3231
+ mutate(symbol).symbolSource = symbolSource;
3232
+ }
3233
+ return symbol;
3234
+ }
3235
+ /** Get the best source symbol to associate with a template-access-derived symbol. */
3236
+ function getTemplateAccessSymbolSource(type) {
3237
+ const symbolSource = type.kind === "ModelProperty"
3238
+ ? getModelPropertySymbolSource(type)
3239
+ : (getCanonicalTemplateAccessMemberSymbol(type) ?? getTypeSymbol(type));
3240
+ return symbolSource?.symbolSource ?? symbolSource;
3241
+ }
3242
+ function getModelPropertySymbolSource(type) {
3243
+ let fallback;
3244
+ for (let property = type; property; property = property.sourceProperty) {
3245
+ const symbolSource = getCanonicalTemplateAccessMemberSymbol(property) ??
3246
+ (property.model?.symbol && typeof property.name === "string"
3247
+ ? getMemberSymbol(property.model.symbol, property.name)
3248
+ : undefined) ??
3249
+ getTypeSymbol(property);
3250
+ if (symbolSource) {
3251
+ fallback ??= symbolSource;
3252
+ if (!property.sourceProperty) {
3253
+ return symbolSource;
3254
+ }
3255
+ }
3256
+ }
3257
+ return fallback;
3258
+ }
2543
3259
  /**
2544
3260
  * Return the symbol that is aliased by this alias declaration. If no such symbol is aliased,
2545
3261
  * return the symbol for the alias instead. For member containers which need to be late bound
@@ -2550,7 +3266,12 @@ export function createChecker(program, resolver) {
2550
3266
  const node = getSymNode(aliasSymbol);
2551
3267
  const links = resolver.getSymbolLinks(aliasSymbol);
2552
3268
  if (!links.aliasResolutionIsTemplate) {
2553
- return links.aliasedSymbol ?? resolver.getNodeLinks(node).resolvedSymbol;
3269
+ const aliased = links.aliasedSymbol ?? resolver.getNodeLinks(node).resolvedSymbol;
3270
+ if (aliased && isTemplatedNode(getSymNode(aliased))) {
3271
+ const aliasType = getTypeForNode(node, ctx);
3272
+ return lateBindContainer(aliasType, aliasSymbol);
3273
+ }
3274
+ return aliased;
2554
3275
  }
2555
3276
  // Otherwise for templates we need to get the type and retrieve the late bound symbol.
2556
3277
  const aliasType = getTypeForNode(node, ctx);
@@ -2607,7 +3328,9 @@ export function createChecker(program, resolver) {
2607
3328
  if (isValue(typeOrValue)) {
2608
3329
  hasValue = true;
2609
3330
  }
2610
- else if ("kind" in typeOrValue && typeOrValue.kind === "TemplateParameter") {
3331
+ else if ("kind" in typeOrValue &&
3332
+ (typeOrValue.kind === "TemplateParameter" ||
3333
+ typeOrValue.kind === "TemplateParameterAccess")) {
2611
3334
  if (typeOrValue.constraint) {
2612
3335
  if (typeOrValue.constraint.valueType) {
2613
3336
  hasValue = true;
@@ -2636,7 +3359,9 @@ export function createChecker(program, resolver) {
2636
3359
  let str = node.head.value;
2637
3360
  for (const [span, typeOrValue] of spanTypeOrValues) {
2638
3361
  if (typeOrValue !== null &&
2639
- (!("kind" in typeOrValue) || typeOrValue.kind !== "TemplateParameter")) {
3362
+ (!("kind" in typeOrValue) ||
3363
+ (typeOrValue.kind !== "TemplateParameter" &&
3364
+ typeOrValue.kind !== "TemplateParameterAccess"))) {
2640
3365
  compilerAssert(typeOrValue !== null && isValue(typeOrValue), "Expected value.");
2641
3366
  str += stringifyValueForTemplate(typeOrValue);
2642
3367
  }
@@ -3265,7 +3990,7 @@ export function createChecker(program, resolver) {
3265
3990
  if (target.kind === "Scalar" || target.kind === "ScalarConstructor") {
3266
3991
  return target;
3267
3992
  }
3268
- else if (target.kind === "TemplateParameter") {
3993
+ else if (target.kind === "TemplateParameter" || target.kind === "TemplateParameterAccess") {
3269
3994
  const callable = target.constraint && constraintIsCallable(target.constraint);
3270
3995
  if (!callable) {
3271
3996
  reportCheckerDiagnostic(createDiagnostic({
@@ -3751,7 +4476,7 @@ export function createChecker(program, resolver) {
3751
4476
  return entity.type;
3752
4477
  }
3753
4478
  if (isType(entity)) {
3754
- if (entity.kind === "TemplateParameter") {
4479
+ if (entity.kind === "TemplateParameter" || entity.kind === "TemplateParameterAccess") {
3755
4480
  if (entity.constraint === undefined || entity.constraint.type !== undefined) {
3756
4481
  // means this template constraint will accept values
3757
4482
  reportCheckerDiagnostic(createDiagnostic({
@@ -3848,8 +4573,11 @@ export function createChecker(program, resolver) {
3848
4573
  * access a symbol for a type that is created during the check phase.
3849
4574
  */
3850
4575
  function lateBindMemberContainer(type) {
3851
- if (type.symbol)
4576
+ const existingSymbol = type.symbol;
4577
+ if (existingSymbol &&
4578
+ !(isTemplateInstance(type) && !(existingSymbol.flags & 4194304 /* SymbolFlags.LateBound */))) {
3852
4579
  return;
4580
+ }
3853
4581
  switch (type.kind) {
3854
4582
  case "Model":
3855
4583
  type.symbol = createSymbol(type.node, type.name, 2 /* SymbolFlags.Model */ | 4194304 /* SymbolFlags.LateBound */);
@@ -3906,6 +4634,10 @@ export function createChecker(program, resolver) {
3906
4634
  }
3907
4635
  const sym = createSymbol(member.node, member.name, kind | 4194304 /* SymbolFlags.LateBound */, containerSym);
3908
4636
  mutate(sym).type = member;
4637
+ const symbolSource = getTypeSymbol(member);
4638
+ if (symbolSource) {
4639
+ mutate(sym).symbolSource = symbolSource.symbolSource ?? symbolSource;
4640
+ }
3909
4641
  compilerAssert(containerSym.members, "containerSym.members is undefined");
3910
4642
  containerMembers.set(member.name, sym);
3911
4643
  }
@@ -4024,7 +4756,9 @@ export function createChecker(program, resolver) {
4024
4756
  }
4025
4757
  function checkSpreadProperty(ctx, parentModelSym, targetNode, parentModel) {
4026
4758
  const targetType = getTypeForNode(targetNode, ctx);
4027
- if (targetType.kind === "TemplateParameter" || isErrorType(targetType)) {
4759
+ if (targetType.kind === "TemplateParameter" ||
4760
+ targetType.kind === "TemplateParameterAccess" ||
4761
+ isErrorType(targetType)) {
4028
4762
  return [[], undefined];
4029
4763
  }
4030
4764
  if (targetType.kind !== "Model") {
@@ -4108,7 +4842,7 @@ export function createChecker(program, resolver) {
4108
4842
  linkMapper(type, ctx.mapper);
4109
4843
  const shouldRunDecorators = !ctx.hasFlags(CheckFlags.InTemplateDeclaration);
4110
4844
  if (!parentTemplate || shouldRunDecorators) {
4111
- const docComment = docFromCommentForSym.get(sym);
4845
+ const docComment = getDocCommentForSymbol(sym) ?? getOperationParameterDocComment(prop);
4112
4846
  if (docComment) {
4113
4847
  type.decorators.unshift(createDocFromCommentDecorator("self", docComment));
4114
4848
  }
@@ -4116,6 +4850,20 @@ export function createChecker(program, resolver) {
4116
4850
  pendingResolutions.finish(sym, ResolutionKind.Type);
4117
4851
  return finishType(type, { skipDecorators: !shouldRunDecorators });
4118
4852
  }
4853
+ function getOperationParameterDocComment(prop) {
4854
+ if (prop.parent?.kind !== SyntaxKind.ModelExpression) {
4855
+ return undefined;
4856
+ }
4857
+ const signature = prop.parent.parent;
4858
+ if (signature?.kind !== SyntaxKind.OperationSignatureDeclaration) {
4859
+ return undefined;
4860
+ }
4861
+ const operation = signature.parent;
4862
+ if (operation?.kind !== SyntaxKind.OperationStatement) {
4863
+ return undefined;
4864
+ }
4865
+ return extractParamDocs(operation).get(prop.id.sv);
4866
+ }
4119
4867
  function createDocFromCommentDecorator(key, doc) {
4120
4868
  return {
4121
4869
  decorator: docFromCommentDecorator,
@@ -4125,6 +4873,32 @@ export function createChecker(program, resolver) {
4125
4873
  ],
4126
4874
  };
4127
4875
  }
4876
+ function getDocCommentForSymbol(sym) {
4877
+ if (!sym) {
4878
+ return undefined;
4879
+ }
4880
+ const relatedSource = getRelatedSymbolSource(sym);
4881
+ return (docFromCommentForSym.get(sym) ??
4882
+ (relatedSource && docFromCommentForSym.get(relatedSource)) ??
4883
+ (sym.flags & 4194304 /* SymbolFlags.LateBound */ && sym.type && isType(sym.type)
4884
+ ? getDocCommentForType(sym.type)
4885
+ : undefined));
4886
+ }
4887
+ function getDocCommentForType(type) {
4888
+ if (!type) {
4889
+ return undefined;
4890
+ }
4891
+ if (type.kind === "ModelProperty") {
4892
+ for (let property = type; property; property = property.sourceProperty) {
4893
+ const docComment = getDocCommentForSymbol(getTemplateAccessSymbolSource(property));
4894
+ if (docComment) {
4895
+ return docComment;
4896
+ }
4897
+ }
4898
+ return undefined;
4899
+ }
4900
+ return getDocCommentForSymbol(getTemplateAccessSymbolSource(type));
4901
+ }
4128
4902
  function checkDefaultValue(ctx, defaultNode, type) {
4129
4903
  if (isErrorType(type)) {
4130
4904
  // if the prop type is an error we don't need to validate again.
@@ -5196,7 +5970,7 @@ export function createChecker(program, resolver) {
5196
5970
  function cloneTypeForSymbol(sym, type, additionalProps = {}) {
5197
5971
  let clone = initializeClone(type, additionalProps);
5198
5972
  if ("decorators" in clone) {
5199
- const docComment = docFromCommentForSym.get(sym);
5973
+ const docComment = getDocCommentForSymbol(sym) ?? getDocCommentForType(type);
5200
5974
  if (docComment) {
5201
5975
  clone.decorators.push(createDocFromCommentDecorator("self", docComment));
5202
5976
  }