@typespec/compiler 1.10.0-dev.2 → 1.10.0-dev.4

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 (62) hide show
  1. package/dist/generated-defs/TypeSpec.Prototypes.ts-test.js +1 -1
  2. package/dist/generated-defs/TypeSpec.Prototypes.ts-test.js.map +1 -1
  3. package/dist/generated-defs/TypeSpec.ts-test.js +1 -1
  4. package/dist/generated-defs/TypeSpec.ts-test.js.map +1 -1
  5. package/dist/manifest.js +2 -2
  6. package/dist/src/core/binder.d.ts.map +1 -1
  7. package/dist/src/core/binder.js +10 -8
  8. package/dist/src/core/binder.js.map +1 -1
  9. package/dist/src/core/checker.d.ts.map +1 -1
  10. package/dist/src/core/checker.js +485 -46
  11. package/dist/src/core/checker.js.map +1 -1
  12. package/dist/src/core/helpers/string-template-utils.d.ts.map +1 -1
  13. package/dist/src/core/helpers/string-template-utils.js +7 -4
  14. package/dist/src/core/helpers/string-template-utils.js.map +1 -1
  15. package/dist/src/core/helpers/type-name-utils.d.ts.map +1 -1
  16. package/dist/src/core/helpers/type-name-utils.js +13 -0
  17. package/dist/src/core/helpers/type-name-utils.js.map +1 -1
  18. package/dist/src/core/js-marshaller.d.ts +4 -2
  19. package/dist/src/core/js-marshaller.d.ts.map +1 -1
  20. package/dist/src/core/js-marshaller.js +96 -6
  21. package/dist/src/core/js-marshaller.js.map +1 -1
  22. package/dist/src/core/messages.d.ts +101 -12
  23. package/dist/src/core/messages.d.ts.map +1 -1
  24. package/dist/src/core/messages.js +27 -2
  25. package/dist/src/core/messages.js.map +1 -1
  26. package/dist/src/core/name-resolver.d.ts.map +1 -1
  27. package/dist/src/core/name-resolver.js +30 -0
  28. package/dist/src/core/name-resolver.js.map +1 -1
  29. package/dist/src/core/parser.d.ts.map +1 -1
  30. package/dist/src/core/parser.js +34 -6
  31. package/dist/src/core/parser.js.map +1 -1
  32. package/dist/src/core/scanner.d.ts +5 -5
  33. package/dist/src/core/scanner.d.ts.map +1 -1
  34. package/dist/src/core/scanner.js +8 -8
  35. package/dist/src/core/scanner.js.map +1 -1
  36. package/dist/src/core/semantic-walker.d.ts +3 -3
  37. package/dist/src/core/semantic-walker.d.ts.map +1 -1
  38. package/dist/src/core/semantic-walker.js +63 -44
  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 +157 -10
  42. package/dist/src/core/type-relation-checker.js.map +1 -1
  43. package/dist/src/core/types.d.ts +187 -23
  44. package/dist/src/core/types.d.ts.map +1 -1
  45. package/dist/src/core/types.js +1 -0
  46. package/dist/src/core/types.js.map +1 -1
  47. package/dist/src/experimental/typekit/index.d.ts.map +1 -1
  48. package/dist/src/experimental/typekit/index.js.map +1 -1
  49. package/dist/src/formatter/print/printer.d.ts.map +1 -1
  50. package/dist/src/formatter/print/printer.js +13 -0
  51. package/dist/src/formatter/print/printer.js.map +1 -1
  52. package/dist/src/index.d.ts +2 -1
  53. package/dist/src/index.d.ts.map +1 -1
  54. package/dist/src/index.js +1 -0
  55. package/dist/src/index.js.map +1 -1
  56. package/dist/src/lib/examples.d.ts +10 -3
  57. package/dist/src/lib/examples.d.ts.map +1 -1
  58. package/dist/src/lib/examples.js +22 -7
  59. package/dist/src/lib/examples.js.map +1 -1
  60. package/dist/src/server/type-signature.js +19 -0
  61. package/dist/src/server/type-signature.js.map +1 -1
  62. package/package.json +1 -1
@@ -7,12 +7,12 @@ import { createSymbol, getSymNode } from "./binder.js";
7
7
  import { createChangeIdentifierCodeFix } from "./compiler-code-fixes/change-identifier.codefix.js";
8
8
  import { createModelToObjectValueCodeFix, createTupleToArrayValueCodeFix, } from "./compiler-code-fixes/convert-to-value.codefix.js";
9
9
  import { getDeprecationDetails, markDeprecated } from "./deprecation.js";
10
- import { compilerAssert, ignoreDiagnostics, reportDeprecated } from "./diagnostics.js";
10
+ import { compilerAssert, createDiagnosticCollector, ignoreDiagnostics, reportDeprecated, } from "./diagnostics.js";
11
11
  import { validateInheritanceDiscriminatedUnions } from "./helpers/discriminator-utils.js";
12
12
  import { explainStringTemplateNotSerializable } from "./helpers/string-template-utils.js";
13
13
  import { typeReferenceToString } from "./helpers/syntax-utils.js";
