kopscript 0.7.2 → 0.9.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.
- package/LLM.md +17 -8
- package/README.md +29 -3
- package/dist/checker.js +89 -89
- package/dist/cli.js +12 -2
- package/dist/codegen.js +162 -17
- package/dist/diagnostics.js +5 -5
- package/dist/lexer.js +4 -4
- package/dist/modules.js +21 -14
- package/dist/parser.js +22 -22
- package/dist/sourcemap.js +113 -0
- package/dist/template_compiler.js +1 -1
- package/dist/template_lexer.js +2 -2
- package/dist/template_parser.js +12 -12
- package/package.json +1 -1
package/dist/checker.js
CHANGED
|
@@ -221,7 +221,7 @@ export class Checker {
|
|
|
221
221
|
this.externValues.set(decl.name, T.STRING);
|
|
222
222
|
const resolvedPath = resolve(dirname(this.currentFilePath), decl.path);
|
|
223
223
|
if (!existsSync(resolvedPath)) {
|
|
224
|
-
this.diagnostics.error(`Cannot find file '${decl.path}' referenced by 'raw string ${decl.name}' (looked for '${resolvedPath}')`, decl.line, decl.col);
|
|
224
|
+
this.diagnostics.error("KS4001", `Cannot find file '${decl.path}' referenced by 'raw string ${decl.name}' (looked for '${resolvedPath}')`, decl.line, decl.col);
|
|
225
225
|
return;
|
|
226
226
|
}
|
|
227
227
|
this.rawContents.set(decl.name, readFileSync(resolvedPath, "utf-8"));
|
|
@@ -272,10 +272,10 @@ export class Checker {
|
|
|
272
272
|
const seen = new Map();
|
|
273
273
|
const declare = (name, line, col) => {
|
|
274
274
|
if (this.importedNames.has(name)) {
|
|
275
|
-
this.diagnostics.error(`Declaration '${name}' conflicts with a name brought in by 'using'`, line, col);
|
|
275
|
+
this.diagnostics.error("KS4002", `Declaration '${name}' conflicts with a name brought in by 'using'`, line, col);
|
|
276
276
|
}
|
|
277
277
|
else if (seen.has(name)) {
|
|
278
|
-
this.diagnostics.error(`Duplicate top-level declaration '${name}'`, line, col);
|
|
278
|
+
this.diagnostics.error("KS4003", `Duplicate top-level declaration '${name}'`, line, col);
|
|
279
279
|
}
|
|
280
280
|
seen.set(name, true);
|
|
281
281
|
};
|
|
@@ -305,11 +305,11 @@ export class Checker {
|
|
|
305
305
|
return;
|
|
306
306
|
const info = this.classes.get(decl.name);
|
|
307
307
|
if (info.superclass && !this.isExportedName(info.superclass)) {
|
|
308
|
-
this.diagnostics.error(`Exported class '${decl.name}' has a base class '${info.superclass}' that isn't exported`, decl.line, decl.col);
|
|
308
|
+
this.diagnostics.error("KS4004", `Exported class '${decl.name}' has a base class '${info.superclass}' that isn't exported`, decl.line, decl.col);
|
|
309
309
|
}
|
|
310
310
|
for (const ifaceName of info.interfaces) {
|
|
311
311
|
if (!this.isExportedName(ifaceName)) {
|
|
312
|
-
this.diagnostics.error(`Exported class '${decl.name}' implements interface '${ifaceName}', which isn't exported`, decl.line, decl.col);
|
|
312
|
+
this.diagnostics.error("KS4005", `Exported class '${decl.name}' implements interface '${ifaceName}', which isn't exported`, decl.line, decl.col);
|
|
313
313
|
}
|
|
314
314
|
}
|
|
315
315
|
}
|
|
@@ -321,7 +321,7 @@ export class Checker {
|
|
|
321
321
|
return;
|
|
322
322
|
for (const baseName of info.bases) {
|
|
323
323
|
if (!this.isExportedName(baseName)) {
|
|
324
|
-
this.diagnostics.error(`Exported interface '${decl.name}' extends interface '${baseName}', which isn't exported`, decl.line, decl.col);
|
|
324
|
+
this.diagnostics.error("KS4006", `Exported interface '${decl.name}' extends interface '${baseName}', which isn't exported`, decl.line, decl.col);
|
|
325
325
|
}
|
|
326
326
|
}
|
|
327
327
|
}
|
|
@@ -342,10 +342,10 @@ export class Checker {
|
|
|
342
342
|
if (!resolved) {
|
|
343
343
|
const name = node.kind === "NamedType" ? node.name : "[]";
|
|
344
344
|
if (name !== "[]" && this.genericTypeParams.has(name) && !node.typeArgs) {
|
|
345
|
-
this.diagnostics.error(`Generic type '${name}' requires a type argument (e.g. '${name}<T>')`, line, col);
|
|
345
|
+
this.diagnostics.error("KS4007", `Generic type '${name}' requires a type argument (e.g. '${name}<T>')`, line, col);
|
|
346
346
|
}
|
|
347
347
|
else {
|
|
348
|
-
this.diagnostics.error(`Unknown type '${name}'`, line, col);
|
|
348
|
+
this.diagnostics.error("KS4008", `Unknown type '${name}'`, line, col);
|
|
349
349
|
}
|
|
350
350
|
return T.UNKNOWN;
|
|
351
351
|
}
|
|
@@ -380,10 +380,10 @@ export class Checker {
|
|
|
380
380
|
case "interface": {
|
|
381
381
|
const isGeneric = this.genericTypeParams.has(type.name);
|
|
382
382
|
if (isGeneric && !type.typeArg) {
|
|
383
|
-
this.diagnostics.error(`Generic type '${type.name}' requires a type argument (e.g. '${type.name}<T>')`, line, col);
|
|
383
|
+
this.diagnostics.error("KS4009", `Generic type '${type.name}' requires a type argument (e.g. '${type.name}<T>')`, line, col);
|
|
384
384
|
}
|
|
385
385
|
else if (!isGeneric && type.typeArg) {
|
|
386
|
-
this.diagnostics.error(`Type '${type.name}' is not generic — it doesn't take a type argument`, line, col);
|
|
386
|
+
this.diagnostics.error("KS4010", `Type '${type.name}' is not generic — it doesn't take a type argument`, line, col);
|
|
387
387
|
}
|
|
388
388
|
if (type.typeArg)
|
|
389
389
|
this.validateGenericArity(type.typeArg, line, col);
|
|
@@ -450,7 +450,7 @@ export class Checker {
|
|
|
450
450
|
const members = new Map();
|
|
451
451
|
decl.members.forEach((name, index) => {
|
|
452
452
|
if (members.has(name)) {
|
|
453
|
-
this.diagnostics.error(`Duplicate enum member '${name}'`, decl.line, decl.col);
|
|
453
|
+
this.diagnostics.error("KS4011", `Duplicate enum member '${name}'`, decl.line, decl.col);
|
|
454
454
|
return;
|
|
455
455
|
}
|
|
456
456
|
members.set(name, index);
|
|
@@ -468,14 +468,14 @@ export class Checker {
|
|
|
468
468
|
const bases = [];
|
|
469
469
|
for (const baseName of decl.baseList) {
|
|
470
470
|
if (this.namedTypes.get(baseName) !== "interface") {
|
|
471
|
-
this.diagnostics.error(`Interface '${decl.name}' can only extend other interfaces (unknown interface '${baseName}')`, decl.line, decl.col);
|
|
471
|
+
this.diagnostics.error("KS4012", `Interface '${decl.name}' can only extend other interfaces (unknown interface '${baseName}')`, decl.line, decl.col);
|
|
472
472
|
continue;
|
|
473
473
|
}
|
|
474
474
|
// v1 generics can't appear in a base list at all — only as a
|
|
475
475
|
// standalone type (field/param/return/local, `new Box<T>()`). A
|
|
476
476
|
// class/interface always implements/extends a *bare* name.
|
|
477
477
|
if (this.genericTypeParams.has(baseName)) {
|
|
478
|
-
this.diagnostics.error(`Interface '${decl.name}' cannot extend generic interface '${baseName}' — not supported in v1`, decl.line, decl.col);
|
|
478
|
+
this.diagnostics.error("KS4013", `Interface '${decl.name}' cannot extend generic interface '${baseName}' — not supported in v1`, decl.line, decl.col);
|
|
479
479
|
continue;
|
|
480
480
|
}
|
|
481
481
|
bases.push(baseName);
|
|
@@ -492,7 +492,7 @@ export class Checker {
|
|
|
492
492
|
while (stack.length > 0) {
|
|
493
493
|
const current = stack.pop();
|
|
494
494
|
if (current === decl.name) {
|
|
495
|
-
this.diagnostics.error(`Circular interface inheritance detected involving '${decl.name}'`, decl.line, decl.col);
|
|
495
|
+
this.diagnostics.error("KS4014", `Circular interface inheritance detected involving '${decl.name}'`, decl.line, decl.col);
|
|
496
496
|
return;
|
|
497
497
|
}
|
|
498
498
|
if (seen.has(current))
|
|
@@ -563,13 +563,13 @@ export class Checker {
|
|
|
563
563
|
// standalone type (field/param/return/local, `new Box<T>()`). A
|
|
564
564
|
// class always extends/implements a *bare* name.
|
|
565
565
|
if (this.genericTypeParams.has(baseName)) {
|
|
566
|
-
this.diagnostics.error(`Class '${decl.name}' cannot extend/implement generic type '${baseName}' — not supported in v1`, decl.line, decl.col);
|
|
566
|
+
this.diagnostics.error("KS4015", `Class '${decl.name}' cannot extend/implement generic type '${baseName}' — not supported in v1`, decl.line, decl.col);
|
|
567
567
|
continue;
|
|
568
568
|
}
|
|
569
569
|
const kind = this.namedTypes.get(baseName);
|
|
570
570
|
if (kind === "class") {
|
|
571
571
|
if (superclass !== null) {
|
|
572
|
-
this.diagnostics.error(`Class '${decl.name}' cannot extend multiple classes ('${superclass}' and '${baseName}')`, decl.line, decl.col);
|
|
572
|
+
this.diagnostics.error("KS4016", `Class '${decl.name}' cannot extend multiple classes ('${superclass}' and '${baseName}')`, decl.line, decl.col);
|
|
573
573
|
}
|
|
574
574
|
else {
|
|
575
575
|
superclass = baseName;
|
|
@@ -579,7 +579,7 @@ export class Checker {
|
|
|
579
579
|
interfaces.push(baseName);
|
|
580
580
|
}
|
|
581
581
|
else {
|
|
582
|
-
this.diagnostics.error(`Unknown base class or interface '${baseName}'`, decl.line, decl.col);
|
|
582
|
+
this.diagnostics.error("KS4017", `Unknown base class or interface '${baseName}'`, decl.line, decl.col);
|
|
583
583
|
}
|
|
584
584
|
}
|
|
585
585
|
this.classes.set(decl.name, {
|
|
@@ -602,7 +602,7 @@ export class Checker {
|
|
|
602
602
|
let current = info.superclass;
|
|
603
603
|
while (current) {
|
|
604
604
|
if (seen.has(current)) {
|
|
605
|
-
this.diagnostics.error(`Circular inheritance detected involving '${decl.name}'`, decl.line, decl.col);
|
|
605
|
+
this.diagnostics.error("KS4018", `Circular inheritance detected involving '${decl.name}'`, decl.line, decl.col);
|
|
606
606
|
return;
|
|
607
607
|
}
|
|
608
608
|
seen.add(current);
|
|
@@ -617,11 +617,11 @@ export class Checker {
|
|
|
617
617
|
return;
|
|
618
618
|
if (info.superclass) {
|
|
619
619
|
if (!decl.constructor.baseArgs) {
|
|
620
|
-
this.diagnostics.error(`Class '${decl.name}' extends '${info.superclass}' and must call the base constructor via ': base(...)'`, decl.constructor.line, decl.constructor.col);
|
|
620
|
+
this.diagnostics.error("KS4019", `Class '${decl.name}' extends '${info.superclass}' and must call the base constructor via ': base(...)'`, decl.constructor.line, decl.constructor.col);
|
|
621
621
|
}
|
|
622
622
|
}
|
|
623
623
|
else if (decl.constructor.baseArgs) {
|
|
624
|
-
this.diagnostics.error(`Class '${decl.name}' has no base class; ': base(...)' is not valid here`, decl.constructor.line, decl.constructor.col);
|
|
624
|
+
this.diagnostics.error("KS4020", `Class '${decl.name}' has no base class; ': base(...)' is not valid here`, decl.constructor.line, decl.constructor.col);
|
|
625
625
|
}
|
|
626
626
|
}
|
|
627
627
|
// Walks up the inheritance chain to find the nearest ancestor that already
|
|
@@ -644,28 +644,28 @@ export class Checker {
|
|
|
644
644
|
continue; // static methods don't participate in virtual dispatch
|
|
645
645
|
const methodInfo = classInfo.methods.get(method.name);
|
|
646
646
|
if (methodInfo.isVirtual && methodInfo.isOverride) {
|
|
647
|
-
this.diagnostics.error(`Method '${method.name}' cannot be both 'virtual' and 'override'`, method.line, method.col);
|
|
647
|
+
this.diagnostics.error("KS4021", `Method '${method.name}' cannot be both 'virtual' and 'override'`, method.line, method.col);
|
|
648
648
|
}
|
|
649
649
|
const ancestor = this.findNearestMethodInChain(decl.name, method.name);
|
|
650
650
|
if (ancestor) {
|
|
651
651
|
const overridable = ancestor.info.isVirtual || ancestor.info.isOverride;
|
|
652
652
|
if (!methodInfo.isOverride) {
|
|
653
|
-
this.diagnostics.error(`Method '${method.name}' hides inherited member '${ancestor.owner}.${method.name}'; add 'override' (and mark the base member 'virtual')`, method.line, method.col);
|
|
653
|
+
this.diagnostics.error("KS4022", `Method '${method.name}' hides inherited member '${ancestor.owner}.${method.name}'; add 'override' (and mark the base member 'virtual')`, method.line, method.col);
|
|
654
654
|
}
|
|
655
655
|
else if (!overridable) {
|
|
656
|
-
this.diagnostics.error(`Cannot override non-virtual method '${ancestor.owner}.${method.name}'; mark it 'virtual' in '${ancestor.owner}'`, method.line, method.col);
|
|
656
|
+
this.diagnostics.error("KS4023", `Cannot override non-virtual method '${ancestor.owner}.${method.name}'; mark it 'virtual' in '${ancestor.owner}'`, method.line, method.col);
|
|
657
657
|
}
|
|
658
658
|
else {
|
|
659
659
|
const paramsMatch = methodInfo.params.length === ancestor.info.params.length &&
|
|
660
660
|
methodInfo.params.every((p, i) => T.typesEqual(p, ancestor.info.params[i]));
|
|
661
661
|
const returnMatches = T.typesEqual(methodInfo.returnType, ancestor.info.returnType);
|
|
662
662
|
if (!paramsMatch || !returnMatches) {
|
|
663
|
-
this.diagnostics.error(`Method '${method.name}' does not match the signature of overridden method '${ancestor.owner}.${method.name}'`, method.line, method.col);
|
|
663
|
+
this.diagnostics.error("KS4024", `Method '${method.name}' does not match the signature of overridden method '${ancestor.owner}.${method.name}'`, method.line, method.col);
|
|
664
664
|
}
|
|
665
665
|
}
|
|
666
666
|
}
|
|
667
667
|
else if (methodInfo.isOverride) {
|
|
668
|
-
this.diagnostics.error(`Method '${method.name}' marked 'override' but no matching method was found in a base class`, method.line, method.col);
|
|
668
|
+
this.diagnostics.error("KS4025", `Method '${method.name}' marked 'override' but no matching method was found in a base class`, method.line, method.col);
|
|
669
669
|
}
|
|
670
670
|
}
|
|
671
671
|
}
|
|
@@ -677,12 +677,12 @@ export class Checker {
|
|
|
677
677
|
for (const sig of this.collectInterfaceMethods(ifaceName)) {
|
|
678
678
|
const found = this.lookupMethod(decl.name, sig.name);
|
|
679
679
|
if (!found) {
|
|
680
|
-
this.diagnostics.error(`Class '${decl.name}' does not implement method '${sig.name}' required by interface '${ifaceName}'`, decl.line, decl.col);
|
|
680
|
+
this.diagnostics.error("KS4026", `Class '${decl.name}' does not implement method '${sig.name}' required by interface '${ifaceName}'`, decl.line, decl.col);
|
|
681
681
|
continue;
|
|
682
682
|
}
|
|
683
683
|
const paramsMatch = found.info.params.length === sig.params.length && found.info.params.every((p, i) => T.typesEqual(p, sig.params[i]));
|
|
684
684
|
if (!paramsMatch || !T.typesEqual(found.info.returnType, sig.returnType)) {
|
|
685
|
-
this.diagnostics.error(`Class '${decl.name}' member '${sig.name}' does not match the signature required by interface '${ifaceName}'`, decl.line, decl.col);
|
|
685
|
+
this.diagnostics.error("KS4027", `Class '${decl.name}' member '${sig.name}' does not match the signature required by interface '${ifaceName}'`, decl.line, decl.col);
|
|
686
686
|
}
|
|
687
687
|
}
|
|
688
688
|
}
|
|
@@ -772,13 +772,13 @@ export class Checker {
|
|
|
772
772
|
return;
|
|
773
773
|
if (visibility === "private") {
|
|
774
774
|
if (ctx.currentClass?.name !== owner) {
|
|
775
|
-
this.diagnostics.error(`'${memberName}' is private and not accessible outside class '${owner}'`, line, col);
|
|
775
|
+
this.diagnostics.error("KS4028", `'${memberName}' is private and not accessible outside class '${owner}'`, line, col);
|
|
776
776
|
}
|
|
777
777
|
return;
|
|
778
778
|
}
|
|
779
779
|
// protected
|
|
780
780
|
if (!ctx.currentClass || !(ctx.currentClass.name === owner || this.isSubclass(ctx.currentClass.name, owner))) {
|
|
781
|
-
this.diagnostics.error(`'${memberName}' is protected and only accessible within '${owner}' or its subclasses`, line, col);
|
|
781
|
+
this.diagnostics.error("KS4029", `'${memberName}' is protected and only accessible within '${owner}' or its subclasses`, line, col);
|
|
782
782
|
}
|
|
783
783
|
}
|
|
784
784
|
// Assignability: is a value of type `from` usable where type `to` is expected?
|
|
@@ -840,13 +840,13 @@ export class Checker {
|
|
|
840
840
|
resolveBodyReturnType(declaredReturnType, isAsync, line, col) {
|
|
841
841
|
if (isAsync) {
|
|
842
842
|
if (declaredReturnType.kind !== "task") {
|
|
843
|
-
this.diagnostics.error(`'async' functions/methods must return 'task' or 'task<T>', got '${T.typeToString(declaredReturnType)}'`, line, col);
|
|
843
|
+
this.diagnostics.error("KS4030", `'async' functions/methods must return 'task' or 'task<T>', got '${T.typeToString(declaredReturnType)}'`, line, col);
|
|
844
844
|
return T.UNKNOWN;
|
|
845
845
|
}
|
|
846
846
|
return declaredReturnType.resultType;
|
|
847
847
|
}
|
|
848
848
|
if (declaredReturnType.kind === "task") {
|
|
849
|
-
this.diagnostics.error(`A function/method returning 'task'/'task<T>' must be marked 'async'`, line, col);
|
|
849
|
+
this.diagnostics.error("KS4031", `A function/method returning 'task'/'task<T>' must be marked 'async'`, line, col);
|
|
850
850
|
}
|
|
851
851
|
return declaredReturnType;
|
|
852
852
|
}
|
|
@@ -886,13 +886,13 @@ export class Checker {
|
|
|
886
886
|
const baseCtorParams = this.lookupCtorParams(info.superclass);
|
|
887
887
|
const baseCtx = { returnType: T.VOID, currentClass: info, inConstructor: false, loopDepth: 0, isAsync: false };
|
|
888
888
|
if (decl.constructor.baseArgs.length !== baseCtorParams.length) {
|
|
889
|
-
this.diagnostics.error(`Expected ${baseCtorParams.length} base constructor argument(s), got ${decl.constructor.baseArgs.length}`, decl.constructor.line, decl.constructor.col);
|
|
889
|
+
this.diagnostics.error("KS4032", `Expected ${baseCtorParams.length} base constructor argument(s), got ${decl.constructor.baseArgs.length}`, decl.constructor.line, decl.constructor.col);
|
|
890
890
|
}
|
|
891
891
|
decl.constructor.baseArgs.forEach((arg, i) => {
|
|
892
892
|
const expected = baseCtorParams[i] ?? T.UNKNOWN;
|
|
893
893
|
const argType = this.checkExpressionExpecting(arg, expected, paramScope, baseCtx);
|
|
894
894
|
if (!this.isAssignableType(argType, expected)) {
|
|
895
|
-
this.diagnostics.error(`Base constructor argument ${i + 1} has type '${T.typeToString(argType)}', expected '${T.typeToString(expected)}'`, arg.line, arg.col);
|
|
895
|
+
this.diagnostics.error("KS4033", `Base constructor argument ${i + 1} has type '${T.typeToString(argType)}', expected '${T.typeToString(expected)}'`, arg.line, arg.col);
|
|
896
896
|
}
|
|
897
897
|
});
|
|
898
898
|
}
|
|
@@ -922,7 +922,7 @@ export class Checker {
|
|
|
922
922
|
isAsync: false,
|
|
923
923
|
});
|
|
924
924
|
if (!this.isAssignableType(initType, declaredType)) {
|
|
925
|
-
this.diagnostics.error(`Cannot assign value of type '${T.typeToString(initType)}' to static field of type '${T.typeToString(declaredType)}'`, field.line, field.col);
|
|
925
|
+
this.diagnostics.error("KS4034", `Cannot assign value of type '${T.typeToString(initType)}' to static field of type '${T.typeToString(declaredType)}'`, field.line, field.col);
|
|
926
926
|
}
|
|
927
927
|
}
|
|
928
928
|
}
|
|
@@ -939,7 +939,7 @@ export class Checker {
|
|
|
939
939
|
this.recordHover(stmt.nameLine, stmt.nameCol, `${stmt.isConst ? "const" : "let"} ${stmt.name}: ${T.typeToString(declaredType)}`);
|
|
940
940
|
const initType = this.checkExpressionExpecting(stmt.init, declaredType, scope, ctx);
|
|
941
941
|
if (!this.isAssignableType(initType, declaredType)) {
|
|
942
|
-
this.diagnostics.error(`Cannot assign value of type '${T.typeToString(initType)}' to variable of type '${T.typeToString(declaredType)}'`, stmt.line, stmt.col);
|
|
942
|
+
this.diagnostics.error("KS4035", `Cannot assign value of type '${T.typeToString(initType)}' to variable of type '${T.typeToString(declaredType)}'`, stmt.line, stmt.col);
|
|
943
943
|
}
|
|
944
944
|
scope.declare(stmt.name, declaredType, stmt.isConst);
|
|
945
945
|
return;
|
|
@@ -994,11 +994,11 @@ export class Checker {
|
|
|
994
994
|
const declaredVarType = this.resolveType(stmt.varType, stmt.line, stmt.col);
|
|
995
995
|
if (iterableType.kind === "array") {
|
|
996
996
|
if (!this.isAssignableType(iterableType.element, declaredVarType)) {
|
|
997
|
-
this.diagnostics.error(`Cannot use loop variable of type '${T.typeToString(declaredVarType)}' for array of '${T.typeToString(iterableType.element)}'`, stmt.line, stmt.col);
|
|
997
|
+
this.diagnostics.error("KS4036", `Cannot use loop variable of type '${T.typeToString(declaredVarType)}' for array of '${T.typeToString(iterableType.element)}'`, stmt.line, stmt.col);
|
|
998
998
|
}
|
|
999
999
|
}
|
|
1000
1000
|
else if (iterableType.kind !== "unknown") {
|
|
1001
|
-
this.diagnostics.error(`Cannot iterate over non-array type '${T.typeToString(iterableType)}'`, stmt.line, stmt.col);
|
|
1001
|
+
this.diagnostics.error("KS4037", `Cannot iterate over non-array type '${T.typeToString(iterableType)}'`, stmt.line, stmt.col);
|
|
1002
1002
|
}
|
|
1003
1003
|
const forScope = scope.child();
|
|
1004
1004
|
forScope.declare(stmt.varName, declaredVarType, false);
|
|
@@ -1008,14 +1008,14 @@ export class Checker {
|
|
|
1008
1008
|
case "ReturnStatement": {
|
|
1009
1009
|
const actual = stmt.value ? this.checkExpressionExpecting(stmt.value, ctx.returnType, scope, ctx) : T.VOID;
|
|
1010
1010
|
if (!this.isAssignableType(actual, ctx.returnType)) {
|
|
1011
|
-
this.diagnostics.error(`Return type '${T.typeToString(actual)}' does not match declared return type '${T.typeToString(ctx.returnType)}'`, stmt.line, stmt.col);
|
|
1011
|
+
this.diagnostics.error("KS4038", `Return type '${T.typeToString(actual)}' does not match declared return type '${T.typeToString(ctx.returnType)}'`, stmt.line, stmt.col);
|
|
1012
1012
|
}
|
|
1013
1013
|
return;
|
|
1014
1014
|
}
|
|
1015
1015
|
case "BreakStatement":
|
|
1016
1016
|
case "ContinueStatement":
|
|
1017
1017
|
if (ctx.loopDepth === 0) {
|
|
1018
|
-
this.diagnostics.error(`'${stmt.kind === "BreakStatement" ? "break" : "continue"}' used outside of a loop`, stmt.line, stmt.col);
|
|
1018
|
+
this.diagnostics.error("KS4039", `'${stmt.kind === "BreakStatement" ? "break" : "continue"}' used outside of a loop`, stmt.line, stmt.col);
|
|
1019
1019
|
}
|
|
1020
1020
|
return;
|
|
1021
1021
|
case "ExpressionStatement":
|
|
@@ -1039,30 +1039,30 @@ export class Checker {
|
|
|
1039
1039
|
this.checkExpression(stmt.expression, scope, ctx);
|
|
1040
1040
|
return;
|
|
1041
1041
|
case "FunctionDecl":
|
|
1042
|
-
this.diagnostics.error(`Nested function declarations are not supported`, stmt.line, stmt.col);
|
|
1042
|
+
this.diagnostics.error("KS4040", `Nested function declarations are not supported`, stmt.line, stmt.col);
|
|
1043
1043
|
return;
|
|
1044
1044
|
case "ClassDecl":
|
|
1045
|
-
this.diagnostics.error(`Nested class declarations are not supported`, stmt.line, stmt.col);
|
|
1045
|
+
this.diagnostics.error("KS4041", `Nested class declarations are not supported`, stmt.line, stmt.col);
|
|
1046
1046
|
return;
|
|
1047
1047
|
case "InterfaceDecl":
|
|
1048
|
-
this.diagnostics.error(`Nested interface declarations are not supported`, stmt.line, stmt.col);
|
|
1048
|
+
this.diagnostics.error("KS4042", `Nested interface declarations are not supported`, stmt.line, stmt.col);
|
|
1049
1049
|
return;
|
|
1050
1050
|
case "EnumDecl":
|
|
1051
|
-
this.diagnostics.error(`Nested enum declarations are not supported`, stmt.line, stmt.col);
|
|
1051
|
+
this.diagnostics.error("KS4043", `Nested enum declarations are not supported`, stmt.line, stmt.col);
|
|
1052
1052
|
return;
|
|
1053
1053
|
case "ExternFunctionDecl":
|
|
1054
1054
|
case "ExternClassDecl":
|
|
1055
1055
|
case "ExternValueDecl":
|
|
1056
|
-
this.diagnostics.error(`'extern' declarations are only allowed at the top level of a file`, stmt.line, stmt.col);
|
|
1056
|
+
this.diagnostics.error("KS4044", `'extern' declarations are only allowed at the top level of a file`, stmt.line, stmt.col);
|
|
1057
1057
|
return;
|
|
1058
1058
|
case "RawStringDecl":
|
|
1059
|
-
this.diagnostics.error(`'raw' declarations are only allowed at the top level of a file`, stmt.line, stmt.col);
|
|
1059
|
+
this.diagnostics.error("KS4045", `'raw' declarations are only allowed at the top level of a file`, stmt.line, stmt.col);
|
|
1060
1060
|
return;
|
|
1061
1061
|
}
|
|
1062
1062
|
}
|
|
1063
1063
|
expectType(actual, expected, line, col, context) {
|
|
1064
1064
|
if (!T.typesEqual(actual, expected)) {
|
|
1065
|
-
this.diagnostics.error(`Expected type '${T.typeToString(expected)}' for ${context}, got '${T.typeToString(actual)}'`, line, col);
|
|
1065
|
+
this.diagnostics.error("KS4046", `Expected type '${T.typeToString(expected)}' for ${context}, got '${T.typeToString(actual)}'`, line, col);
|
|
1066
1066
|
}
|
|
1067
1067
|
}
|
|
1068
1068
|
// Recognizes `name != null` / `name == null` (either operand order) as a
|
|
@@ -1129,7 +1129,7 @@ export class Checker {
|
|
|
1129
1129
|
const first = elementTypes[0];
|
|
1130
1130
|
for (let i = 1; i < elementTypes.length; i++) {
|
|
1131
1131
|
if (!T.typesEqual(elementTypes[i], first)) {
|
|
1132
|
-
this.diagnostics.error(`Array elements must all have the same type`, expr.line, expr.col);
|
|
1132
|
+
this.diagnostics.error("KS4047", `Array elements must all have the same type`, expr.line, expr.col);
|
|
1133
1133
|
break;
|
|
1134
1134
|
}
|
|
1135
1135
|
}
|
|
@@ -1156,14 +1156,14 @@ export class Checker {
|
|
|
1156
1156
|
const fnInfo = this.functions.get(expr.name);
|
|
1157
1157
|
if (fnInfo)
|
|
1158
1158
|
return T.functionType(fnInfo.params, fnInfo.returnType);
|
|
1159
|
-
this.diagnostics.error(`Undefined identifier '${expr.name}'`, expr.line, expr.col);
|
|
1159
|
+
this.diagnostics.error("KS4048", `Undefined identifier '${expr.name}'`, expr.line, expr.col);
|
|
1160
1160
|
return T.UNKNOWN;
|
|
1161
1161
|
}
|
|
1162
1162
|
case "ThisExpr": {
|
|
1163
1163
|
const found = scope.resolve("this");
|
|
1164
1164
|
if (found)
|
|
1165
1165
|
return found.type;
|
|
1166
|
-
this.diagnostics.error(`'this' used outside of a class method`, expr.line, expr.col);
|
|
1166
|
+
this.diagnostics.error("KS4049", `'this' used outside of a class method`, expr.line, expr.col);
|
|
1167
1167
|
return T.UNKNOWN;
|
|
1168
1168
|
}
|
|
1169
1169
|
case "UnaryExpr": {
|
|
@@ -1203,13 +1203,13 @@ export class Checker {
|
|
|
1203
1203
|
if (expr.target.kind === "Identifier") {
|
|
1204
1204
|
const found = scope.resolve(expr.target.name);
|
|
1205
1205
|
if (found?.isConst) {
|
|
1206
|
-
this.diagnostics.error(`Cannot assign to const variable '${expr.target.name}'`, expr.line, expr.col);
|
|
1206
|
+
this.diagnostics.error("KS4050", `Cannot assign to const variable '${expr.target.name}'`, expr.line, expr.col);
|
|
1207
1207
|
}
|
|
1208
1208
|
}
|
|
1209
1209
|
}
|
|
1210
1210
|
const valueType = this.checkExpressionExpecting(expr.value, targetType, scope, ctx);
|
|
1211
1211
|
if (!this.isAssignableType(valueType, targetType)) {
|
|
1212
|
-
this.diagnostics.error(`Cannot assign value of type '${T.typeToString(valueType)}' to target of type '${T.typeToString(targetType)}'`, expr.line, expr.col);
|
|
1212
|
+
this.diagnostics.error("KS4051", `Cannot assign value of type '${T.typeToString(valueType)}' to target of type '${T.typeToString(targetType)}'`, expr.line, expr.col);
|
|
1213
1213
|
}
|
|
1214
1214
|
return targetType;
|
|
1215
1215
|
}
|
|
@@ -1226,7 +1226,7 @@ export class Checker {
|
|
|
1226
1226
|
if (objType.kind === "array")
|
|
1227
1227
|
return objType.element;
|
|
1228
1228
|
if (objType.kind !== "unknown") {
|
|
1229
|
-
this.diagnostics.error(`Cannot index into non-array type '${T.typeToString(objType)}'`, expr.line, expr.col);
|
|
1229
|
+
this.diagnostics.error("KS4052", `Cannot index into non-array type '${T.typeToString(objType)}'`, expr.line, expr.col);
|
|
1230
1230
|
}
|
|
1231
1231
|
return T.UNKNOWN;
|
|
1232
1232
|
}
|
|
@@ -1240,13 +1240,13 @@ export class Checker {
|
|
|
1240
1240
|
return this.checkLambda(expr, T.UNKNOWN, scope, ctx);
|
|
1241
1241
|
case "AwaitExpr": {
|
|
1242
1242
|
if (!ctx.isAsync) {
|
|
1243
|
-
this.diagnostics.error(`'await' can only be used inside an 'async' function or method`, expr.line, expr.col);
|
|
1243
|
+
this.diagnostics.error("KS4053", `'await' can only be used inside an 'async' function or method`, expr.line, expr.col);
|
|
1244
1244
|
}
|
|
1245
1245
|
const operandType = this.checkExpression(expr.operand, scope, ctx);
|
|
1246
1246
|
if (operandType.kind === "task")
|
|
1247
1247
|
return operandType.resultType;
|
|
1248
1248
|
if (operandType.kind !== "unknown") {
|
|
1249
|
-
this.diagnostics.error(`Cannot 'await' a value of type '${T.typeToString(operandType)}'`, expr.line, expr.col);
|
|
1249
|
+
this.diagnostics.error("KS4054", `Cannot 'await' a value of type '${T.typeToString(operandType)}'`, expr.line, expr.col);
|
|
1250
1250
|
}
|
|
1251
1251
|
return T.UNKNOWN;
|
|
1252
1252
|
}
|
|
@@ -1268,7 +1268,7 @@ export class Checker {
|
|
|
1268
1268
|
return this.checkLambda(expr, expected, scope, ctx);
|
|
1269
1269
|
if (expr.kind === "NullLiteral") {
|
|
1270
1270
|
if (expected.kind !== "nullable" && expected.kind !== "unknown") {
|
|
1271
|
-
this.diagnostics.error(`Cannot assign 'null' to non-nullable type '${T.typeToString(expected)}'`, expr.line, expr.col);
|
|
1271
|
+
this.diagnostics.error("KS4055", `Cannot assign 'null' to non-nullable type '${T.typeToString(expected)}'`, expr.line, expr.col);
|
|
1272
1272
|
return T.UNKNOWN;
|
|
1273
1273
|
}
|
|
1274
1274
|
return expected;
|
|
@@ -1278,7 +1278,7 @@ export class Checker {
|
|
|
1278
1278
|
checkLambda(expr, expected, scope, ctx) {
|
|
1279
1279
|
if (expected.kind !== "function") {
|
|
1280
1280
|
if (expected.kind !== "unknown") {
|
|
1281
|
-
this.diagnostics.error(`Lambda expression is not valid where type '${T.typeToString(expected)}' is expected`, expr.line, expr.col);
|
|
1281
|
+
this.diagnostics.error("KS4056", `Lambda expression is not valid where type '${T.typeToString(expected)}' is expected`, expr.line, expr.col);
|
|
1282
1282
|
}
|
|
1283
1283
|
const paramTypes = expr.params.map((p) => this.resolveType(p.type, expr.line, expr.col));
|
|
1284
1284
|
const lambdaScope = scope.child();
|
|
@@ -1289,13 +1289,13 @@ export class Checker {
|
|
|
1289
1289
|
return T.functionType(paramTypes, actualReturnType);
|
|
1290
1290
|
}
|
|
1291
1291
|
if (expr.params.length !== expected.params.length) {
|
|
1292
|
-
this.diagnostics.error(`Lambda has ${expr.params.length} parameter(s), but ${expected.params.length} were expected`, expr.line, expr.col);
|
|
1292
|
+
this.diagnostics.error("KS4057", `Lambda has ${expr.params.length} parameter(s), but ${expected.params.length} were expected`, expr.line, expr.col);
|
|
1293
1293
|
}
|
|
1294
1294
|
const paramTypes = expr.params.map((p, i) => {
|
|
1295
1295
|
const declared = this.resolveType(p.type, expr.line, expr.col);
|
|
1296
1296
|
const expectedParamType = expected.params[i];
|
|
1297
1297
|
if (expectedParamType && !T.typesEqual(declared, expectedParamType)) {
|
|
1298
|
-
this.diagnostics.error(`Lambda parameter '${p.name}' has type '${T.typeToString(declared)}', expected '${T.typeToString(expectedParamType)}'`, expr.line, expr.col);
|
|
1298
|
+
this.diagnostics.error("KS4058", `Lambda parameter '${p.name}' has type '${T.typeToString(declared)}', expected '${T.typeToString(expectedParamType)}'`, expr.line, expr.col);
|
|
1299
1299
|
}
|
|
1300
1300
|
return declared;
|
|
1301
1301
|
});
|
|
@@ -1330,7 +1330,7 @@ export class Checker {
|
|
|
1330
1330
|
}
|
|
1331
1331
|
const actual = this.checkExpressionExpecting(expr.body, expectedReturnType, scope, lambdaCtx);
|
|
1332
1332
|
if (expectedReturnType.kind !== "unknown" && !this.isAssignableType(actual, expectedReturnType)) {
|
|
1333
|
-
this.diagnostics.error(`Lambda body has type '${T.typeToString(actual)}', expected '${T.typeToString(expectedReturnType)}'`, expr.line, expr.col);
|
|
1333
|
+
this.diagnostics.error("KS4059", `Lambda body has type '${T.typeToString(actual)}', expected '${T.typeToString(expectedReturnType)}'`, expr.line, expr.col);
|
|
1334
1334
|
}
|
|
1335
1335
|
return actual;
|
|
1336
1336
|
}
|
|
@@ -1361,12 +1361,12 @@ export class Checker {
|
|
|
1361
1361
|
if (nullSide) {
|
|
1362
1362
|
const otherType = nullSide === "left" ? rightType : leftType;
|
|
1363
1363
|
if (otherType.kind !== "nullable" && otherType.kind !== "unknown") {
|
|
1364
|
-
this.diagnostics.error(`Type '${T.typeToString(otherType)}' can never be null — only a nullable type (e.g. '${T.typeToString(otherType)}?') can be compared to 'null'`, line, col);
|
|
1364
|
+
this.diagnostics.error("KS4060", `Type '${T.typeToString(otherType)}' can never be null — only a nullable type (e.g. '${T.typeToString(otherType)}?') can be compared to 'null'`, line, col);
|
|
1365
1365
|
}
|
|
1366
1366
|
return T.BOOL;
|
|
1367
1367
|
}
|
|
1368
1368
|
if (!T.typesEqual(leftType, rightType)) {
|
|
1369
|
-
this.diagnostics.error(`Cannot compare '${T.typeToString(leftType)}' with '${T.typeToString(rightType)}'`, line, col);
|
|
1369
|
+
this.diagnostics.error("KS4061", `Cannot compare '${T.typeToString(leftType)}' with '${T.typeToString(rightType)}'`, line, col);
|
|
1370
1370
|
}
|
|
1371
1371
|
return T.BOOL;
|
|
1372
1372
|
}
|
|
@@ -1387,7 +1387,7 @@ export class Checker {
|
|
|
1387
1387
|
expr.args.forEach((a) => this.checkExpression(a, scope, ctx));
|
|
1388
1388
|
return T.VOID;
|
|
1389
1389
|
}
|
|
1390
|
-
this.diagnostics.error(`Undefined function '${expr.callee.name}'`, expr.line, expr.col);
|
|
1390
|
+
this.diagnostics.error("KS4062", `Undefined function '${expr.callee.name}'`, expr.line, expr.col);
|
|
1391
1391
|
expr.args.forEach((a) => this.checkExpression(a, scope, ctx));
|
|
1392
1392
|
return T.UNKNOWN;
|
|
1393
1393
|
}
|
|
@@ -1427,43 +1427,43 @@ export class Checker {
|
|
|
1427
1427
|
return calleeType.returnType;
|
|
1428
1428
|
}
|
|
1429
1429
|
if (calleeType.kind !== "unknown") {
|
|
1430
|
-
this.diagnostics.error(`Cannot call a value of type '${T.typeToString(calleeType)}'`, expr.line, expr.col);
|
|
1430
|
+
this.diagnostics.error("KS4063", `Cannot call a value of type '${T.typeToString(calleeType)}'`, expr.line, expr.col);
|
|
1431
1431
|
}
|
|
1432
1432
|
expr.args.forEach((a) => this.checkExpression(a, scope, ctx));
|
|
1433
1433
|
return T.UNKNOWN;
|
|
1434
1434
|
}
|
|
1435
1435
|
checkArgs(expr, paramTypes, scope, ctx) {
|
|
1436
1436
|
if (expr.args.length !== paramTypes.length) {
|
|
1437
|
-
this.diagnostics.error(`Expected ${paramTypes.length} argument(s), got ${expr.args.length}`, expr.line, expr.col);
|
|
1437
|
+
this.diagnostics.error("KS4064", `Expected ${paramTypes.length} argument(s), got ${expr.args.length}`, expr.line, expr.col);
|
|
1438
1438
|
}
|
|
1439
1439
|
expr.args.forEach((arg, i) => {
|
|
1440
1440
|
const expected = paramTypes[i] ?? T.UNKNOWN;
|
|
1441
1441
|
const argType = this.checkExpressionExpecting(arg, expected, scope, ctx);
|
|
1442
1442
|
if (!this.isAssignableType(argType, expected)) {
|
|
1443
|
-
this.diagnostics.error(`Argument ${i + 1} has type '${T.typeToString(argType)}', expected '${T.typeToString(expected)}'`, arg.line, arg.col);
|
|
1443
|
+
this.diagnostics.error("KS4065", `Argument ${i + 1} has type '${T.typeToString(argType)}', expected '${T.typeToString(expected)}'`, arg.line, arg.col);
|
|
1444
1444
|
}
|
|
1445
1445
|
});
|
|
1446
1446
|
}
|
|
1447
1447
|
checkNew(expr, scope, ctx) {
|
|
1448
1448
|
if (this.interfaces.has(expr.className)) {
|
|
1449
|
-
this.diagnostics.error(`Cannot instantiate interface '${expr.className}'`, expr.line, expr.col);
|
|
1449
|
+
this.diagnostics.error("KS4066", `Cannot instantiate interface '${expr.className}'`, expr.line, expr.col);
|
|
1450
1450
|
expr.args.forEach((a) => this.checkExpression(a, scope, ctx));
|
|
1451
1451
|
return T.UNKNOWN;
|
|
1452
1452
|
}
|
|
1453
1453
|
if (this.enums.has(expr.className)) {
|
|
1454
|
-
this.diagnostics.error(`Cannot instantiate enum '${expr.className}'`, expr.line, expr.col);
|
|
1454
|
+
this.diagnostics.error("KS4067", `Cannot instantiate enum '${expr.className}'`, expr.line, expr.col);
|
|
1455
1455
|
expr.args.forEach((a) => this.checkExpression(a, scope, ctx));
|
|
1456
1456
|
return T.UNKNOWN;
|
|
1457
1457
|
}
|
|
1458
1458
|
const info = this.classes.get(expr.className);
|
|
1459
1459
|
if (!info) {
|
|
1460
|
-
this.diagnostics.error(`Unknown class '${expr.className}'`, expr.line, expr.col);
|
|
1460
|
+
this.diagnostics.error("KS4068", `Unknown class '${expr.className}'`, expr.line, expr.col);
|
|
1461
1461
|
expr.args.forEach((a) => this.checkExpression(a, scope, ctx));
|
|
1462
1462
|
return T.UNKNOWN;
|
|
1463
1463
|
}
|
|
1464
1464
|
let typeArg;
|
|
1465
1465
|
if (info.typeParam && !expr.typeArgs) {
|
|
1466
|
-
this.diagnostics.error(`Generic class '${expr.className}' requires a type argument (e.g. 'new ${expr.className}<T>(...)')`, expr.line, expr.col);
|
|
1466
|
+
this.diagnostics.error("KS4069", `Generic class '${expr.className}' requires a type argument (e.g. 'new ${expr.className}<T>(...)')`, expr.line, expr.col);
|
|
1467
1467
|
// Abstractly-typed (T-containing) ctor params, un-substitutable
|
|
1468
1468
|
// without a real type argument, would otherwise cascade into a
|
|
1469
1469
|
// confusing "expected 'T'" error on every argument — one clear error
|
|
@@ -1472,7 +1472,7 @@ export class Checker {
|
|
|
1472
1472
|
return T.UNKNOWN;
|
|
1473
1473
|
}
|
|
1474
1474
|
if (!info.typeParam && expr.typeArgs) {
|
|
1475
|
-
this.diagnostics.error(`Class '${expr.className}' is not generic — it doesn't take a type argument`, expr.line, expr.col);
|
|
1475
|
+
this.diagnostics.error("KS4070", `Class '${expr.className}' is not generic — it doesn't take a type argument`, expr.line, expr.col);
|
|
1476
1476
|
expr.args.forEach((a) => this.checkExpression(a, scope, ctx));
|
|
1477
1477
|
return T.UNKNOWN;
|
|
1478
1478
|
}
|
|
@@ -1487,13 +1487,13 @@ export class Checker {
|
|
|
1487
1487
|
ctorParams = ctorParams.map((p) => this.substituteTypeParam(p, paramName, arg));
|
|
1488
1488
|
}
|
|
1489
1489
|
if (expr.args.length !== ctorParams.length) {
|
|
1490
|
-
this.diagnostics.error(`Expected ${ctorParams.length} constructor argument(s), got ${expr.args.length}`, expr.line, expr.col);
|
|
1490
|
+
this.diagnostics.error("KS4071", `Expected ${ctorParams.length} constructor argument(s), got ${expr.args.length}`, expr.line, expr.col);
|
|
1491
1491
|
}
|
|
1492
1492
|
expr.args.forEach((arg, i) => {
|
|
1493
1493
|
const expected = ctorParams[i] ?? T.UNKNOWN;
|
|
1494
1494
|
const argType = this.checkExpressionExpecting(arg, expected, scope, ctx);
|
|
1495
1495
|
if (!this.isAssignableType(argType, expected)) {
|
|
1496
|
-
this.diagnostics.error(`Constructor argument ${i + 1} has type '${T.typeToString(argType)}', expected '${T.typeToString(expected)}'`, arg.line, arg.col);
|
|
1496
|
+
this.diagnostics.error("KS4072", `Constructor argument ${i + 1} has type '${T.typeToString(argType)}', expected '${T.typeToString(expected)}'`, arg.line, arg.col);
|
|
1497
1497
|
}
|
|
1498
1498
|
});
|
|
1499
1499
|
return T.classType(expr.className, typeArg);
|
|
@@ -1538,7 +1538,7 @@ export class Checker {
|
|
|
1538
1538
|
// inference is needed there at all.
|
|
1539
1539
|
checkArrayMap(expr, elementType, scope, ctx) {
|
|
1540
1540
|
if (expr.args.length !== 1) {
|
|
1541
|
-
this.diagnostics.error(`Map expects exactly 1 argument, got ${expr.args.length}`, expr.line, expr.col);
|
|
1541
|
+
this.diagnostics.error("KS4073", `Map expects exactly 1 argument, got ${expr.args.length}`, expr.line, expr.col);
|
|
1542
1542
|
expr.args.forEach((a) => this.checkExpression(a, scope, ctx));
|
|
1543
1543
|
return T.arrayOf(T.UNKNOWN);
|
|
1544
1544
|
}
|
|
@@ -1547,12 +1547,12 @@ export class Checker {
|
|
|
1547
1547
|
const argType = this.checkExpressionExpecting(arg, expectedCallbackType, scope, ctx);
|
|
1548
1548
|
if (argType.kind !== "function") {
|
|
1549
1549
|
if (argType.kind !== "unknown") {
|
|
1550
|
-
this.diagnostics.error(`Map expects a function as its argument, got '${T.typeToString(argType)}'`, arg.line, arg.col);
|
|
1550
|
+
this.diagnostics.error("KS4074", `Map expects a function as its argument, got '${T.typeToString(argType)}'`, arg.line, arg.col);
|
|
1551
1551
|
}
|
|
1552
1552
|
return T.arrayOf(T.UNKNOWN);
|
|
1553
1553
|
}
|
|
1554
1554
|
if (argType.params.length !== 1 || !this.isAssignableType(elementType, argType.params[0])) {
|
|
1555
|
-
this.diagnostics.error(`Map callback must take a single '${T.typeToString(elementType)}' parameter, got '${T.typeToString(argType)}'`, arg.line, arg.col);
|
|
1555
|
+
this.diagnostics.error("KS4075", `Map callback must take a single '${T.typeToString(elementType)}' parameter, got '${T.typeToString(argType)}'`, arg.line, arg.col);
|
|
1556
1556
|
}
|
|
1557
1557
|
return T.arrayOf(argType.returnType);
|
|
1558
1558
|
}
|
|
@@ -1573,7 +1573,7 @@ export class Checker {
|
|
|
1573
1573
|
this.recordHover(expr.object.line, expr.object.col, `enum ${objName}`);
|
|
1574
1574
|
const enumInfo = this.enums.get(objName);
|
|
1575
1575
|
if (!enumInfo.members.has(expr.property)) {
|
|
1576
|
-
this.diagnostics.error(`Enum '${objName}' has no member '${expr.property}'`, expr.line, expr.col);
|
|
1576
|
+
this.diagnostics.error("KS4076", `Enum '${objName}' has no member '${expr.property}'`, expr.line, expr.col);
|
|
1577
1577
|
return { type: T.UNKNOWN, methodInfo: null };
|
|
1578
1578
|
}
|
|
1579
1579
|
return { type: T.enumType(objName), methodInfo: null };
|
|
@@ -1590,13 +1590,13 @@ export class Checker {
|
|
|
1590
1590
|
this.checkAccessibility(method.info.visibility, method.owner, ctx, expr.property, expr.line, expr.col);
|
|
1591
1591
|
return { type: method.info.returnType, methodInfo: method.info };
|
|
1592
1592
|
}
|
|
1593
|
-
this.diagnostics.error(`Class '${objName}' has no static member '${expr.property}'`, expr.line, expr.col);
|
|
1593
|
+
this.diagnostics.error("KS4077", `Class '${objName}' has no static member '${expr.property}'`, expr.line, expr.col);
|
|
1594
1594
|
return { type: T.UNKNOWN, methodInfo: null };
|
|
1595
1595
|
}
|
|
1596
1596
|
}
|
|
1597
1597
|
const objectType = this.checkExpression(expr.object, scope, ctx);
|
|
1598
1598
|
if (objectType.kind === "nullable") {
|
|
1599
|
-
this.diagnostics.error(`Cannot access member '${expr.property}' on possibly-null type '${T.typeToString(objectType)}' — check for null first (e.g. 'if (x != null) { ... }')`, expr.line, expr.col);
|
|
1599
|
+
this.diagnostics.error("KS4078", `Cannot access member '${expr.property}' on possibly-null type '${T.typeToString(objectType)}' — check for null first (e.g. 'if (x != null) { ... }')`, expr.line, expr.col);
|
|
1600
1600
|
return { type: T.UNKNOWN, methodInfo: null };
|
|
1601
1601
|
}
|
|
1602
1602
|
if (objectType.kind === "string") {
|
|
@@ -1610,7 +1610,7 @@ export class Checker {
|
|
|
1610
1610
|
const method = STRING_METHODS[expr.property];
|
|
1611
1611
|
if (method)
|
|
1612
1612
|
return { type: method.returnType, methodInfo: method };
|
|
1613
|
-
this.diagnostics.error(`Unknown string member '${expr.property}'`, expr.line, expr.col);
|
|
1613
|
+
this.diagnostics.error("KS4079", `Unknown string member '${expr.property}'`, expr.line, expr.col);
|
|
1614
1614
|
return { type: T.UNKNOWN, methodInfo: null };
|
|
1615
1615
|
}
|
|
1616
1616
|
if (objectType.kind === "array") {
|
|
@@ -1626,7 +1626,7 @@ export class Checker {
|
|
|
1626
1626
|
// ever reaching checkMember, so `arr.Map(f)` works; `arr.Map` used as
|
|
1627
1627
|
// a bare value (not called) falls through to this error, same as an
|
|
1628
1628
|
// unknown member — a known v1 restriction.
|
|
1629
|
-
this.diagnostics.error(`Unknown array member '${expr.property}'`, expr.line, expr.col);
|
|
1629
|
+
this.diagnostics.error("KS4080", `Unknown array member '${expr.property}'`, expr.line, expr.col);
|
|
1630
1630
|
return { type: T.UNKNOWN, methodInfo: null };
|
|
1631
1631
|
}
|
|
1632
1632
|
if (objectType.kind === "state") {
|
|
@@ -1642,7 +1642,7 @@ export class Checker {
|
|
|
1642
1642
|
};
|
|
1643
1643
|
return { type: method.returnType, methodInfo: method };
|
|
1644
1644
|
}
|
|
1645
|
-
this.diagnostics.error(`Unknown state member '${expr.property}'`, expr.line, expr.col);
|
|
1645
|
+
this.diagnostics.error("KS4081", `Unknown state member '${expr.property}'`, expr.line, expr.col);
|
|
1646
1646
|
return { type: T.UNKNOWN, methodInfo: null };
|
|
1647
1647
|
}
|
|
1648
1648
|
if (objectType.kind === "class") {
|
|
@@ -1658,7 +1658,7 @@ export class Checker {
|
|
|
1658
1658
|
if (isAssignTarget && !field.info.hasSetter) {
|
|
1659
1659
|
const inOwnCtor = ctx.inConstructor && ctx.currentClass?.name === field.owner;
|
|
1660
1660
|
if (!inOwnCtor) {
|
|
1661
|
-
this.diagnostics.error(`'${expr.property}' has no setter and can only be assigned within ${field.owner}'s constructor`, expr.line, expr.col);
|
|
1661
|
+
this.diagnostics.error("KS4082", `'${expr.property}' has no setter and can only be assigned within ${field.owner}'s constructor`, expr.line, expr.col);
|
|
1662
1662
|
}
|
|
1663
1663
|
}
|
|
1664
1664
|
return { type: substitute(field.info.type), methodInfo: null };
|
|
@@ -1669,7 +1669,7 @@ export class Checker {
|
|
|
1669
1669
|
const info = paramName ? { ...method.info, params: method.info.params.map(substitute), returnType: substitute(method.info.returnType) } : method.info;
|
|
1670
1670
|
return { type: info.returnType, methodInfo: info };
|
|
1671
1671
|
}
|
|
1672
|
-
this.diagnostics.error(`Class '${objectType.name}' has no member '${expr.property}'`, expr.line, expr.col);
|
|
1672
|
+
this.diagnostics.error("KS4083", `Class '${objectType.name}' has no member '${expr.property}'`, expr.line, expr.col);
|
|
1673
1673
|
return { type: T.UNKNOWN, methodInfo: null };
|
|
1674
1674
|
}
|
|
1675
1675
|
if (objectType.kind === "interface") {
|
|
@@ -1684,11 +1684,11 @@ export class Checker {
|
|
|
1684
1684
|
const returnType = substitute(sig.returnType);
|
|
1685
1685
|
return { type: returnType, methodInfo: { params, returnType, visibility: "public", isVirtual: false, isOverride: false } };
|
|
1686
1686
|
}
|
|
1687
|
-
this.diagnostics.error(`Interface '${objectType.name}' has no member '${expr.property}'`, expr.line, expr.col);
|
|
1687
|
+
this.diagnostics.error("KS4084", `Interface '${objectType.name}' has no member '${expr.property}'`, expr.line, expr.col);
|
|
1688
1688
|
return { type: T.UNKNOWN, methodInfo: null };
|
|
1689
1689
|
}
|
|
1690
1690
|
if (objectType.kind !== "unknown") {
|
|
1691
|
-
this.diagnostics.error(`Cannot access member '${expr.property}' on type '${T.typeToString(objectType)}'`, expr.line, expr.col);
|
|
1691
|
+
this.diagnostics.error("KS4085", `Cannot access member '${expr.property}' on type '${T.typeToString(objectType)}'`, expr.line, expr.col);
|
|
1692
1692
|
}
|
|
1693
1693
|
return { type: T.UNKNOWN, methodInfo: null };
|
|
1694
1694
|
}
|
|
@@ -1696,19 +1696,19 @@ export class Checker {
|
|
|
1696
1696
|
const subjectType = this.checkExpression(expr.subject, scope, ctx);
|
|
1697
1697
|
this.expectType(subjectType, T.STRING, expr.line, expr.col, "match subject");
|
|
1698
1698
|
if (expr.arms.length === 0) {
|
|
1699
|
-
this.diagnostics.error(`match expression must have at least one arm`, expr.line, expr.col);
|
|
1699
|
+
this.diagnostics.error("KS4086", `match expression must have at least one arm`, expr.line, expr.col);
|
|
1700
1700
|
return T.UNKNOWN;
|
|
1701
1701
|
}
|
|
1702
1702
|
const lastArm = expr.arms[expr.arms.length - 1];
|
|
1703
1703
|
if (lastArm.pattern.kind !== "WildcardPattern") {
|
|
1704
|
-
this.diagnostics.error(`match expression must end with a wildcard '_' arm`, lastArm.line, lastArm.col);
|
|
1704
|
+
this.diagnostics.error("KS4087", `match expression must end with a wildcard '_' arm`, lastArm.line, lastArm.col);
|
|
1705
1705
|
}
|
|
1706
1706
|
let resultType = null;
|
|
1707
1707
|
for (const arm of expr.arms) {
|
|
1708
1708
|
if (arm.pattern.kind === "LiteralPattern") {
|
|
1709
1709
|
for (const value of arm.pattern.values) {
|
|
1710
1710
|
if (value.kind !== "StringLiteral") {
|
|
1711
|
-
this.diagnostics.error(`match patterns must be string literals`, value.line, value.col);
|
|
1711
|
+
this.diagnostics.error("KS4088", `match patterns must be string literals`, value.line, value.col);
|
|
1712
1712
|
}
|
|
1713
1713
|
else {
|
|
1714
1714
|
this.checkExpression(value, scope, ctx);
|
|
@@ -1721,7 +1721,7 @@ export class Checker {
|
|
|
1721
1721
|
resultType = armResultType;
|
|
1722
1722
|
}
|
|
1723
1723
|
else if (!this.isAssignableType(armResultType, resultType)) {
|
|
1724
|
-
this.diagnostics.error(`match arm result type '${T.typeToString(armResultType)}' does not match preceding arms' type '${T.typeToString(resultType)}'`, arm.line, arm.col);
|
|
1724
|
+
this.diagnostics.error("KS4089", `match arm result type '${T.typeToString(armResultType)}' does not match preceding arms' type '${T.typeToString(resultType)}'`, arm.line, arm.col);
|
|
1725
1725
|
}
|
|
1726
1726
|
}
|
|
1727
1727
|
return resultType ?? T.UNKNOWN;
|