@typespec/compiler 1.10.0-dev.3 → 1.10.0-dev.6
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.
- package/dist/generated-defs/TypeSpec.Prototypes.ts-test.js +1 -1
- package/dist/generated-defs/TypeSpec.Prototypes.ts-test.js.map +1 -1
- package/dist/generated-defs/TypeSpec.ts-test.js +1 -1
- package/dist/generated-defs/TypeSpec.ts-test.js.map +1 -1
- package/dist/manifest.js +2 -2
- package/dist/src/core/binder.d.ts.map +1 -1
- package/dist/src/core/binder.js +10 -8
- package/dist/src/core/binder.js.map +1 -1
- package/dist/src/core/checker.d.ts.map +1 -1
- package/dist/src/core/checker.js +485 -46
- package/dist/src/core/checker.js.map +1 -1
- package/dist/src/core/helpers/type-name-utils.d.ts.map +1 -1
- package/dist/src/core/helpers/type-name-utils.js +13 -0
- package/dist/src/core/helpers/type-name-utils.js.map +1 -1
- package/dist/src/core/js-marshaller.d.ts +4 -2
- package/dist/src/core/js-marshaller.d.ts.map +1 -1
- package/dist/src/core/js-marshaller.js +96 -6
- package/dist/src/core/js-marshaller.js.map +1 -1
- package/dist/src/core/messages.d.ts +101 -12
- package/dist/src/core/messages.d.ts.map +1 -1
- package/dist/src/core/messages.js +27 -2
- package/dist/src/core/messages.js.map +1 -1
- package/dist/src/core/name-resolver.d.ts.map +1 -1
- package/dist/src/core/name-resolver.js +30 -0
- package/dist/src/core/name-resolver.js.map +1 -1
- package/dist/src/core/parser.d.ts.map +1 -1
- package/dist/src/core/parser.js +34 -6
- package/dist/src/core/parser.js.map +1 -1
- package/dist/src/core/scanner.d.ts +5 -5
- package/dist/src/core/scanner.d.ts.map +1 -1
- package/dist/src/core/scanner.js +8 -8
- package/dist/src/core/scanner.js.map +1 -1
- package/dist/src/core/semantic-walker.d.ts +3 -3
- package/dist/src/core/semantic-walker.d.ts.map +1 -1
- package/dist/src/core/semantic-walker.js +63 -44
- package/dist/src/core/semantic-walker.js.map +1 -1
- package/dist/src/core/type-relation-checker.d.ts.map +1 -1
- package/dist/src/core/type-relation-checker.js +157 -10
- package/dist/src/core/type-relation-checker.js.map +1 -1
- package/dist/src/core/types.d.ts +187 -23
- package/dist/src/core/types.d.ts.map +1 -1
- package/dist/src/core/types.js +1 -0
- package/dist/src/core/types.js.map +1 -1
- package/dist/src/experimental/typekit/index.d.ts.map +1 -1
- package/dist/src/experimental/typekit/index.js.map +1 -1
- package/dist/src/formatter/print/printer.d.ts.map +1 -1
- package/dist/src/formatter/print/printer.js +13 -0
- package/dist/src/formatter/print/printer.js.map +1 -1
- package/dist/src/index.d.ts +3 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/lib/examples.d.ts +10 -3
- package/dist/src/lib/examples.d.ts.map +1 -1
- package/dist/src/lib/examples.js +22 -7
- package/dist/src/lib/examples.js.map +1 -1
- package/dist/src/server/type-signature.js +19 -0
- package/dist/src/server/type-signature.js.map +1 -1
- package/package.json +1 -1
- package/templates/__snapshots__/emitter-ts/eslint.config.js +10 -1
- package/templates/__snapshots__/emitter-ts/package.json +1 -2
- package/templates/__snapshots__/emitter-ts/test/test-host.ts +7 -34
- package/templates/__snapshots__/library-ts/eslint.config.js +10 -1
- package/templates/__snapshots__/library-ts/package.json +1 -2
- package/templates/__snapshots__/library-ts/test/decorators.test.ts +18 -23
- package/templates/__snapshots__/library-ts/test/test-host.ts +5 -15
- package/templates/emitter-ts/eslint.config.js +10 -1
- package/templates/emitter-ts/package.json +1 -2
- package/templates/emitter-ts/test/test-host.ts.mu +7 -34
- package/templates/library-ts/eslint.config.js +10 -1
- package/templates/library-ts/package.json +1 -2
- package/templates/library-ts/test/decorators.test.ts.mu +18 -23
- package/templates/library-ts/test/test-host.ts.mu +5 -15
package/dist/src/core/checker.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
424
|
-
|
|
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,
|
|
481
|
-
const ctx = CheckContext.from(
|
|
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
|
-
|
|
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,
|
|
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 (
|
|
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
|
-
|
|
1359
|
-
|
|
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
|
|
2217
|
-
return !(sym.flags &
|
|
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 =
|
|
3133
|
-
if (target.
|
|
3134
|
-
|
|
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:
|
|
3244
|
+
format: { type: kind },
|
|
3140
3245
|
target: node.target,
|
|
3141
3246
|
}));
|
|
3142
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
4268
|
+
function resolveArgumentJsValue(value, valueConstraint, diagnosticTarget) {
|
|
3877
4269
|
if (valueConstraint !== undefined) {
|
|
3878
4270
|
if (isValue(value)) {
|
|
3879
|
-
|
|
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
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
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
|
|
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
|
}
|