kopscript 0.7.1 → 0.8.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 +44 -5
- package/README.md +10 -1
- package/dist/checker.js +89 -89
- package/dist/diagnostics.js +5 -5
- package/dist/lexer.js +4 -4
- package/dist/modules.js +9 -9
- package/dist/parser.js +22 -22
- 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/LLM.md
CHANGED
|
@@ -21,7 +21,7 @@ ks build|check <file.ks> --json # single JSON object on stdout instead of huma
|
|
|
21
21
|
`--json` output shape (also what a tool/agent should parse instead of scraping text):
|
|
22
22
|
|
|
23
23
|
```json
|
|
24
|
-
{ "success": false, "diagnostics": [{ "severity": "error", "message": "...", "line": 2, "col": 14, "file": "/abs/path.ks" }], "written": [] }
|
|
24
|
+
{ "success": false, "diagnostics": [{ "code": "KS4065", "severity": "error", "message": "...", "line": 2, "col": 14, "file": "/abs/path.ks" }], "written": [] }
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
`written` is the absolute paths actually written (`build` only, and only on success — a
|
|
@@ -504,9 +504,48 @@ signature, use different names, not overloading. (Nested function/class/interfac
|
|
|
504
504
|
declarations, by contrast, *are* a clean compile error: "Nested ... declarations are not
|
|
505
505
|
supported".)
|
|
506
506
|
|
|
507
|
+
## Common mistakes (seeded from real generation failures)
|
|
508
|
+
|
|
509
|
+
Real, observed cases where a plausible-looking guess was wrong — not hypothetical gotchas:
|
|
510
|
+
|
|
511
|
+
- **No implicit `this`.** Every reference to a member of the enclosing class — a field, a
|
|
512
|
+
property, a method call — must be `this.X`, always, everywhere, including inside a lambda.
|
|
513
|
+
`Increment()` inside a method body does **not** resolve to `this.Increment()` the way it
|
|
514
|
+
would in Java/C#/Python; it's an undefined-identifier error unless `Increment` is a real
|
|
515
|
+
free function. (Discovered building the template compiler above — templates need a
|
|
516
|
+
dedicated rewriter, `qualifyThis`, specifically because this isn't automatic.)
|
|
517
|
+
- **No `let`/`var`, ever.** Every local is `Type name = value;` — writing `let x = 5;` or a
|
|
518
|
+
bare `const x = 5;` (missing the type) is a parse error, not a lenient inferred form.
|
|
519
|
+
- **No generic functions.** `T Identity<T>(T x) { return x; }` doesn't parse — only classes
|
|
520
|
+
and interfaces take `<T>`. Don't reach for this even though every mainstream generic
|
|
521
|
+
language supports it.
|
|
522
|
+
- **Early-return doesn't narrow a nullable type.** `if (x == null) { return; } print(x.Length);`
|
|
523
|
+
still errors on `x.Length` — narrowing is scope-based (an `if`/`else` block), not
|
|
524
|
+
control-flow/reachability-based. Wrap the rest of the logic in the `if (x != null) { ... }`
|
|
525
|
+
block instead of guard-clause-and-continue.
|
|
526
|
+
- **Never write `async` on an `extern` signature.** `async task<string> text();` inside an
|
|
527
|
+
`extern class` is a parse error — declare the return type as `task<string> text();`
|
|
528
|
+
directly; `async` only means something for a body the checker validates, and an `extern`
|
|
529
|
+
signature has no body.
|
|
530
|
+
- **No object-literal syntax, anywhere, ever.** Reaching for `{ method: "POST", body: x }` to
|
|
531
|
+
call a JS API that expects one (a `fetch`-style options argument) doesn't compile — there's
|
|
532
|
+
no way to construct that value in KopScript at all. See the `extern`/`raw string` sections
|
|
533
|
+
above and Kopular's `http_runtime.js` for the actual workaround (a small hand-written JS
|
|
534
|
+
shim), not a language feature to reach for.
|
|
535
|
+
- **No ternary expression.** `cond ? a : b` doesn't parse. Use `match` on a `bool`, or an
|
|
536
|
+
`if`/`else` assigning to a local declared above it.
|
|
537
|
+
- **Two methods with the same name in one class don't error** — the second silently replaces
|
|
538
|
+
the first (see "Sharp edge" above). If a generated program seems to call the wrong
|
|
539
|
+
implementation of something, check for an accidental duplicate name before assuming a
|
|
540
|
+
compiler bug.
|
|
541
|
+
|
|
507
542
|
## Diagnostics
|
|
508
543
|
|
|
509
|
-
Every compiler error/warning is `{ severity, message, line, col }` (1-based).
|
|
510
|
-
|
|
511
|
-
`
|
|
512
|
-
|
|
544
|
+
Every compiler error/warning is `{ code, severity, message, line, col }` (1-based).
|
|
545
|
+
`code` is `KS` + a number, stable across compiler versions even when `message`'s wording
|
|
546
|
+
changes — match on `code` in tooling, not on `message` text. Ranges by pipeline stage,
|
|
547
|
+
never reused once assigned: `KS1xxx` lexer, `KS2xxx` parser, `KS3xxx` module/`using`
|
|
548
|
+
resolution, `KS4xxx` checker (the large majority of real errors), `KS5xxx` templates.
|
|
549
|
+
CLI output format: `` file:line:col - severity code: message `` plus a source line and a
|
|
550
|
+
`^` pointer. `DiagnosticBag.hasErrors` gates whether codegen runs at all — a program with
|
|
551
|
+
any error produces no output.
|
package/README.md
CHANGED
|
@@ -755,7 +755,7 @@ programmatically instead of scraping formatted text:
|
|
|
755
755
|
{
|
|
756
756
|
"success": false,
|
|
757
757
|
"diagnostics": [
|
|
758
|
-
{ "severity": "error", "message": "Argument 2 has type 'string', expected 'number'", "line": 2, "col": 14, "file": "/abs/path/to/file.ks" }
|
|
758
|
+
{ "code": "KS4065", "severity": "error", "message": "Argument 2 has type 'string', expected 'number'", "line": 2, "col": 14, "file": "/abs/path/to/file.ks" }
|
|
759
759
|
],
|
|
760
760
|
"written": []
|
|
761
761
|
}
|
|
@@ -767,6 +767,15 @@ error-free). A missing entry file reports `{ "success": false, "diagnostics": []
|
|
|
767
767
|
"written": [], "error": "cannot find file '...'" }` instead of throwing. Exit code is 0
|
|
768
768
|
exactly when `success` is `true`, both with and without `--json`.
|
|
769
769
|
|
|
770
|
+
Every diagnostic carries a stable `code` (`KS` + a number) alongside its human-readable
|
|
771
|
+
`message` — meant for a tool/agent to pattern-match reliably (`code === "KS4065"`) instead
|
|
772
|
+
of parsing prose that can be reworded between versions. Codes are grouped by pipeline stage
|
|
773
|
+
and never reused once assigned: `KS1xxx` lexer, `KS2xxx` parser, `KS3xxx` module/`using`
|
|
774
|
+
resolution, `KS4xxx` the checker (by far the largest category — most real type errors live
|
|
775
|
+
here), `KS5xxx` templates. There's no generated reference doc mapping every code to an
|
|
776
|
+
explanation yet — for now, `message` is still the primary explanation; `code` is for
|
|
777
|
+
matching, not (yet) for looking up docs.
|
|
778
|
+
|
|
770
779
|
During development, use `npm run ks -- <build|run|watch|check> <file.ks>` (backed by
|
|
771
780
|
`tsx`), or run `npm run build` to compile the TypeScript compiler itself to `dist/` and
|
|
772
781
|
use `node dist/cli.js` directly.
|
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;
|
package/dist/diagnostics.js
CHANGED
|
@@ -2,11 +2,11 @@ export class DiagnosticBag {
|
|
|
2
2
|
constructor() {
|
|
3
3
|
this.diagnostics = [];
|
|
4
4
|
}
|
|
5
|
-
error(message, line, col) {
|
|
6
|
-
this.diagnostics.push({ severity: "error", message, line, col });
|
|
5
|
+
error(code, message, line, col) {
|
|
6
|
+
this.diagnostics.push({ code, severity: "error", message, line, col });
|
|
7
7
|
}
|
|
8
|
-
warning(message, line, col) {
|
|
9
|
-
this.diagnostics.push({ severity: "warning", message, line, col });
|
|
8
|
+
warning(code, message, line, col) {
|
|
9
|
+
this.diagnostics.push({ code, severity: "warning", message, line, col });
|
|
10
10
|
}
|
|
11
11
|
get hasErrors() {
|
|
12
12
|
return this.diagnostics.some((d) => d.severity === "error");
|
|
@@ -17,7 +17,7 @@ export class DiagnosticBag {
|
|
|
17
17
|
.map((d) => {
|
|
18
18
|
const sourceLine = lines[d.line - 1] ?? "";
|
|
19
19
|
const pointer = " ".repeat(Math.max(0, d.col - 1)) + "^";
|
|
20
|
-
return (`${fileName}:${d.line}:${d.col} - ${d.severity}: ${d.message}\n` +
|
|
20
|
+
return (`${fileName}:${d.line}:${d.col} - ${d.severity} ${d.code}: ${d.message}\n` +
|
|
21
21
|
` ${sourceLine}\n` +
|
|
22
22
|
` ${pointer}`);
|
|
23
23
|
})
|
package/dist/lexer.js
CHANGED
|
@@ -107,7 +107,7 @@ export class Lexer {
|
|
|
107
107
|
value += this.readStringChar();
|
|
108
108
|
}
|
|
109
109
|
if (this.isAtEnd()) {
|
|
110
|
-
this.diagnostics.error("Unterminated string literal", line, col);
|
|
110
|
+
this.diagnostics.error("KS1001", "Unterminated string literal", line, col);
|
|
111
111
|
}
|
|
112
112
|
else {
|
|
113
113
|
this.advance(); // closing quote
|
|
@@ -127,7 +127,7 @@ export class Lexer {
|
|
|
127
127
|
raw += this.advance();
|
|
128
128
|
}
|
|
129
129
|
if (this.isAtEnd()) {
|
|
130
|
-
this.diagnostics.error("Unterminated interpolated string literal", line, col);
|
|
130
|
+
this.diagnostics.error("KS1002", "Unterminated interpolated string literal", line, col);
|
|
131
131
|
}
|
|
132
132
|
else {
|
|
133
133
|
this.advance(); // closing quote
|
|
@@ -157,7 +157,7 @@ export class Lexer {
|
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
if (this.isAtEnd()) {
|
|
160
|
-
this.diagnostics.error("Unterminated regex literal", line, col);
|
|
160
|
+
this.diagnostics.error("KS1003", "Unterminated regex literal", line, col);
|
|
161
161
|
}
|
|
162
162
|
else {
|
|
163
163
|
this.advance(); // closing quote
|
|
@@ -284,7 +284,7 @@ export class Lexer {
|
|
|
284
284
|
}
|
|
285
285
|
break;
|
|
286
286
|
}
|
|
287
|
-
this.diagnostics.error(`Unexpected character '${c}'`, line, col);
|
|
287
|
+
this.diagnostics.error("KS1004", `Unexpected character '${c}'`, line, col);
|
|
288
288
|
return this.next();
|
|
289
289
|
}
|
|
290
290
|
make(kind, lexeme, line, col) {
|
package/dist/modules.js
CHANGED
|
@@ -25,21 +25,21 @@ function expandTemplates(program, absPath, diagnostics, fileOverrides) {
|
|
|
25
25
|
continue;
|
|
26
26
|
const templateRef = stmt.template;
|
|
27
27
|
if (stmt.methods.some((m) => m.name === "Render" && !m.isStatic)) {
|
|
28
|
-
diagnostics.error(`Class '${stmt.name}' has both a 'template' declaration and a hand-written 'Render()' method — remove one`, templateRef.line, templateRef.col);
|
|
28
|
+
diagnostics.error("KS3001", `Class '${stmt.name}' has both a 'template' declaration and a hand-written 'Render()' method — remove one`, templateRef.line, templateRef.col);
|
|
29
29
|
continue;
|
|
30
30
|
}
|
|
31
31
|
const templatePath = resolve(dirname(absPath), templateRef.path);
|
|
32
32
|
watchFiles.push(templatePath);
|
|
33
33
|
const exists = fileOverrides?.has(templatePath) || existsSync(templatePath);
|
|
34
34
|
if (!exists) {
|
|
35
|
-
diagnostics.error(`Cannot find template file '${templateRef.path}' (looked for '${displayPath(templatePath)}')`, templateRef.line, templateRef.col);
|
|
35
|
+
diagnostics.error("KS3002", `Cannot find template file '${templateRef.path}' (looked for '${displayPath(templatePath)}')`, templateRef.line, templateRef.col);
|
|
36
36
|
continue;
|
|
37
37
|
}
|
|
38
38
|
const templateSource = fileOverrides?.get(templatePath) ?? readFileSync(templatePath, "utf-8");
|
|
39
39
|
const templateDiagnostics = new DiagnosticBag();
|
|
40
40
|
const root = new TemplateParser(templateSource, templateDiagnostics).parseDocument();
|
|
41
41
|
for (const d of templateDiagnostics.diagnostics) {
|
|
42
|
-
diagnostics.error(`[${templateRef.path}:${d.line}:${d.col}] ${d.message}`, templateRef.line, templateRef.col);
|
|
42
|
+
diagnostics.error("KS3003", `[${templateRef.path}:${d.line}:${d.col}] ${d.message}`, templateRef.line, templateRef.col);
|
|
43
43
|
}
|
|
44
44
|
if (!root)
|
|
45
45
|
continue;
|
|
@@ -48,12 +48,12 @@ function expandTemplates(program, absPath, diagnostics, fileOverrides) {
|
|
|
48
48
|
const compileDiagnostics = new DiagnosticBag();
|
|
49
49
|
const { renderMethod, autoSubscribeFields } = new TemplateCompiler(memberNames, stateFieldNames, compileDiagnostics).compile(root, templateRef);
|
|
50
50
|
for (const d of compileDiagnostics.diagnostics) {
|
|
51
|
-
diagnostics.error(`[${templateRef.path}:${d.line}:${d.col}] ${d.message}`, templateRef.line, templateRef.col);
|
|
51
|
+
diagnostics.error("KS3004", `[${templateRef.path}:${d.line}:${d.col}] ${d.message}`, templateRef.line, templateRef.col);
|
|
52
52
|
}
|
|
53
53
|
stmt.methods.push(renderMethod);
|
|
54
54
|
if (autoSubscribeFields.length > 0) {
|
|
55
55
|
if (!stmt.constructor) {
|
|
56
|
-
diagnostics.error(`Class '${stmt.name}' has a template referencing state<T> field(s) (${autoSubscribeFields.join(", ")}) but no constructor to subscribe from`, templateRef.line, templateRef.col);
|
|
56
|
+
diagnostics.error("KS3005", `Class '${stmt.name}' has a template referencing state<T> field(s) (${autoSubscribeFields.join(", ")}) but no constructor to subscribe from`, templateRef.line, templateRef.col);
|
|
57
57
|
continue;
|
|
58
58
|
}
|
|
59
59
|
for (const fieldName of autoSubscribeFields) {
|
|
@@ -128,16 +128,16 @@ export function loadModuleGraph(entryAbsPath, fileOverrides) {
|
|
|
128
128
|
stack.push(absPath);
|
|
129
129
|
for (const u of program.usings) {
|
|
130
130
|
if (!u.path.startsWith("./") && !u.path.startsWith("../")) {
|
|
131
|
-
diagnostics.error(`'using' path '${u.path}' must be relative (start with './' or '../')`, u.line, u.col);
|
|
131
|
+
diagnostics.error("KS3006", `'using' path '${u.path}' must be relative (start with './' or '../')`, u.line, u.col);
|
|
132
132
|
continue;
|
|
133
133
|
}
|
|
134
134
|
const depPath = resolve(dirname(absPath), u.path) + ".ks";
|
|
135
135
|
if (!exists(depPath)) {
|
|
136
|
-
diagnostics.error(`Cannot find module '${u.path}' (looked for '${displayPath(depPath)}')`, u.line, u.col);
|
|
136
|
+
diagnostics.error("KS3007", `Cannot find module '${u.path}' (looked for '${displayPath(depPath)}')`, u.line, u.col);
|
|
137
137
|
continue;
|
|
138
138
|
}
|
|
139
139
|
if (stack.includes(depPath)) {
|
|
140
|
-
diagnostics.error(`Circular 'using' dependency: ${[...stack, depPath].map(displayPath).join(" -> ")}`, u.line, u.col);
|
|
140
|
+
diagnostics.error("KS3008", `Circular 'using' dependency: ${[...stack, depPath].map(displayPath).join(" -> ")}`, u.line, u.col);
|
|
141
141
|
continue;
|
|
142
142
|
}
|
|
143
143
|
record.dependencies.push(depPath);
|
|
@@ -180,7 +180,7 @@ export function compileGraph(entryAbsPath, fileOverrides) {
|
|
|
180
180
|
const mergeOne = (name, apply) => {
|
|
181
181
|
const existingFrom = importedFrom.get(name);
|
|
182
182
|
if (existingFrom && existingFrom !== depPath) {
|
|
183
|
-
mod.diagnostics.error(`'${name}' is exported by both '${displayPath(existingFrom)}' and '${displayPath(depPath)}' — ambiguous 'using'`, u.line, u.col);
|
|
183
|
+
mod.diagnostics.error("KS3009", `'${name}' is exported by both '${displayPath(existingFrom)}' and '${displayPath(depPath)}' — ambiguous 'using'`, u.line, u.col);
|
|
184
184
|
return;
|
|
185
185
|
}
|
|
186
186
|
importedFrom.set(name, depPath);
|
package/dist/parser.js
CHANGED
|
@@ -16,7 +16,7 @@ export class Parser {
|
|
|
16
16
|
while (!this.check(TokenKind.EOF)) {
|
|
17
17
|
if (this.check(TokenKind.Using)) {
|
|
18
18
|
const t = this.peek();
|
|
19
|
-
this.diagnostics.error("'using' directives must appear at the top of the file, before any other declaration", t.line, t.col);
|
|
19
|
+
this.diagnostics.error("KS2001", "'using' directives must appear at the top of the file, before any other declaration", t.line, t.col);
|
|
20
20
|
this.parseUsing(); // consume and discard so parsing can continue
|
|
21
21
|
continue;
|
|
22
22
|
}
|
|
@@ -44,7 +44,7 @@ export class Parser {
|
|
|
44
44
|
const expr = this.parseExpression();
|
|
45
45
|
if (!this.check(TokenKind.EOF)) {
|
|
46
46
|
const t = this.peek();
|
|
47
|
-
this.diagnostics.error(`Unexpected token '${t.lexeme}' after expression`, t.line, t.col);
|
|
47
|
+
this.diagnostics.error("KS2002", `Unexpected token '${t.lexeme}' after expression`, t.line, t.col);
|
|
48
48
|
}
|
|
49
49
|
return expr;
|
|
50
50
|
}
|
|
@@ -60,7 +60,7 @@ export class Parser {
|
|
|
60
60
|
const type = this.parseType();
|
|
61
61
|
if (!this.check(TokenKind.EOF)) {
|
|
62
62
|
const t = this.peek();
|
|
63
|
-
this.diagnostics.error(`Unexpected token '${t.lexeme}' after type`, t.line, t.col);
|
|
63
|
+
this.diagnostics.error("KS2003", `Unexpected token '${t.lexeme}' after type`, t.line, t.col);
|
|
64
64
|
}
|
|
65
65
|
return type;
|
|
66
66
|
}
|
|
@@ -152,11 +152,11 @@ export class Parser {
|
|
|
152
152
|
if (this.isDeclStart()) {
|
|
153
153
|
const decl = this.parseDeclaration(isExported);
|
|
154
154
|
if (decl.kind === "VarDecl") {
|
|
155
|
-
this.diagnostics.error("'public'/'private' cannot modify a variable declaration", modifierTok.line, modifierTok.col);
|
|
155
|
+
this.diagnostics.error("KS2004", "'public'/'private' cannot modify a variable declaration", modifierTok.line, modifierTok.col);
|
|
156
156
|
}
|
|
157
157
|
return decl;
|
|
158
158
|
}
|
|
159
|
-
this.diagnostics.error("Expected a class, interface, enum, or function declaration after 'public'/'private'", modifierTok.line, modifierTok.col);
|
|
159
|
+
this.diagnostics.error("KS2005", "Expected a class, interface, enum, or function declaration after 'public'/'private'", modifierTok.line, modifierTok.col);
|
|
160
160
|
throw new ParseError();
|
|
161
161
|
}
|
|
162
162
|
// Lookahead for the C#-style `Type name` declaration shape (locals and
|
|
@@ -204,7 +204,7 @@ export class Parser {
|
|
|
204
204
|
return { kind: "FunctionDecl", isExported, isAsync, name, params, returnType: type, body, line: start.line, col: start.col };
|
|
205
205
|
}
|
|
206
206
|
if (isAsync) {
|
|
207
|
-
this.diagnostics.error("'async' cannot modify a variable declaration", start.line, start.col);
|
|
207
|
+
this.diagnostics.error("KS2006", "'async' cannot modify a variable declaration", start.line, start.col);
|
|
208
208
|
}
|
|
209
209
|
this.consume(TokenKind.Assign, "Expected '=' in variable declaration");
|
|
210
210
|
const init = this.parseExpression();
|
|
@@ -273,7 +273,7 @@ export class Parser {
|
|
|
273
273
|
if (this.check(TokenKind.Template)) {
|
|
274
274
|
const templateStart = this.advance();
|
|
275
275
|
if (template) {
|
|
276
|
-
this.diagnostics.error(`Class '${name}' already has a 'template' declaration`, templateStart.line, templateStart.col);
|
|
276
|
+
this.diagnostics.error("KS2007", `Class '${name}' already has a 'template' declaration`, templateStart.line, templateStart.col);
|
|
277
277
|
}
|
|
278
278
|
this.consume(TokenKind.From, "Expected 'from \"<path>\"' after 'template'");
|
|
279
279
|
const pathTok = this.consume(TokenKind.String, "Expected a file path string after 'from'");
|
|
@@ -333,7 +333,7 @@ export class Parser {
|
|
|
333
333
|
const params = this.parseParamList();
|
|
334
334
|
const body = this.parseBlock();
|
|
335
335
|
if (isStatic && (isVirtual || isOverride)) {
|
|
336
|
-
this.diagnostics.error("'static' methods cannot be 'virtual' or 'override'", memberStart.line, memberStart.col);
|
|
336
|
+
this.diagnostics.error("KS2008", "'static' methods cannot be 'virtual' or 'override'", memberStart.line, memberStart.col);
|
|
337
337
|
}
|
|
338
338
|
methods.push({
|
|
339
339
|
kind: "MethodDecl",
|
|
@@ -354,13 +354,13 @@ export class Parser {
|
|
|
354
354
|
}
|
|
355
355
|
else if (this.check(TokenKind.LBrace)) {
|
|
356
356
|
if (isVirtual || isOverride) {
|
|
357
|
-
this.diagnostics.error("'virtual'/'override' are only valid on methods", memberStart.line, memberStart.col);
|
|
357
|
+
this.diagnostics.error("KS2009", "'virtual'/'override' are only valid on methods", memberStart.line, memberStart.col);
|
|
358
358
|
}
|
|
359
359
|
if (isAsync) {
|
|
360
|
-
this.diagnostics.error("'async' is only valid on methods", memberStart.line, memberStart.col);
|
|
360
|
+
this.diagnostics.error("KS2010", "'async' is only valid on methods", memberStart.line, memberStart.col);
|
|
361
361
|
}
|
|
362
362
|
if (isStatic) {
|
|
363
|
-
this.diagnostics.error("'static' properties are not supported in v1 (use a static field instead)", memberStart.line, memberStart.col);
|
|
363
|
+
this.diagnostics.error("KS2011", "'static' properties are not supported in v1 (use a static field instead)", memberStart.line, memberStart.col);
|
|
364
364
|
}
|
|
365
365
|
this.advance(); // '{'
|
|
366
366
|
this.consume(TokenKind.Get, "Expected 'get' in property accessor list");
|
|
@@ -386,10 +386,10 @@ export class Parser {
|
|
|
386
386
|
}
|
|
387
387
|
else {
|
|
388
388
|
if (isVirtual || isOverride) {
|
|
389
|
-
this.diagnostics.error("'virtual'/'override' are only valid on methods", memberStart.line, memberStart.col);
|
|
389
|
+
this.diagnostics.error("KS2012", "'virtual'/'override' are only valid on methods", memberStart.line, memberStart.col);
|
|
390
390
|
}
|
|
391
391
|
if (isAsync) {
|
|
392
|
-
this.diagnostics.error("'async' is only valid on methods", memberStart.line, memberStart.col);
|
|
392
|
+
this.diagnostics.error("KS2013", "'async' is only valid on methods", memberStart.line, memberStart.col);
|
|
393
393
|
}
|
|
394
394
|
let initializer = null;
|
|
395
395
|
if (isStatic) {
|
|
@@ -500,7 +500,7 @@ export class Parser {
|
|
|
500
500
|
const start = this.advance(); // 'raw'
|
|
501
501
|
const typeTok = this.consume(TokenKind.Identifier, "Expected 'string' after 'raw'");
|
|
502
502
|
if (typeTok.lexeme !== "string") {
|
|
503
|
-
this.diagnostics.error(`'raw' declarations must be of type 'string', got '${typeTok.lexeme}'`, typeTok.line, typeTok.col);
|
|
503
|
+
this.diagnostics.error("KS2014", `'raw' declarations must be of type 'string', got '${typeTok.lexeme}'`, typeTok.line, typeTok.col);
|
|
504
504
|
}
|
|
505
505
|
const name = this.consume(TokenKind.Identifier, "Expected name").lexeme;
|
|
506
506
|
this.consume(TokenKind.From, "Expected 'from \"<path>\"' after 'raw string <Name>'");
|
|
@@ -531,10 +531,10 @@ export class Parser {
|
|
|
531
531
|
if (this.check(TokenKind.Constructor)) {
|
|
532
532
|
const ctorTok = this.advance();
|
|
533
533
|
if (virtualTok) {
|
|
534
|
-
this.diagnostics.error(`'virtual' is not valid on an extern constructor`, virtualTok.line, virtualTok.col);
|
|
534
|
+
this.diagnostics.error("KS2015", `'virtual' is not valid on an extern constructor`, virtualTok.line, virtualTok.col);
|
|
535
535
|
}
|
|
536
536
|
if (hasConstructor) {
|
|
537
|
-
this.diagnostics.error(`Extern class '${name}' already has a constructor signature`, ctorTok.line, ctorTok.col);
|
|
537
|
+
this.diagnostics.error("KS2016", `Extern class '${name}' already has a constructor signature`, ctorTok.line, ctorTok.col);
|
|
538
538
|
}
|
|
539
539
|
ctorParams = this.parseParamList();
|
|
540
540
|
hasConstructor = true;
|
|
@@ -550,7 +550,7 @@ export class Parser {
|
|
|
550
550
|
}
|
|
551
551
|
else if (this.check(TokenKind.LBrace)) {
|
|
552
552
|
if (virtualTok) {
|
|
553
|
-
this.diagnostics.error(`'virtual' is not valid on an extern property`, virtualTok.line, virtualTok.col);
|
|
553
|
+
this.diagnostics.error("KS2017", `'virtual' is not valid on an extern property`, virtualTok.line, virtualTok.col);
|
|
554
554
|
}
|
|
555
555
|
this.advance();
|
|
556
556
|
this.consume(TokenKind.Get, "Expected 'get' in extern property accessor list");
|
|
@@ -566,7 +566,7 @@ export class Parser {
|
|
|
566
566
|
}
|
|
567
567
|
else {
|
|
568
568
|
if (virtualTok) {
|
|
569
|
-
this.diagnostics.error(`'virtual' is not valid on an extern property`, virtualTok.line, virtualTok.col);
|
|
569
|
+
this.diagnostics.error("KS2018", `'virtual' is not valid on an extern property`, virtualTok.line, virtualTok.col);
|
|
570
570
|
}
|
|
571
571
|
this.consume(TokenKind.Semicolon, "Expected ';' after extern property declaration");
|
|
572
572
|
properties.push({ isStatic, name: memberName, type, hasSetter: true });
|
|
@@ -671,7 +671,7 @@ export class Parser {
|
|
|
671
671
|
finallyBlock = this.parseBlock();
|
|
672
672
|
}
|
|
673
673
|
if (!catchBlock && !finallyBlock) {
|
|
674
|
-
this.diagnostics.error("'try' must be followed by 'catch' and/or 'finally'", start.line, start.col);
|
|
674
|
+
this.diagnostics.error("KS2019", "'try' must be followed by 'catch' and/or 'finally'", start.line, start.col);
|
|
675
675
|
}
|
|
676
676
|
return { kind: "TryStatement", tryBlock, catchParam, catchBlock, finallyBlock, line: start.line, col: start.col };
|
|
677
677
|
}
|
|
@@ -945,7 +945,7 @@ export class Parser {
|
|
|
945
945
|
this.consume(TokenKind.RParen, "Expected ')' after expression");
|
|
946
946
|
return expr;
|
|
947
947
|
}
|
|
948
|
-
this.diagnostics.error(`Unexpected token '${t.lexeme || t.kind}'`, t.line, t.col);
|
|
948
|
+
this.diagnostics.error("KS2020", `Unexpected token '${t.lexeme || t.kind}'`, t.line, t.col);
|
|
949
949
|
throw new ParseError();
|
|
950
950
|
}
|
|
951
951
|
// Distinguishes a lambda's parameter list from a parenthesized expression.
|
|
@@ -1049,7 +1049,7 @@ export class Parser {
|
|
|
1049
1049
|
const subParser = new Parser(subTokens, subDiagnostics);
|
|
1050
1050
|
const expr = subParser.parseExpression();
|
|
1051
1051
|
for (const d of subDiagnostics.diagnostics) {
|
|
1052
|
-
this.diagnostics.error(d.message, token.line, token.col);
|
|
1052
|
+
this.diagnostics.error("KS2021", d.message, token.line, token.col);
|
|
1053
1053
|
}
|
|
1054
1054
|
return expr;
|
|
1055
1055
|
}
|
|
@@ -1109,7 +1109,7 @@ export class Parser {
|
|
|
1109
1109
|
if (this.check(kind))
|
|
1110
1110
|
return this.advance();
|
|
1111
1111
|
const t = this.peek();
|
|
1112
|
-
this.diagnostics.error(message, t.line, t.col);
|
|
1112
|
+
this.diagnostics.error("KS2022", message, t.line, t.col);
|
|
1113
1113
|
throw new ParseError();
|
|
1114
1114
|
}
|
|
1115
1115
|
synchronize() {
|
|
@@ -222,7 +222,7 @@ export class TemplateCompiler {
|
|
|
222
222
|
const elementChildren = node.children.filter((c) => c.kind === "element");
|
|
223
223
|
const textChildren = node.children.filter((c) => c.kind === "text");
|
|
224
224
|
if (textChildren.length > 0 && elementChildren.length > 0) {
|
|
225
|
-
this.diagnostics.error(`<${node.tag}> cannot mix text and element children — Kopular's DOM bindings have no text-node API, so text can only be a whole element's content`, node.line, node.col);
|
|
225
|
+
this.diagnostics.error("KS5015", `<${node.tag}> cannot mix text and element children — Kopular's DOM bindings have no text-node API, so text can only be a whole element's content`, node.line, node.col);
|
|
226
226
|
}
|
|
227
227
|
else if (textChildren.length > 0) {
|
|
228
228
|
const parts = textChildren.flatMap((t) => t.parts).map((p) => (p.kind === "Expr" ? { kind: "Expr", expression: this.resolve(p.expression, localScope) } : p));
|
package/dist/template_lexer.js
CHANGED
|
@@ -130,7 +130,7 @@ export class TemplateLexer {
|
|
|
130
130
|
while (!this.isAtEnd() && this.peek() !== '"')
|
|
131
131
|
value += this.advance();
|
|
132
132
|
if (this.isAtEnd()) {
|
|
133
|
-
this.diagnostics.error("Unterminated attribute value", line, col);
|
|
133
|
+
this.diagnostics.error("KS5001", "Unterminated attribute value", line, col);
|
|
134
134
|
}
|
|
135
135
|
else {
|
|
136
136
|
this.advance(); // closing '"'
|
|
@@ -143,7 +143,7 @@ export class TemplateLexer {
|
|
|
143
143
|
while (!this.isAtEnd() && !/[\s=>/]/.test(this.peek()))
|
|
144
144
|
name += this.advance();
|
|
145
145
|
if (name.length === 0) {
|
|
146
|
-
this.diagnostics.error(`Unexpected character '${this.peek()}' in tag`, line, col);
|
|
146
|
+
this.diagnostics.error("KS5002", `Unexpected character '${this.peek()}' in tag`, line, col);
|
|
147
147
|
this.advance(); // avoid an infinite loop on a genuinely unexpected character
|
|
148
148
|
return this.nextTagToken();
|
|
149
149
|
}
|
package/dist/template_parser.js
CHANGED
|
@@ -44,7 +44,7 @@ export class TemplateParser {
|
|
|
44
44
|
const expr = new Parser(tokens, localDiagnostics).parseStandaloneExpression();
|
|
45
45
|
for (const d of localDiagnostics.diagnostics) {
|
|
46
46
|
const { line, col } = remapPosition(fragmentLine, fragmentCol, d.line, d.col);
|
|
47
|
-
this.diagnostics.error(d.message, line, col);
|
|
47
|
+
this.diagnostics.error("KS5003", d.message, line, col);
|
|
48
48
|
}
|
|
49
49
|
return expr;
|
|
50
50
|
}
|
|
@@ -57,7 +57,7 @@ export class TemplateParser {
|
|
|
57
57
|
const tokens = new Lexer(source, localDiagnostics).tokenize();
|
|
58
58
|
const ofIndex = tokens.findIndex((t) => t.kind === TokenKind.Identifier && t.lexeme === "of");
|
|
59
59
|
if (ofIndex < 1 || tokens[ofIndex - 1].kind !== TokenKind.Identifier) {
|
|
60
|
-
this.diagnostics.error(`Expected '*for="Type varName of iterable"', got '${source}'`, fragmentLine, fragmentCol);
|
|
60
|
+
this.diagnostics.error("KS5004", `Expected '*for="Type varName of iterable"', got '${source}'`, fragmentLine, fragmentCol);
|
|
61
61
|
return null;
|
|
62
62
|
}
|
|
63
63
|
const varNameToken = tokens[ofIndex - 1];
|
|
@@ -67,7 +67,7 @@ export class TemplateParser {
|
|
|
67
67
|
const iterable = new Parser(iterableTokens, localDiagnostics).parseStandaloneExpression();
|
|
68
68
|
for (const d of localDiagnostics.diagnostics) {
|
|
69
69
|
const { line, col } = remapPosition(fragmentLine, fragmentCol, d.line, d.col);
|
|
70
|
-
this.diagnostics.error(d.message, line, col);
|
|
70
|
+
this.diagnostics.error("KS5005", d.message, line, col);
|
|
71
71
|
}
|
|
72
72
|
return { varType, varName: varNameToken.lexeme, iterable, line: fragmentLine, col: fragmentCol };
|
|
73
73
|
}
|
|
@@ -104,7 +104,7 @@ export class TemplateParser {
|
|
|
104
104
|
const exprCol = col;
|
|
105
105
|
advancePos(text.slice(i, exprStartOffset));
|
|
106
106
|
if (end === -1) {
|
|
107
|
-
this.diagnostics.error("Unterminated '{{' interpolation", exprLine, exprCol);
|
|
107
|
+
this.diagnostics.error("KS5006", "Unterminated '{{' interpolation", exprLine, exprCol);
|
|
108
108
|
break;
|
|
109
109
|
}
|
|
110
110
|
const exprSource = text.slice(exprStartOffset, end);
|
|
@@ -139,7 +139,7 @@ export class TemplateParser {
|
|
|
139
139
|
const elementRoots = roots.filter((r) => r.kind === "element");
|
|
140
140
|
if (elementRoots.length !== 1) {
|
|
141
141
|
const at = roots[0] ?? { line: 1, col: 1 };
|
|
142
|
-
this.diagnostics.error(`A template must have exactly one top-level element, found ${elementRoots.length}`, at.line, at.col);
|
|
142
|
+
this.diagnostics.error("KS5007", `A template must have exactly one top-level element, found ${elementRoots.length}`, at.line, at.col);
|
|
143
143
|
return null;
|
|
144
144
|
}
|
|
145
145
|
return elementRoots[0];
|
|
@@ -153,7 +153,7 @@ export class TemplateParser {
|
|
|
153
153
|
return this.parseElement();
|
|
154
154
|
}
|
|
155
155
|
const t = this.advance();
|
|
156
|
-
this.diagnostics.error(`Unexpected token in template ('${t.lexeme}')`, t.line, t.col);
|
|
156
|
+
this.diagnostics.error("KS5008", `Unexpected token in template ('${t.lexeme}')`, t.line, t.col);
|
|
157
157
|
return null;
|
|
158
158
|
}
|
|
159
159
|
parseElement() {
|
|
@@ -183,7 +183,7 @@ export class TemplateParser {
|
|
|
183
183
|
}
|
|
184
184
|
else if (name === "*if") {
|
|
185
185
|
if (ifCondition || forBinding) {
|
|
186
|
-
this.diagnostics.error(`Only one structural directive ('*if'/'*for') is allowed per element`, nameTok.line, nameTok.col);
|
|
186
|
+
this.diagnostics.error("KS5009", `Only one structural directive ('*if'/'*for') is allowed per element`, nameTok.line, nameTok.col);
|
|
187
187
|
}
|
|
188
188
|
else {
|
|
189
189
|
ifCondition = this.parseEmbeddedExpression(valueTok.lexeme, valueTok.line, valueTok.col);
|
|
@@ -191,7 +191,7 @@ export class TemplateParser {
|
|
|
191
191
|
}
|
|
192
192
|
else if (name === "*for") {
|
|
193
193
|
if (ifCondition || forBinding) {
|
|
194
|
-
this.diagnostics.error(`Only one structural directive ('*if'/'*for') is allowed per element`, nameTok.line, nameTok.col);
|
|
194
|
+
this.diagnostics.error("KS5010", `Only one structural directive ('*if'/'*for') is allowed per element`, nameTok.line, nameTok.col);
|
|
195
195
|
}
|
|
196
196
|
else {
|
|
197
197
|
forBinding = this.parseForBinding(valueTok.lexeme, valueTok.line, valueTok.col);
|
|
@@ -219,11 +219,11 @@ export class TemplateParser {
|
|
|
219
219
|
if (this.check(TemplateTokenKind.TagClose)) {
|
|
220
220
|
const closeTok = this.advance();
|
|
221
221
|
if (closeTok.lexeme !== tag) {
|
|
222
|
-
this.diagnostics.error(`Mismatched closing tag: expected '</${tag}>', got '</${closeTok.lexeme}>'`, closeTok.line, closeTok.col);
|
|
222
|
+
this.diagnostics.error("KS5011", `Mismatched closing tag: expected '</${tag}>', got '</${closeTok.lexeme}>'`, closeTok.line, closeTok.col);
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
else {
|
|
226
|
-
this.diagnostics.error(`Expected closing tag '</${tag}>'`, tagTok.line, tagTok.col);
|
|
226
|
+
this.diagnostics.error("KS5012", `Expected closing tag '</${tag}>'`, tagTok.line, tagTok.col);
|
|
227
227
|
}
|
|
228
228
|
}
|
|
229
229
|
return { kind: "element", tag, staticAttrs, propBindings, eventBindings, ifCondition, forBinding, children, line: tagTok.line, col: tagTok.col };
|
|
@@ -233,7 +233,7 @@ export class TemplateParser {
|
|
|
233
233
|
}
|
|
234
234
|
consumeAttrValue(attrNameTok) {
|
|
235
235
|
if (!this.check(TemplateTokenKind.AttrValue)) {
|
|
236
|
-
this.diagnostics.error(`Expected a quoted value after '${attrNameTok.lexeme}='`, attrNameTok.line, attrNameTok.col);
|
|
236
|
+
this.diagnostics.error("KS5013", `Expected a quoted value after '${attrNameTok.lexeme}='`, attrNameTok.line, attrNameTok.col);
|
|
237
237
|
return null;
|
|
238
238
|
}
|
|
239
239
|
return this.advance();
|
|
@@ -243,7 +243,7 @@ export class TemplateParser {
|
|
|
243
243
|
this.advance();
|
|
244
244
|
}
|
|
245
245
|
else {
|
|
246
|
-
this.diagnostics.error(message, at.line, at.col);
|
|
246
|
+
this.diagnostics.error("KS5014", message, at.line, at.col);
|
|
247
247
|
}
|
|
248
248
|
}
|
|
249
249
|
}
|
package/package.json
CHANGED