14
14
  import { getEntityName, getTypeName } from "./helpers/type-name-utils.js";
15
- import { marshallTypeForJS } from "./js-marshaller.js";
15
+ import { marshalTypeForJs, unmarshalJsToValue } from "./js-marshaller.js";
16
16
  import { createDiagnostic } from "./messages.js";
17
17
  import { Numeric } from "./numeric.js";
18
18
  import { exprIsBareIdentifier, getFirstAncestor, getIdentifierContext, hasParseError, visitChildren, } from "./parser.js";
@@ -175,6 +175,7 @@ export function createChecker(program, resolver) {
175
175
  nullType,
176
176
  anyType: unknownType,
177
177
  voidType,
178
+ unknownType,
178
179
  typePrototype,
179
180
  createType,
180
181
  createAndFinishType,
@@ -346,7 +347,7 @@ export function createChecker(program, resolver) {
346
347
  function getValueForNode(node, mapperOrContext, constraint) {
347
348
  const ctx = CheckContext.from(mapperOrContext);
348
349
  const initial = checkNode(ctx, node, constraint);
349
- if (initial === null) {
350
+ if (initial === null || initial === errorType) {
350
351
  return null;
351
352
  }
352
353
  let entity;
@@ -360,6 +361,8 @@ export function createChecker(program, resolver) {
360
361
  return null;
361
362
  }
362
363
  if (isValue(entity)) {
364
+ if (entity.valueKind === "Function")
365
+ return entity;
363
366
  return constraint ? inferScalarsFromConstraints(entity, constraint.type) : entity;
364
367
  }
365
368
  // If a template parameter that can be a value is used in a template declaration then we allow it but we return null because we don't have an actual value.
@@ -420,9 +423,8 @@ export function createChecker(program, resolver) {
420
423
  case "UnionVariant":
421
424
  return getValueFromIndeterminate(type.type, constraint, node);
422
425
  case "Intrinsic":
423
- switch (type.name) {
424
- case "null":
425
- return checkNullValue(type, constraint, node);
426
+ if (type.name === "null") {
427
+ return checkNullValue(type, constraint, node);
426
428
  }
427
429
  return type;
428
430
  default:
@@ -477,8 +479,8 @@ export function createChecker(program, resolver) {
477
479
  * For nodes that can be both type or values(e.g. string), the value will be returned if the constraint expect a value of that type even if the constrain also allows the type.
478
480
  * This means that if the constraint is `string | valueof string` passing `"abc"` will send the value `"abc"` and not the type `"abc"`.
479
481
  */
480
- function getTypeOrValueForNode(node, mapper, constraint) {
481
- const ctx = CheckContext.from(mapper);
482
+ function getTypeOrValueForNode(node, contextOrMapper, constraint) {
483
+ const ctx = CheckContext.from(contextOrMapper);
482
484
  const valueConstraint = extractValueOfConstraints(constraint);
483
485
  const entity = checkNode(ctx, node, valueConstraint);
484
486
  if (entity === null) {
@@ -507,6 +509,12 @@ export function createChecker(program, resolver) {
507
509
  reportCheckerDiagnostics(valueDiagnostics);
508
510
  return result;
509
511
  }
512
+ else {
513
+ const canBeType = constraint?.constraint.type !== undefined;
514
+ // If the node _must_ resolve to a value, we will return it unconstrained, so that we will at least produce
515
+ // a value. If it _can_ be a type, we already failed the value constraint, so we return the type as is.
516
+ return canBeType ? entity.type : getValueFromIndeterminate(entity.type, undefined, node);
517
+ }
510
518
  }
511
519
  return entity.type;
512
520
  }
@@ -571,6 +579,8 @@ export function createChecker(program, resolver) {
571
579
  return checkDecoratorDeclaration(ctx, node);
572
580
  case SyntaxKind.FunctionDeclarationStatement:
573
581
  return checkFunctionDeclaration(ctx, node);
582
+ case SyntaxKind.FunctionTypeExpression:
583
+ return checkFunctionTypeExpression(ctx, node);
574
584
  case SyntaxKind.TypeReference:
575
585
  return checkTypeOrValueReference(ctx, node);
576
586
  case SyntaxKind.TemplateArgument:
@@ -711,6 +721,7 @@ export function createChecker(program, resolver) {
711
721
  * @param node Node.
712
722
  * @param mapper Type mapper for template instantiation context.
713
723
  * @param instantiateTemplate If templated type should be instantiated if they haven't yet.
724
+ * @param allowFunctions If functions are allowed as types.
714
725
  * @returns Resolved type.
715
726
  */
716
727
  function checkTypeReference(ctx, node, instantiateTemplate = true) {
@@ -726,6 +737,7 @@ export function createChecker(program, resolver) {
726
737
  * @param node Node.
727
738
  * @param mapper Type mapper for template instantiation context.
728
739
  * @param instantiateTemplate If templated type should be instantiated if they haven't yet.
740
+ * @param allowFunctions If functions are allowed as types.
729
741
  * @returns Resolved type.
730
742
  */
731
743
  function checkTypeOrValueReference(ctx, node, instantiateTemplate = true) {
@@ -953,6 +965,7 @@ export function createChecker(program, resolver) {
953
965
  * @param node Node
954
966
  * @param mapper Type mapper for template instantiation context.
955
967
  * @param instantiateTemplates If a templated type should be instantiated if not yet @default true
968
+ * @param allowFunctions If functions are allowed as types. @default false
956
969
  * @returns resolved type.
957
970
  */
958
971
  function checkTypeReferenceSymbol(ctx, sym, node, instantiateTemplates = true) {
@@ -983,8 +996,7 @@ export function createChecker(program, resolver) {
983
996
  return errorType;
984
997
  }
985
998
  if (sym.flags & 2048 /* SymbolFlags.Function */) {
986
- reportCheckerDiagnostic(createDiagnostic({ code: "invalid-type-ref", messageId: "function", target: sym }));
987
- return errorType;
999
+ return getValueForNode(sym.declarations[0], ctx);
988
1000
  }
989
1001
  const argumentNodes = node.kind === SyntaxKind.TypeReference ? node.arguments : [];
990
1002
  const symbolLinks = getSymbolLinks(sym);
@@ -1141,7 +1153,7 @@ export function createChecker(program, resolver) {
1141
1153
  function checkDeclaredType(ctx, sym, node) {
1142
1154
  return getTypeForTypeOrIndeterminate(checkDeclaredTypeOrIndeterminate(ctx, sym, node));
1143
1155
  }
1144
- function getOrInstantiateTemplate(ctx, templateNode, params, args, source, parentMapper, instantiateTempalates = true) {
1156
+ function getOrInstantiateTemplate(ctx, templateNode, params, args, source, parentMapper, instantiateTemplates = true) {
1145
1157
  const symbolLinks = templateNode.kind === SyntaxKind.OperationStatement &&
1146
1158
  templateNode.parent.kind === SyntaxKind.InterfaceStatement
1147
1159
  ? getSymbolLinksForMember(templateNode)
@@ -1161,7 +1173,7 @@ export function createChecker(program, resolver) {
1161
1173
  if (cached) {
1162
1174
  return cached;
1163
1175
  }
1164
- if (instantiateTempalates) {
1176
+ if (instantiateTemplates) {
1165
1177
  return instantiateTemplate(ctx.withMapper(mapper), symbolLinks.instantiations, templateNode, params);
1166
1178
  }
1167
1179
  else {
@@ -1355,8 +1367,69 @@ export function createChecker(program, resolver) {
1355
1367
  return decoratorType;
1356
1368
  }
1357
1369
  function checkFunctionDeclaration(ctx, node) {
1358
- reportCheckerDiagnostic(createDiagnostic({ code: "function-unsupported", target: node }));
1359
- return errorType;
1370
+ const mergedSymbol = getMergedSymbol(node.symbol);
1371
+ const links = getSymbolLinks(mergedSymbol);
1372
+ if (links.value !== undefined) {
1373
+ return links.value;
1374
+ }
1375
+ reportCheckerDiagnostic(createDiagnostic({
1376
+ code: "experimental-feature",
1377
+ messageId: "functionDeclarations",
1378
+ target: node,
1379
+ }));
1380
+ const namespace = getParentNamespaceType(node);
1381
+ compilerAssert(namespace, `Function ${node.id.sv} should have resolved a declared namespace or the global namespace.`);
1382
+ const name = node.id.sv;
1383
+ if (!(node.modifierFlags & 2 /* ModifierFlags.Extern */)) {
1384
+ reportCheckerDiagnostic(createDiagnostic({ code: "function-extern", target: node }));
1385
+ }
1386
+ const implementation = mergedSymbol.value;
1387
+ if (implementation === undefined) {
1388
+ reportCheckerDiagnostic(createDiagnostic({ code: "missing-implementation", target: node }));
1389
+ }
1390
+ const parameters = node.parameters.map((x) => checkFunctionParameter(ctx, x, true));
1391
+ const returnType = node.returnType
1392
+ ? getParamConstraintEntityForNode(ctx, node.returnType)
1393
+ : {
1394
+ entityKind: "MixedParameterConstraint",
1395
+ type: unknownType,
1396
+ };
1397
+ const functionValue = createValue({
1398
+ entityKind: "Value",
1399
+ valueKind: "Function",
1400
+ name,
1401
+ type: createAndFinishType({
1402
+ kind: "FunctionType",
1403
+ parameters,
1404
+ returnType,
1405
+ }),
1406
+ parameters,
1407
+ returnType,
1408
+ namespace,
1409
+ node,
1410
+ implementation: implementation ??
1411
+ Object.assign(() => getDefaultFunctionResult(returnType), {
1412
+ isDefaultFunctionImplementation: true,
1413
+ }),
1414
+ }, unknownType);
1415
+ namespace.functionDeclarations.set(name, functionValue);
1416
+ links.value = functionValue;
1417
+ return functionValue;
1418
+ }
1419
+ function checkFunctionTypeExpression(ctx, node) {
1420
+ const parameters = node.parameters.map((param) => checkFunctionParameter(ctx, param, true));
1421
+ const returnType = node.returnType
1422
+ ? getParamConstraintEntityForNode(ctx, node.returnType)
1423
+ : {
1424
+ entityKind: "MixedParameterConstraint",
1425
+ type: unknownType,
1426
+ };
1427
+ return createAndFinishType({
1428
+ kind: "FunctionType",
1429
+ node,
1430
+ parameters,
1431
+ returnType,
1432
+ });
1360
1433
  }
1361
1434
  function checkFunctionParameter(ctx, node, mixed) {
1362
1435
  const links = getSymbolLinks(node.symbol);
@@ -1979,7 +2052,7 @@ export function createChecker(program, resolver) {
1979
2052
  return undefined;
1980
2053
  }
1981
2054
  const ctorType = checkCallExpressionTarget(CheckContext.DEFAULT, callExpNode);
1982
- if (ctorType?.kind !== "ScalarConstructor") {
2055
+ if (ctorType?.entityKind !== "Type" || ctorType?.kind !== "ScalarConstructor") {
1983
2056
  return undefined;
1984
2057
  }
1985
2058
  const argIndex = callExpNode.arguments.findIndex((n) => n === argNode);
@@ -2209,12 +2282,15 @@ export function createChecker(program, resolver) {
2209
2282
  case IdentifierKind.Decorator:
2210
2283
  // Only return decorators and namespaces when completing decorator
2211
2284
  return !!(sym.flags & (512 /* SymbolFlags.Decorator */ | 256 /* SymbolFlags.Namespace */));
2285
+ case IdentifierKind.Function:
2286
+ // Only return functions and namespaces when completing function calls
2287
+ return !!(sym.flags & (2048 /* SymbolFlags.Function */ | 256 /* SymbolFlags.Namespace */));
2212
2288
  case IdentifierKind.Using:
2213
2289
  // Only return namespaces when completing using
2214
2290
  return !!(sym.flags & 256 /* SymbolFlags.Namespace */);
2215
2291
  case IdentifierKind.TypeReference:
2216
- // Do not return functions or decorators when completing types
2217
- return !(sym.flags & (2048 /* SymbolFlags.Function */ | 512 /* SymbolFlags.Decorator */));
2292
+ // Do not return decorators when completing types
2293
+ return !(sym.flags & 512 /* SymbolFlags.Decorator */);
2218
2294
  case IdentifierKind.TemplateArgument:
2219
2295
  return !!(sym.flags & 1024 /* SymbolFlags.TemplateParameter */);
2220
2296
  default:
@@ -3129,17 +3205,68 @@ export function createChecker(program, resolver) {
3129
3205
  }, literalType);
3130
3206
  }
3131
3207
  function checkCallExpressionTarget(ctx, node) {
3132
- const target = checkTypeReference(ctx, node.target);
3133
- if (target.kind === "Scalar" || target.kind === "ScalarConstructor") {
3134
- return target;
3208
+ const target = checkTypeOrValueReference(ctx, node.target);
3209
+ if (target.entityKind === "Type") {
3210
+ if (target.kind === "Scalar" || target.kind === "ScalarConstructor") {
3211
+ return target;
3212
+ }
3213
+ else if (target.kind === "TemplateParameter") {
3214
+ const callable = target.constraint && constraintIsCallable(target.constraint);
3215
+ if (!callable) {
3216
+ reportCheckerDiagnostic(createDiagnostic({
3217
+ code: "non-callable",
3218
+ messageId: "templateParameter",
3219
+ format: {
3220
+ name: target.node.id.sv,
3221
+ constraint: target.constraint
3222
+ ? getEntityName(target.constraint, { printable: true })
3223
+ : "unknown",
3224
+ },
3225
+ target: node.target,
3226
+ }));
3227
+ }
3228
+ return null;
3229
+ }
3135
3230
  }
3136
- else {
3231
+ else if (target.entityKind === "Value") {
3232
+ if (target.valueKind === "Function") {
3233
+ return target;
3234
+ }
3235
+ }
3236
+ const kind = target.entityKind === "Type"
3237
+ ? target.kind
3238
+ : target.entityKind === "Indeterminate"
3239
+ ? target.type.kind
3240
+ : target.valueKind;
3241
+ if (!isErrorType(target)) {
3137
3242
  reportCheckerDiagnostic(createDiagnostic({
3138
3243
  code: "non-callable",
3139
- format: { type: target.kind },
3244
+ format: { type: kind },
3140
3245
  target: node.target,
3141
3246
  }));
3142
- return null;
3247
+ }
3248
+ return null;
3249
+ function constraintIsCallable(constraint) {
3250
+ compilerAssert(constraint.type || constraint.valueType, "Expected constraint to have type or value type");
3251
+ let callable = true;
3252
+ if (constraint.type) {
3253
+ callable &&= typeIsCallable(constraint.type);
3254
+ }
3255
+ if (constraint.valueType) {
3256
+ callable &&= constraint.valueType.kind === "FunctionType";
3257
+ }
3258
+ return callable;
3259
+ }
3260
+ function typeIsCallable(type) {
3261
+ switch (type.kind) {
3262
+ case "Scalar":
3263
+ case "ScalarConstructor":
3264
+ return true;
3265
+ case "Union":
3266
+ return [...type.variants.values()].every(typeIsCallable);
3267
+ default:
3268
+ return false;
3269
+ }
3143
3270
  }
3144
3271
  }
3145
3272
  /** Check the arguments of the call expression are a single value of the given syntax. */
@@ -3264,9 +3391,13 @@ export function createChecker(program, resolver) {
3264
3391
  if (target === null) {
3265
3392
  return null;
3266
3393
  }
3267
- if (target.kind === "ScalarConstructor") {
3394
+ if (target.entityKind === "Type" && target.kind === "ScalarConstructor") {
3268
3395
  return createScalarValue(ctx, node, target);
3269
3396
  }
3397
+ else if (target.entityKind === "Value" && target.valueKind === "Function") {
3398
+ return checkFunctionCall(ctx, node, target);
3399
+ }
3400
+ compilerAssert(target.entityKind === "Type", "Expected type entity");
3270
3401
  if (relation.areScalarsRelated(target, getStdType("string"))) {
3271
3402
  return checkPrimitiveArg(node, target, "StringValue");
3272
3403
  }
@@ -3285,6 +3416,264 @@ export function createChecker(program, resolver) {
3285
3416
  return null;
3286
3417
  }
3287
3418
  }
3419
+ function checkFunctionCall(ctx, node, target) {
3420
+ const [satisfied, resolvedArgs] = checkFunctionCallArguments(ctx, node.arguments, target);
3421
+ const canCall = satisfied &&
3422
+ !ctx.hasFlags(CheckFlags.InTemplateDeclaration) &&
3423
+ !target.implementation.isDefaultFunctionImplementation;
3424
+ const fnCtx = createFunctionContext(program, node);
3425
+ if (!canCall) {
3426
+ return getDefaultFunctionResult(target.returnType);
3427
+ }
3428
+ const functionReturn = target.implementation(fnCtx, ...resolvedArgs);
3429
+ const returnIsEntity = typeof functionReturn === "object" &&
3430
+ functionReturn !== null &&
3431
+ "entityKind" in functionReturn &&
3432
+ (functionReturn.entityKind === "Type" ||
3433
+ functionReturn.entityKind === "Value" ||
3434
+ functionReturn.entityKind === "Indeterminate");
3435
+ // special case for when the return value is `undefined` and the return type is `void` or `valueof void`.
3436
+ if (functionReturn === undefined && isVoidReturn(target.returnType)) {
3437
+ return voidType;
3438
+ }
3439
+ const unmarshaled = returnIsEntity
3440
+ ? functionReturn
3441
+ : unmarshalJsToValue(program, functionReturn, function onInvalid(value) {
3442
+ let valueSummary = String(value);
3443
+ if (valueSummary.length > 30) {
3444
+ valueSummary = valueSummary.slice(0, 27) + "...";
3445
+ }
3446
+ reportCheckerDiagnostic(createDiagnostic({
3447
+ code: "function-return",
3448
+ messageId: "invalid-value",
3449
+ format: { value: valueSummary },
3450
+ target: node,
3451
+ }));
3452
+ });
3453
+ let result = unmarshaled;
3454
+ if (satisfied && result !== null)
3455
+ result = checkFunctionReturn(target, result, node);
3456
+ return result;
3457
+ }
3458
+ function isVoidReturn(constraint) {
3459
+ if (constraint.valueType) {
3460
+ return false;
3461
+ }
3462
+ if (constraint.type) {
3463
+ if (!isVoidType(constraint.type))
3464
+ return false;
3465
+ }
3466
+ return true;
3467
+ function isVoidType(type) {
3468
+ return type.kind === "Intrinsic" && type.name === "void";
3469
+ }
3470
+ }
3471
+ /**
3472
+ * Produces the default function result when a function call cannot be completed.
3473
+ *
3474
+ * This produces `null` if the function has a value type constraint but no type constraint, `errorType`
3475
+ * otherwise (if the function _could_ return a Type).
3476
+ *
3477
+ * @param constraint - The function return constraint.
3478
+ * @returns
3479
+ */
3480
+ function getDefaultFunctionResult(constraint) {
3481
+ if (constraint.valueType) {
3482
+ // If the function _can_ return a value, we will just return null. This is a bit of a hack, but it's the best fallback
3483
+ // for a function that could return a type or value, since returning a type would cause type-in-value errors.
3484
+ return null;
3485
+ }
3486
+ else {
3487
+ compilerAssert(constraint.type, "Expected function to have a return type when it did not have a value type constraint");
3488
+ // If for some reason we cannot evaluate the call, we will return the type constraint itself as a fallback.
3489
+ // This is the strongest thing we can do in the context of an error or a template declaration, since the result
3490
+ // of the function call must be assignable to the constraint, and by the transitive property if the constraint
3491
+ // is assignable in the context of evaluation, so will be the result of any valid evaluation of the implementation.
3492
+ // Technically, this isn't exactly ideal, since it could prevent function calls that return subtypes of the constraint
3493
+ // from assigning if the _result_ would be assignable. However, we have other cases where we check constraints in
3494
+ // templates to try to ensure that any instance will validate. We may need to revisit this in the future, but for
3495
+ // now, I am choosing strictness in the face of uncertainty.
3496
+ return constraint.type;
3497
+ }
3498
+ }
3499
+ function checkFunctionCallArguments(ctx, args, target) {
3500
+ let satisfied = true;
3501
+ const minArgs = target.parameters.filter((p) => !p.optional && !p.rest).length;
3502
+ const maxArgs = target.parameters[target.parameters.length - 1]?.rest
3503
+ ? undefined
3504
+ : target.parameters.length;
3505
+ if (args.length < minArgs) {
3506
+ reportCheckerDiagnostic(createDiagnostic({
3507
+ code: "invalid-argument-count",
3508
+ messageId: "atLeast",
3509
+ format: { actual: args.length.toString(), expected: minArgs.toString() },
3510
+ target: target.node,
3511
+ }));
3512
+ return [false, []];
3513
+ }
3514
+ else if (maxArgs !== undefined && args.length > maxArgs) {
3515
+ reportCheckerDiagnostic(createDiagnostic({
3516
+ code: "invalid-argument-count",
3517
+ format: { actual: args.length.toString(), expected: maxArgs.toString() },
3518
+ target: target.node,
3519
+ }));
3520
+ // This error doesn't actually prevent us from checking the arguments and evaluating the function.
3521
+ }
3522
+ const collector = createDiagnosticCollector();
3523
+ const resolvedArgs = [];
3524
+ let idx = 0;
3525
+ for (const param of target.parameters) {
3526
+ if (param.rest) {
3527
+ const constraint = extractRestParamConstraint(param.type);
3528
+ if (!constraint) {
3529
+ satisfied = false;
3530
+ continue;
3531
+ }
3532
+ const restArgExpressions = args.slice(idx);
3533
+ const restArgs = restArgExpressions.map((arg) => getTypeOrValueForNode(arg, ctx, { kind: "argument", constraint }));
3534
+ if (restArgs.some((x) => x === null)) {
3535
+ satisfied = false;
3536
+ continue;
3537
+ }
3538
+ resolvedArgs.push(...restArgs.map((v, idx) => v !== null && isValue(v) ? marshalTypeForJs(v, undefined) : v));
3539
+ }
3540
+ else {
3541
+ const arg = args[idx++];
3542
+ if (!arg) {
3543
+ if (param.optional) {
3544
+ resolvedArgs.push(undefined);
3545
+ continue;
3546
+ }
3547
+ else {
3548
+ // No need to report a diagnostic here because we already reported one for
3549
+ // invalid argument counts above.
3550
+ satisfied = false;
3551
+ continue;
3552
+ }
3553
+ }
3554
+ // Normal param
3555
+ const checkedArg = getTypeOrValueForNode(arg, ctx, {
3556
+ kind: "argument",
3557
+ constraint: param.type,
3558
+ });
3559
+ if (!checkedArg) {
3560
+ satisfied = false;
3561
+ continue;
3562
+ }
3563
+ const resolved = collector.pipe(checkEntityAssignableToConstraint(checkedArg, param.type, arg));
3564
+ satisfied &&= !!resolved;
3565
+ resolvedArgs.push(resolved
3566
+ ? isValue(resolved)
3567
+ ? marshalTypeForJs(resolved, undefined)
3568
+ : resolved
3569
+ : undefined);
3570
+ }
3571
+ }
3572
+ reportCheckerDiagnostics(collector.diagnostics);
3573
+ return [satisfied, resolvedArgs];
3574
+ }
3575
+ function checkFunctionReturn(target, result, diagnosticTarget) {
3576
+ const [checked, diagnostics] = checkEntityAssignableToConstraint(result, target.returnType, diagnosticTarget);
3577
+ if (diagnostics.length > 0) {
3578
+ reportCheckerDiagnostic(createDiagnostic({
3579
+ code: "function-return",
3580
+ messageId: "unassignable",
3581
+ format: {
3582
+ name: getEntityName(target, { printable: true }),
3583
+ entityKind: result.entityKind.toLowerCase(),
3584
+ return: getEntityName(result, { printable: true }),
3585
+ type: getEntityName(target.returnType, { printable: true }),
3586
+ },
3587
+ target: diagnosticTarget,
3588
+ }));
3589
+ }
3590
+ return checked;
3591
+ }
3592
+ function checkEntityAssignableToConstraint(entity, constraint, diagnosticTarget) {
3593
+ const constraintIsValue = !!constraint.valueType;
3594
+ const constraintIsType = !!constraint.type;
3595
+ const collector = createDiagnosticCollector();
3596
+ switch (true) {
3597
+ case constraintIsValue && constraintIsType: {
3598
+ const tried = tryAssignValue();
3599
+ if (tried[0] !== null || entity.entityKind === "Value") {
3600
+ // Succeeded as value or is a value
3601
+ return tried;
3602
+ }
3603
+ // Now we are guaranteed a type.
3604
+ const typeEntity = entity.entityKind === "Indeterminate" ? entity.type : entity;
3605
+ const assignable = collector.pipe(relation.isTypeAssignableTo(typeEntity, constraint.type, diagnosticTarget));
3606
+ return collector.wrap(assignable ? typeEntity : null);
3607
+ }
3608
+ case constraintIsValue: {
3609
+ const normed = collector.pipe(normalizeValue(entity, constraint, diagnosticTarget));
3610
+ // Error should have been reported in normalizeValue
3611
+ if (!normed)
3612
+ return collector.wrap(null);
3613
+ const assignable = collector.pipe(relation.isValueOfType(normed, constraint.valueType, diagnosticTarget));
3614
+ return collector.wrap(assignable ? normed : null);
3615
+ }
3616
+ case constraintIsType: {
3617
+ if (entity.entityKind === "Indeterminate")
3618
+ entity = entity.type;
3619
+ if (entity.entityKind !== "Type") {
3620
+ collector.add(createDiagnostic({
3621
+ code: "value-in-type",
3622
+ format: { name: getTypeName(entity.type) },
3623
+ target: diagnosticTarget,
3624
+ }));
3625
+ return collector.wrap(null);
3626
+ }
3627
+ const assignable = collector.pipe(relation.isTypeAssignableTo(entity, constraint.type, diagnosticTarget));
3628
+ return collector.wrap(assignable ? entity : null);
3629
+ }
3630
+ default: {
3631
+ compilerAssert(false, "Expected at least one of type or value constraint to be defined.");
3632
+ }
3633
+ }
3634
+ function tryAssignValue() {
3635
+ const collector = createDiagnosticCollector();
3636
+ const normed = collector.pipe(normalizeValue(entity, constraint, diagnosticTarget));
3637
+ const assignable = normed
3638
+ ? collector.pipe(relation.isValueOfType(normed, constraint.valueType, diagnosticTarget))
3639
+ : false;
3640
+ return collector.wrap(assignable ? normed : null);
3641
+ }
3642
+ }
3643
+ function normalizeValue(entity, constraint, diagnosticTarget) {
3644
+ if (entity.entityKind === "Value")
3645
+ return [entity, []];
3646
+ if (entity.entityKind === "Indeterminate") {
3647
+ // Coerce to a value
3648
+ const coerced = getValueFromIndeterminate(entity.type, constraint.type && { kind: "argument", type: constraint.type }, entity.type.node);
3649
+ if (coerced?.entityKind !== "Value") {
3650
+ return [
3651
+ null,
3652
+ [
3653
+ createDiagnostic({
3654
+ code: "expect-value",
3655
+ format: { name: getTypeName(entity.type) },
3656
+ target: diagnosticTarget,
3657
+ }),
3658
+ ],
3659
+ ];
3660
+ }
3661
+ return [coerced, []];
3662
+ }
3663
+ if (entity.entityKind === "Type") {
3664
+ return [
3665
+ null,
3666
+ [
3667
+ createDiagnostic({
3668
+ code: "expect-value",
3669
+ format: { name: getTypeName(entity) },
3670
+ target: diagnosticTarget,
3671
+ }),
3672
+ ],
3673
+ ];
3674
+ }
3675
+ compilerAssert(false, `Unreachable: unexpected entity kind '${entity.entityKind}'`);
3676
+ }
3288
3677
  function checkTypeOfExpression(ctx, node) {
3289
3678
  const entity = checkNode(ctx, node.target, undefined);
3290
3679
  if (entity === null) {
@@ -3666,7 +4055,7 @@ export function createChecker(program, resolver) {
3666
4055
  // if the prop type is an error we don't need to validate again.
3667
4056
  return null;
3668
4057
  }
3669
- const defaultValue = getValueForNode(defaultNode, ctx.mapper, {
4058
+ const defaultValue = getValueForNode(defaultNode, ctx, {
3670
4059
  kind: "assignment",
3671
4060
  type,
3672
4061
  });
@@ -3792,20 +4181,23 @@ export function createChecker(program, resolver) {
3792
4181
  }
3793
4182
  const resolvedArgs = [];
3794
4183
  function resolveArg(argNode, perParamType) {
3795
- const arg = getTypeOrValueForNode(argNode, ctx.mapper, {
4184
+ const arg = getTypeOrValueForNode(argNode, ctx, {
3796
4185
  kind: "argument",
3797
4186
  constraint: perParamType,
3798
4187
  });
3799
4188
  if (arg !== null &&
3800
4189
  !(isType(arg) && isErrorType(arg)) &&
3801
4190
  checkArgumentAssignable(arg, perParamType, argNode)) {
4191
+ const [valid, jsValue] = resolveArgumentJsValue(arg, extractValueOfConstraints({
4192
+ kind: "argument",
4193
+ constraint: perParamType,
4194
+ }), argNode);
4195
+ if (!valid)
4196
+ return undefined;
3802
4197
  return {
3803
4198
  value: arg,
3804
4199
  node: argNode,
3805
- jsValue: resolveDecoratorArgJsValue(arg, extractValueOfConstraints({
3806
- kind: "argument",
3807
- constraint: perParamType,
3808
- })),
4200
+ jsValue,
3809
4201
  };
3810
4202
  }
3811
4203
  else {
@@ -3873,16 +4265,17 @@ export function createChecker(program, resolver) {
3873
4265
  function getIndexType(type) {
3874
4266
  return type.kind === "Model" ? type.indexer?.value : undefined;
3875
4267
  }
3876
- function resolveDecoratorArgJsValue(value, valueConstraint) {
4268
+ function resolveArgumentJsValue(value, valueConstraint, diagnosticTarget) {
3877
4269
  if (valueConstraint !== undefined) {
3878
4270
  if (isValue(value)) {
3879
- return marshallTypeForJS(value, valueConstraint.type);
4271
+ const unmarshaled = marshalTypeForJs(value, valueConstraint.type);
4272
+ return [true, unmarshaled];
3880
4273
  }
3881
4274
  else {
3882
- return value;
4275
+ return [true, value];
3883
4276
  }
3884
4277
  }
3885
- return value;
4278
+ return [true, value];
3886
4279
  }
3887
4280
  function checkArgumentAssignable(argumentType, parameterType, diagnosticTarget) {
3888
4281
  const [valid] = relation.isTypeAssignableTo(argumentType, parameterType, diagnosticTarget);
@@ -4184,6 +4577,7 @@ export function createChecker(program, resolver) {
4184
4577
  case "EnumValue":
4185
4578
  case "NullValue":
4186
4579
  case "ScalarValue":
4580
+ case "Function":
4187
4581
  return value;
4188
4582
  }
4189
4583
  }
@@ -5154,25 +5548,70 @@ function applyDecoratorToType(program, decApp, target) {
5154
5548
  }
5155
5549
  }
5156
5550
  }
5157
- function createDecoratorContext(program, decApp) {
5158
- function createPassThruContext(program, decApp) {
5159
- return {
5160
- program,
5161
- decoratorTarget: decApp.node,
5162
- getArgumentTarget: () => decApp.node,
5163
- call: (decorator, target, ...args) => {
5164
- return decorator(createPassThruContext(program, decApp), target, ...args);
5165
- },
5166
- };
5167
- }
5551
+ function createPassThruContexts(program, target) {
5552
+ const decCtx = {
5553
+ program,
5554
+ decoratorTarget: target,
5555
+ getArgumentTarget: () => target,
5556
+ call: (decorator, target, ...args) => {
5557
+ return decCtx.callDecorator(decorator, target, ...args);
5558
+ },
5559
+ callDecorator(decorator, target, ...args) {
5560
+ return decorator(decCtx, target, ...args);
5561
+ },
5562
+ callFunction(fn, ...args) {
5563
+ return fn(fnCtx, ...args);
5564
+ },
5565
+ };
5566
+ const fnCtx = {
5567
+ program,
5568
+ functionCallTarget: target,
5569
+ getArgumentTarget: () => target,
5570
+ callFunction(fn, ...args) {
5571
+ return fn(fnCtx, ...args);
5572
+ },
5573
+ callDecorator(decorator, target, ...args) {
5574
+ return decorator(decCtx, target, ...args);
5575
+ },
5576
+ };
5168
5577
  return {
5578
+ decorator: decCtx,
5579
+ function: fnCtx,
5580
+ };
5581
+ }
5582
+ function createDecoratorContext(program, decApp) {
5583
+ const passthrough = createPassThruContexts(program, decApp.node);
5584
+ const decCtx = {
5169
5585
  program,
5170
5586
  decoratorTarget: decApp.node,
5171
5587
  getArgumentTarget: (index) => {
5172
5588
  return decApp.args[index]?.node;
5173
5589
  },
5174
5590
  call: (decorator, target, ...args) => {
5175
- return decorator(createPassThruContext(program, decApp), target, ...args);
5591
+ return decCtx.callDecorator(decorator, target, ...args);
5592
+ },
5593
+ callDecorator: (decorator, target, ...args) => {
5594
+ return decorator(passthrough.decorator, target, ...args);
5595
+ },
5596
+ callFunction(fn, ...args) {
5597
+ return fn(passthrough.function, ...args);
5598
+ },
5599
+ };
5600
+ return decCtx;
5601
+ }
5602
+ function createFunctionContext(program, fnCall) {
5603
+ const passthrough = createPassThruContexts(program, fnCall);
5604
+ return {
5605
+ program,
5606
+ functionCallTarget: fnCall,
5607
+ getArgumentTarget: (index) => {
5608
+ return fnCall.arguments[index];
5609
+ },
5610
+ callDecorator(decorator, target, ...args) {
5611
+ return decorator(passthrough.decorator, target, ...args);
5612
+ },
5613
+ callFunction(fn, ...args) {
5614
+ return fn(passthrough.function, ...args);
5176
5615
  },
5177
5616
  };
5178
5617
  }