@typespec/compiler 0.60.0-dev.9 → 0.60.0

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.
@@ -0,0 +1,643 @@
1
+ import { MultiKeyMap } from "../utils/misc.js";
2
+ import { walkPropertiesInherited } from "./checker.js";
3
+ import { compilerAssert, reportDeprecated } from "./diagnostics.js";
4
+ import { getEntityName, getTypeName } from "./helpers/type-name-utils.js";
5
+ import { getMaxItems, getMaxLength, getMaxValueAsNumeric, getMaxValueExclusiveAsNumeric, getMinItems, getMinLength, getMinValueAsNumeric, getMinValueExclusiveAsNumeric, } from "./intrinsic-type-state.js";
6
+ import { createDiagnostic } from "./messages.js";
7
+ import { numericRanges } from "./numeric-ranges.js";
8
+ import { isArrayModelType, isNeverType, isUnknownType, isValue, isVoidType } from "./type-utils.js";
9
+ import { NoTarget, SyntaxKind, } from "./types.js";
10
+ var Related;
11
+ (function (Related) {
12
+ Related[Related["false"] = 0] = "false";
13
+ Related[Related["true"] = 1] = "true";
14
+ Related[Related["maybe"] = 2] = "maybe";
15
+ })(Related || (Related = {}));
16
+ /**
17
+ * Mapping from the reflection models to Type["kind"] value
18
+ */
19
+ const ReflectionNameToKind = {
20
+ Enum: "Enum",
21
+ EnumMember: "EnumMember",
22
+ Interface: "Interface",
23
+ Model: "Model",
24
+ ModelProperty: "ModelProperty",
25
+ Namespace: "Namespace",
26
+ Operation: "Operation",
27
+ Scalar: "Scalar",
28
+ TemplateParameter: "TemplateParameter",
29
+ Tuple: "Tuple",
30
+ Union: "Union",
31
+ UnionVariant: "UnionVariant",
32
+ };
33
+ const _assertReflectionNameToKind = ReflectionNameToKind;
34
+ export function createTypeRelationChecker(program, checker) {
35
+ return {
36
+ isTypeAssignableTo,
37
+ isValueOfType,
38
+ isReflectionType,
39
+ areScalarsRelated,
40
+ };
41
+ /**
42
+ * Check if the source type can be assigned to the target type.
43
+ * @param source Source type
44
+ * @param target Target type
45
+ * @param diagnosticTarget Target for the diagnostic, unless something better can be inferred.
46
+ */
47
+ function isTypeAssignableTo(source, target, diagnosticTarget) {
48
+ const [related, errors] = isTypeAssignableToInternal(source, target, diagnosticTarget, new MultiKeyMap());
49
+ return [related === Related.true, convertErrorsToDiagnostics(errors, diagnosticTarget)];
50
+ }
51
+ function isTargetChildOf(target, base) {
52
+ const errorNode = "kind" in target && typeof target.kind === "number" ? target : target.node;
53
+ const baseNode = "kind" in base && typeof base.kind === "number" ? base : base.node;
54
+ let currentNode = errorNode;
55
+ while (currentNode) {
56
+ if (currentNode === baseNode) {
57
+ return true;
58
+ }
59
+ currentNode = currentNode.parent;
60
+ }
61
+ return false;
62
+ }
63
+ function convertErrorsToDiagnostics(errors, diagnosticBase) {
64
+ return errors.flatMap((x) => convertErrorToDiagnostic(x, diagnosticBase));
65
+ }
66
+ function combineErrorMessage(error) {
67
+ let message = error.message;
68
+ let current = error.children[0];
69
+ let indent = " ";
70
+ while (current !== undefined) {
71
+ message += current.message
72
+ .split("\n")
73
+ .map((line) => `\n${indent}${line}`)
74
+ .join("");
75
+ indent += " ";
76
+ current = current.children[0];
77
+ }
78
+ return message;
79
+ }
80
+ function flattenErrors(error, diagnosticBase) {
81
+ if (!isTargetChildOf(error.target, diagnosticBase)) {
82
+ return [{ ...error, target: diagnosticBase }];
83
+ }
84
+ if (error.children.length === 0) {
85
+ return [error];
86
+ }
87
+ return error.children.flatMap((x) => flattenErrors(x, error.target));
88
+ }
89
+ function convertErrorToDiagnostic(error, diagnosticBase) {
90
+ const flattened = flattenErrors(error, diagnosticBase);
91
+ return flattened.map((error) => {
92
+ const messageBase = error.skipIfFirst && error.children.length > 0 ? error.children[0] : error;
93
+ const message = combineErrorMessage(messageBase);
94
+ return {
95
+ severity: "error",
96
+ code: error.code,
97
+ message: message,
98
+ target: error.target,
99
+ };
100
+ });
101
+ }
102
+ /**
103
+ * Check if the given Value type is of the given type.
104
+ * @param source Value
105
+ * @param target Target type
106
+ * @param diagnosticTarget Target for the diagnostic, unless something better can be inferred.
107
+ */
108
+ function isValueOfType(source, target, diagnosticTarget) {
109
+ const [related, errors] = isValueOfTypeInternal(source, target, diagnosticTarget, new MultiKeyMap());
110
+ return [related === Related.true, convertErrorsToDiagnostics(errors, diagnosticTarget)];
111
+ }
112
+ function isTypeAssignableToInternal(source, target, diagnosticTarget, relationCache) {
113
+ const cached = relationCache.get([source, target]);
114
+ if (cached !== undefined) {
115
+ return [cached, []];
116
+ }
117
+ const [result, diagnostics] = isTypeAssignableToWorker(source, target, diagnosticTarget, new MultiKeyMap());
118
+ relationCache.set([source, target], result);
119
+ return [result, diagnostics];
120
+ }
121
+ function isTypeAssignableToWorker(source, target, diagnosticTarget, relationCache) {
122
+ // BACKCOMPAT: Allow certain type to be accepted as values
123
+ if ("kind" in source &&
124
+ "entityKind" in target &&
125
+ source.kind === "TemplateParameter" &&
126
+ source.constraint?.type &&
127
+ source.constraint.valueType === undefined &&
128
+ target.entityKind === "MixedParameterConstraint" &&
129
+ target.valueType) {
130
+ const [assignable] = isTypeAssignableToInternal(source.constraint.type, target.valueType, diagnosticTarget, relationCache);
131
+ if (assignable) {
132
+ const constraint = getEntityName(source.constraint);
133
+ reportDeprecated(program, `Template constrainted to '${constraint}' will not be assignable to '${getEntityName(target)}' in the future. Update the constraint to be 'valueof ${constraint}'`, diagnosticTarget);
134
+ return [Related.true, []];
135
+ }
136
+ }
137
+ if ("kind" in source && source.kind === "TemplateParameter") {
138
+ source = source.constraint ?? checker.anyType;
139
+ }
140
+ if (target.entityKind === "Indeterminate") {
141
+ target = target.type;
142
+ }
143
+ if (source === target)
144
+ return [Related.true, []];
145
+ if (isValue(target)) {
146
+ return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
147
+ }
148
+ if (source.entityKind === "Indeterminate") {
149
+ return isIndeterminateEntityAssignableTo(source, target, diagnosticTarget, relationCache);
150
+ }
151
+ if (target.entityKind === "MixedParameterConstraint") {
152
+ return isAssignableToMixedParameterConstraint(source, target, diagnosticTarget, relationCache);
153
+ }
154
+ if (isValue(source) || (source.entityKind === "MixedParameterConstraint" && source.valueType)) {
155
+ return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
156
+ }
157
+ if (source.entityKind === "MixedParameterConstraint") {
158
+ return isTypeAssignableToInternal(source.type, target, diagnosticTarget, relationCache);
159
+ }
160
+ const isSimpleTypeRelated = isSimpleTypeAssignableTo(source, target);
161
+ if (isSimpleTypeRelated === true) {
162
+ return [Related.true, []];
163
+ }
164
+ else if (isSimpleTypeRelated === false) {
165
+ return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
166
+ }
167
+ if (source.kind === "Union") {
168
+ for (const variant of source.variants.values()) {
169
+ const [variantAssignable] = isTypeAssignableToInternal(variant.type, target, diagnosticTarget, relationCache);
170
+ if (!variantAssignable) {
171
+ return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
172
+ }
173
+ }
174
+ return [Related.true, []];
175
+ }
176
+ if (target.kind === "Model" &&
177
+ source.kind === "Model" &&
178
+ target.name !== "object" &&
179
+ target.indexer === undefined &&
180
+ source.indexer &&
181
+ source.indexer.key.name === "integer") {
182
+ return [
183
+ Related.false,
184
+ [
185
+ createTypeRelationError({
186
+ code: "missing-index",
187
+ format: {
188
+ indexType: getTypeName(source.indexer.key),
189
+ sourceType: getTypeName(target),
190
+ },
191
+ diagnosticTarget,
192
+ }),
193
+ ],
194
+ ];
195
+ }
196
+ else if (target.kind === "Model" &&
197
+ isArrayModelType(program, target) &&
198
+ source.kind === "Model") {
199
+ if (isArrayModelType(program, source)) {
200
+ return hasIndexAndIsAssignableTo(source, target, diagnosticTarget, relationCache);
201
+ }
202
+ else {
203
+ // For other models just fallback to unassignable
204
+ return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
205
+ }
206
+ }
207
+ else if (target.kind === "Model" && source.kind === "Model") {
208
+ return areModelsRelated(source, target, diagnosticTarget, relationCache);
209
+ }
210
+ else if (target.kind === "Model" &&
211
+ isArrayModelType(program, target) &&
212
+ source.kind === "Tuple") {
213
+ return isTupleAssignableToArray(source, target, diagnosticTarget, relationCache);
214
+ }
215
+ else if (target.kind === "Tuple" && source.kind === "Tuple") {
216
+ return isTupleAssignableToTuple(source, target, diagnosticTarget, relationCache);
217
+ }
218
+ else if (target.kind === "Union") {
219
+ return isAssignableToUnion(source, target, diagnosticTarget, relationCache);
220
+ }
221
+ else if (target.kind === "Enum") {
222
+ return isAssignableToEnum(source, target, diagnosticTarget);
223
+ }
224
+ return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
225
+ }
226
+ function isIndeterminateEntityAssignableTo(indeterminate, target, diagnosticTarget, relationCache) {
227
+ const [typeRelated, typeDiagnostics] = isTypeAssignableToInternal(indeterminate.type, target, diagnosticTarget, relationCache);
228
+ if (typeRelated) {
229
+ return [Related.true, []];
230
+ }
231
+ if (target.entityKind === "MixedParameterConstraint" && target.valueType) {
232
+ const [valueRelated] = isTypeAssignableToInternal(indeterminate.type, target.valueType, diagnosticTarget, relationCache);
233
+ if (valueRelated) {
234
+ return [Related.true, []];
235
+ }
236
+ }
237
+ return [Related.false, typeDiagnostics];
238
+ }
239
+ function isAssignableToValueType(source, target, diagnosticTarget, relationCache) {
240
+ if (!isValue(source)) {
241
+ return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
242
+ }
243
+ return isValueOfTypeInternal(source, target, diagnosticTarget, relationCache);
244
+ }
245
+ function isAssignableToMixedParameterConstraint(source, target, diagnosticTarget, relationCache) {
246
+ if ("entityKind" in source && source.entityKind === "MixedParameterConstraint") {
247
+ if (source.type && target.type) {
248
+ const [variantAssignable, diagnostics] = isTypeAssignableToInternal(source.type, target.type, diagnosticTarget, relationCache);
249
+ if (variantAssignable === Related.false) {
250
+ return [Related.false, diagnostics];
251
+ }
252
+ return [Related.true, []];
253
+ }
254
+ if (source.valueType && target.valueType) {
255
+ const [variantAssignable, diagnostics] = isTypeAssignableToInternal(source.valueType, target.valueType, diagnosticTarget, relationCache);
256
+ if (variantAssignable === Related.false) {
257
+ return [Related.false, diagnostics];
258
+ }
259
+ return [Related.true, []];
260
+ }
261
+ return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
262
+ }
263
+ if (target.type) {
264
+ const [related] = isTypeAssignableToInternal(source, target.type, diagnosticTarget, relationCache);
265
+ if (related) {
266
+ return [Related.true, []];
267
+ }
268
+ }
269
+ if (target.valueType) {
270
+ const [related] = isAssignableToValueType(source, target.valueType, diagnosticTarget, relationCache);
271
+ if (related) {
272
+ return [Related.true, []];
273
+ }
274
+ }
275
+ return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
276
+ }
277
+ /** Check if the value is assignable to the given type. */
278
+ function isValueOfTypeInternal(source, target, diagnosticTarget, relationCache) {
279
+ return isTypeAssignableToInternal(source.type, target, diagnosticTarget, relationCache);
280
+ }
281
+ function isReflectionType(type) {
282
+ return (type.kind === "Model" &&
283
+ type.namespace?.name === "Reflection" &&
284
+ type.namespace?.namespace?.name === "TypeSpec");
285
+ }
286
+ function isRelatedToScalar(source, target) {
287
+ switch (source.kind) {
288
+ case "Number":
289
+ return isNumericLiteralRelatedTo(source, target);
290
+ case "String":
291
+ case "StringTemplate":
292
+ return isStringLiteralRelatedTo(source, target);
293
+ case "Boolean":
294
+ return areScalarsRelated(target, checker.getStdType("boolean"));
295
+ case "Scalar":
296
+ return areScalarsRelated(source, target);
297
+ case "Union":
298
+ return undefined;
299
+ default:
300
+ return false;
301
+ }
302
+ }
303
+ function areScalarsRelated(source, target) {
304
+ let current = source;
305
+ while (current) {
306
+ if (current === target) {
307
+ return true;
308
+ }
309
+ current = current.baseScalar;
310
+ }
311
+ return false;
312
+ }
313
+ function isSimpleTypeAssignableTo(source, target) {
314
+ if (isNeverType(source))
315
+ return true;
316
+ if (isVoidType(target))
317
+ return false;
318
+ if (isUnknownType(target))
319
+ return true;
320
+ if (isReflectionType(target)) {
321
+ return source.kind === ReflectionNameToKind[target.name];
322
+ }
323
+ if (target.kind === "Scalar") {
324
+ return isRelatedToScalar(source, target);
325
+ }
326
+ if (source.kind === "Scalar" && target.kind === "Model") {
327
+ return false;
328
+ }
329
+ if (target.kind === "String") {
330
+ return ((source.kind === "String" && source.value === target.value) ||
331
+ (source.kind === "StringTemplate" && source.stringValue === target.value));
332
+ }
333
+ if (target.kind === "StringTemplate" && target.stringValue) {
334
+ return ((source.kind === "String" && source.value === target.stringValue) ||
335
+ (source.kind === "StringTemplate" && source.stringValue === target.stringValue));
336
+ }
337
+ if (target.kind === "Number") {
338
+ return source.kind === "Number" && target.value === source.value;
339
+ }
340
+ return undefined;
341
+ }
342
+ function isNumericLiteralRelatedTo(source, target) {
343
+ // First check that the source numeric literal is assignable to the target scalar
344
+ if (!isNumericAssignableToNumericScalar(source.numericValue, target)) {
345
+ return false;
346
+ }
347
+ const min = getMinValueAsNumeric(program, target);
348
+ const max = getMaxValueAsNumeric(program, target);
349
+ const minExclusive = getMinValueExclusiveAsNumeric(program, target);
350
+ const maxExclusive = getMaxValueExclusiveAsNumeric(program, target);
351
+ if (min && source.numericValue.lt(min)) {
352
+ return false;
353
+ }
354
+ if (minExclusive && source.numericValue.lte(minExclusive)) {
355
+ return false;
356
+ }
357
+ if (max && source.numericValue.gt(max)) {
358
+ return false;
359
+ }
360
+ if (maxExclusive && source.numericValue.gte(maxExclusive)) {
361
+ return false;
362
+ }
363
+ return true;
364
+ }
365
+ function isNumericAssignableToNumericScalar(source, target) {
366
+ // if the target does not derive from numeric, then it can't be assigned a numeric literal
367
+ if (!areScalarsRelated(target.projectionBase ?? target, checker.getStdType("numeric"))) {
368
+ return false;
369
+ }
370
+ // With respect to literal assignability a custom numeric scalar is
371
+ // equivalent to its nearest TypeSpec.* base. Adjust target accordingly.
372
+ while (!target.namespace || !isTypeSpecNamespace(target.namespace)) {
373
+ compilerAssert(target.baseScalar, "Should not be possible to be derived from TypeSpec.numeric and not have a base when not in TypeSpec namespace.");
374
+ target = target.baseScalar;
375
+ }
376
+ if (target.name === "numeric")
377
+ return true;
378
+ if (target.name === "decimal")
379
+ return true;
380
+ if (target.name === "decimal128")
381
+ return true;
382
+ const isInt = source.isInteger;
383
+ if (target.name === "integer")
384
+ return isInt;
385
+ if (target.name === "float")
386
+ return true;
387
+ if (!(target.name in numericRanges))
388
+ return false;
389
+ const [low, high, options] = numericRanges[target.name];
390
+ return source.gte(low) && source.lte(high) && (!options.int || isInt);
391
+ }
392
+ function isStringLiteralRelatedTo(source, target) {
393
+ if (!areScalarsRelated(target.projectionBase ?? target, checker.getStdType("string"))) {
394
+ return false;
395
+ }
396
+ if (source.kind === "StringTemplate") {
397
+ return true;
398
+ }
399
+ const len = source.value.length;
400
+ const min = getMinLength(program, target);
401
+ const max = getMaxLength(program, target);
402
+ if (min && len < min) {
403
+ return false;
404
+ }
405
+ if (max && len > max) {
406
+ return false;
407
+ }
408
+ return true;
409
+ }
410
+ function areModelsRelated(source, target, diagnosticTarget, relationCache) {
411
+ relationCache.set([source, target], Related.maybe);
412
+ const errors = [];
413
+ const remainingProperties = new Map(source.properties);
414
+ for (const prop of walkPropertiesInherited(target)) {
415
+ const sourceProperty = getProperty(source, prop.name);
416
+ if (sourceProperty === undefined) {
417
+ if (!prop.optional) {
418
+ errors.push(createTypeRelationError({
419
+ code: "missing-property",
420
+ format: {
421
+ propertyName: prop.name,
422
+ sourceType: getTypeName(source),
423
+ targetType: getTypeName(target),
424
+ },
425
+ diagnosticTarget: source,
426
+ }));
427
+ }
428
+ }
429
+ else {
430
+ remainingProperties.delete(prop.name);
431
+ if (sourceProperty.optional && !prop.optional) {
432
+ errors.push(createTypeRelationError({
433
+ code: "property-required",
434
+ format: {
435
+ propName: prop.name,
436
+ targetType: getTypeName(target),
437
+ },
438
+ diagnosticTarget,
439
+ }));
440
+ }
441
+ const [related, propErrors] = isTypeAssignableToInternal(sourceProperty.type, prop.type, diagnosticTarget, relationCache);
442
+ if (!related) {
443
+ errors.push(...wrapUnassignablePropertyErrors(sourceProperty, propErrors));
444
+ }
445
+ }
446
+ }
447
+ if (target.indexer) {
448
+ const [_, indexerDiagnostics] = arePropertiesAssignableToIndexer(remainingProperties, target.indexer.value, diagnosticTarget, relationCache);
449
+ errors.push(...indexerDiagnostics);
450
+ // For anonymous models we don't need an indexer
451
+ if (source.name !== "" && target.indexer.key.name !== "integer") {
452
+ const [related, indexDiagnostics] = hasIndexAndIsAssignableTo(source, target, diagnosticTarget, relationCache);
453
+ if (!related) {
454
+ errors.push(...indexDiagnostics);
455
+ }
456
+ }
457
+ }
458
+ else if (shouldCheckExcessProperties(source)) {
459
+ for (const [propName, prop] of remainingProperties) {
460
+ if (shouldCheckExcessProperty(prop)) {
461
+ errors.push(createTypeRelationError({
462
+ code: "unexpected-property",
463
+ format: {
464
+ propertyName: propName,
465
+ type: getEntityName(target),
466
+ },
467
+ diagnosticTarget: prop,
468
+ }));
469
+ }
470
+ }
471
+ }
472
+ return [
473
+ errors.length === 0 ? Related.true : Related.false,
474
+ wrapUnassignableErrors(source, target, errors),
475
+ ];
476
+ }
477
+ /** If we should check for excess properties on the given model. */
478
+ function shouldCheckExcessProperties(model) {
479
+ return model.node?.kind === SyntaxKind.ObjectLiteral;
480
+ }
481
+ /** If we should check for this specific property */
482
+ function shouldCheckExcessProperty(prop) {
483
+ return (prop.node?.kind === SyntaxKind.ObjectLiteralProperty && prop.node.parent === prop.model?.node);
484
+ }
485
+ function getProperty(model, name) {
486
+ return (model.properties.get(name) ??
487
+ (model.baseModel !== undefined ? getProperty(model.baseModel, name) : undefined));
488
+ }
489
+ function arePropertiesAssignableToIndexer(properties, indexerConstaint, diagnosticTarget, relationCache) {
490
+ for (const prop of properties.values()) {
491
+ const [related, diagnostics] = isTypeAssignableToInternal(prop.type, indexerConstaint, diagnosticTarget, relationCache);
492
+ if (!related) {
493
+ return [Related.false, diagnostics];
494
+ }
495
+ }
496
+ return [Related.true, []];
497
+ }
498
+ /** Check that the source model has an index, the index key match and the value of the source index is assignable to the target index. */
499
+ function hasIndexAndIsAssignableTo(source, target, diagnosticTarget, relationCache) {
500
+ if (source.indexer === undefined || source.indexer.key !== target.indexer.key) {
501
+ return [
502
+ Related.false,
503
+ [
504
+ createTypeRelationError({
505
+ code: "missing-index",
506
+ format: {
507
+ indexType: getTypeName(target.indexer.key),
508
+ sourceType: getTypeName(source),
509
+ },
510
+ diagnosticTarget,
511
+ }),
512
+ ],
513
+ ];
514
+ }
515
+ return isTypeAssignableToInternal(source.indexer.value, target.indexer.value, diagnosticTarget, relationCache);
516
+ }
517
+ function isTupleAssignableToArray(source, target, diagnosticTarget, relationCache) {
518
+ const minItems = getMinItems(program, target);
519
+ const maxItems = getMaxItems(program, target);
520
+ if (minItems !== undefined && source.values.length < minItems) {
521
+ return [
522
+ Related.false,
523
+ [
524
+ createUnassignableDiagnostic(source, target, diagnosticTarget, `Source has ${source.values.length} element(s) but target requires ${minItems}.`),
525
+ ],
526
+ ];
527
+ }
528
+ if (maxItems !== undefined && source.values.length > maxItems) {
529
+ return [
530
+ Related.false,
531
+ [
532
+ createUnassignableDiagnostic(source, target, diagnosticTarget, `Source has ${source.values.length} element(s) but target only allows ${maxItems}.`),
533
+ ],
534
+ ];
535
+ }
536
+ for (const item of source.values) {
537
+ const [related, diagnostics] = isTypeAssignableToInternal(item, target.indexer.value, diagnosticTarget, relationCache);
538
+ if (!related) {
539
+ return [Related.false, diagnostics];
540
+ }
541
+ }
542
+ return [Related.true, []];
543
+ }
544
+ function isTupleAssignableToTuple(source, target, diagnosticTarget, relationCache) {
545
+ if (source.values.length !== target.values.length) {
546
+ return [
547
+ Related.false,
548
+ [
549
+ createUnassignableDiagnostic(source, target, diagnosticTarget, `Source has ${source.values.length} element(s) but target requires ${target.values.length}.`),
550
+ ],
551
+ ];
552
+ }
553
+ for (const [index, sourceItem] of source.values.entries()) {
554
+ const targetItem = target.values[index];
555
+ const [related, diagnostics] = isTypeAssignableToInternal(sourceItem, targetItem, diagnosticTarget, relationCache);
556
+ if (!related) {
557
+ return [Related.false, diagnostics];
558
+ }
559
+ }
560
+ return [Related.true, []];
561
+ }
562
+ function isAssignableToUnion(source, target, diagnosticTarget, relationCache) {
563
+ if (source.kind === "UnionVariant" && source.union === target) {
564
+ return [Related.true, []];
565
+ }
566
+ for (const option of target.variants.values()) {
567
+ const [related] = isTypeAssignableToInternal(source, option.type, diagnosticTarget, relationCache);
568
+ if (related) {
569
+ return [Related.true, []];
570
+ }
571
+ }
572
+ return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
573
+ }
574
+ function isAssignableToEnum(source, target, diagnosticTarget) {
575
+ switch (source.kind) {
576
+ case "Enum":
577
+ if (source === target) {
578
+ return [Related.true, []];
579
+ }
580
+ else {
581
+ return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
582
+ }
583
+ case "EnumMember":
584
+ if (source.enum === target) {
585
+ return [Related.true, []];
586
+ }
587
+ else {
588
+ return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
589
+ }
590
+ default:
591
+ return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
592
+ }
593
+ }
594
+ function isTypeSpecNamespace(namespace) {
595
+ return (namespace.name === "TypeSpec" &&
596
+ (namespace.namespace === checker.getGlobalNamespaceType() ||
597
+ namespace.namespace?.projectionBase === checker.getGlobalNamespaceType()));
598
+ }
599
+ }
600
+ function wrapUnassignableErrors(source, target, errors) {
601
+ const error = createUnassignableDiagnostic(source, target, source);
602
+ error.children = errors;
603
+ return [error];
604
+ }
605
+ function wrapUnassignablePropertyErrors(source, errors) {
606
+ const error = createTypeRelationError({
607
+ code: "property-unassignable",
608
+ diagnosticTarget: source,
609
+ format: {
610
+ propName: source.name,
611
+ },
612
+ skipIfFirst: true,
613
+ });
614
+ error.children = errors;
615
+ return [error];
616
+ }
617
+ function createTypeRelationError({ code, format, details, diagnosticTarget, skipIfFirst, }) {
618
+ const diag = createDiagnostic({
619
+ code: code,
620
+ format: format,
621
+ target: NoTarget,
622
+ });
623
+ return {
624
+ code: code,
625
+ message: details ? `${diag.message}\n ${details}` : diag.message,
626
+ target: diagnosticTarget,
627
+ skipIfFirst,
628
+ children: [],
629
+ };
630
+ }
631
+ function createUnassignableDiagnostic(source, target, diagnosticTarget, details) {
632
+ return createTypeRelationError({
633
+ code: "unassignable",
634
+ format: {
635
+ sourceType: getEntityName(source),
636
+ targetType: getEntityName(target),
637
+ },
638
+ diagnosticTarget,
639
+ details,
640
+ });
641
+ }
642
+ // #endregion
643
+ //# sourceMappingURL=type-relation-checker.js.map