rooklang 0.3.3 → 0.4.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/README.md +34 -7
- package/README.ru.md +34 -7
- package/dist/ast/declarations.d.ts +39 -4
- package/dist/ast/expressions.d.ts +34 -8
- package/dist/ast/markup.d.ts +6 -1
- package/dist/ast/statements.d.ts +28 -2
- package/dist/auto-import.d.ts +11 -0
- package/dist/auto-import.js +81 -0
- package/dist/breaks.d.ts +6 -0
- package/dist/breaks.js +36 -0
- package/dist/build.d.ts +7 -0
- package/dist/build.js +23 -2
- package/dist/checker/array-literals.d.ts +6 -0
- package/dist/checker/array-literals.js +59 -0
- package/dist/checker/assignments.d.ts +4 -0
- package/dist/checker/assignments.js +23 -3
- package/dist/checker/base.d.ts +8 -2
- package/dist/checker/base.js +7 -7
- package/dist/checker/builtins.js +22 -8
- package/dist/checker/bytes.d.ts +2 -2
- package/dist/checker/bytes.js +2 -2
- package/dist/checker/calls.js +3 -0
- package/dist/checker/channel-objects.d.ts +3 -0
- package/dist/checker/channel-objects.js +40 -0
- package/dist/checker/channels.d.ts +1 -0
- package/dist/checker/channels.js +17 -4
- package/dist/checker/checker.d.ts +1 -1
- package/dist/checker/checker.js +19 -0
- package/dist/checker/class-members.d.ts +22 -0
- package/dist/checker/class-members.js +105 -0
- package/dist/checker/classes.d.ts +5 -4
- package/dist/checker/classes.js +48 -13
- package/dist/checker/closing.d.ts +21 -0
- package/dist/checker/closing.js +164 -0
- package/dist/checker/completion.d.ts +3 -3
- package/dist/checker/completion.js +4 -3
- package/dist/checker/component-bindings.d.ts +7 -0
- package/dist/checker/component-bindings.js +45 -0
- package/dist/checker/component-values.js +10 -7
- package/dist/checker/components.d.ts +2 -2
- package/dist/checker/components.js +8 -3
- package/dist/checker/context.d.ts +41 -5
- package/dist/checker/context.js +23 -0
- package/dist/checker/control-flow.d.ts +11 -5
- package/dist/checker/control-flow.js +65 -40
- package/dist/checker/declarations.d.ts +2 -2
- package/dist/checker/declarations.js +4 -2
- package/dist/checker/decode.d.ts +8 -0
- package/dist/checker/decode.js +32 -0
- package/dist/checker/decorator-exports.js +4 -0
- package/dist/checker/decorator-includes.d.ts +9 -0
- package/dist/checker/decorator-includes.js +71 -0
- package/dist/checker/decorator-members.d.ts +8 -10
- package/dist/checker/decorator-members.js +28 -31
- package/dist/checker/decorator-wrappers.d.ts +1 -1
- package/dist/checker/decorator-wrappers.js +3 -2
- package/dist/checker/decorators.d.ts +14 -5
- package/dist/checker/decorators.js +52 -25
- package/dist/checker/expressions.d.ts +6 -2
- package/dist/checker/expressions.js +20 -40
- package/dist/checker/flow.d.ts +25 -3
- package/dist/checker/flow.js +56 -12
- package/dist/checker/functions.d.ts +2 -2
- package/dist/checker/functions.js +9 -5
- package/dist/checker/helpers.d.ts +5 -2
- package/dist/checker/helpers.js +16 -4
- package/dist/checker/imports.js +5 -2
- package/dist/checker/iterables.d.ts +3 -1
- package/dist/checker/iterables.js +13 -0
- package/dist/checker/iterators.d.ts +4 -1
- package/dist/checker/iterators.js +12 -4
- package/dist/checker/json.d.ts +5 -0
- package/dist/checker/json.js +92 -0
- package/dist/checker/loops.d.ts +2 -0
- package/dist/checker/loops.js +50 -5
- package/dist/checker/markup.js +14 -2
- package/dist/checker/members.js +10 -1
- package/dist/checker/operators.d.ts +2 -2
- package/dist/checker/operators.js +7 -5
- package/dist/checker/queues.d.ts +2 -2
- package/dist/checker/queues.js +2 -2
- package/dist/checker/resolution.js +5 -3
- package/dist/checker/shared-state.d.ts +11 -0
- package/dist/checker/shared-state.js +37 -0
- package/dist/checker/signals.d.ts +8 -0
- package/dist/checker/signals.js +48 -0
- package/dist/checker/statements.d.ts +5 -2
- package/dist/checker/statements.js +51 -22
- package/dist/checker/tagged-unions.d.ts +37 -0
- package/dist/checker/tagged-unions.js +132 -0
- package/dist/checker/topics.d.ts +6 -0
- package/dist/checker/topics.js +17 -0
- package/dist/checker/transfer.d.ts +3 -1
- package/dist/checker/transfer.js +25 -5
- package/dist/checker/try.d.ts +6 -0
- package/dist/checker/try.js +19 -0
- package/dist/checker/types/constructors.d.ts +1 -1
- package/dist/checker/types/constructors.js +2 -2
- package/dist/checker/types/generics.js +1 -1
- package/dist/checker/types/iterables.d.ts +13 -0
- package/dist/checker/types/iterables.js +52 -0
- package/dist/checker/types/model.d.ts +25 -0
- package/dist/checker/types/params.js +22 -3
- package/dist/checker/types/printing.js +4 -3
- package/dist/checker/types/relations.js +19 -25
- package/dist/checker/types/topic.d.ts +6 -0
- package/dist/checker/types/topic.js +23 -0
- package/dist/checker/types.d.ts +2 -0
- package/dist/checker/types.js +2 -0
- package/dist/checker/untyped-globals.d.ts +1 -0
- package/dist/checker/untyped-globals.js +3 -1
- package/dist/cli.js +18 -36
- package/dist/codegen/attributes.d.ts +4 -0
- package/dist/codegen/attributes.js +24 -2
- package/dist/codegen/calls.d.ts +23 -0
- package/dist/codegen/calls.js +88 -7
- package/dist/codegen/component-helpers.d.ts +6 -1
- package/dist/codegen/component-helpers.js +6 -1
- package/dist/codegen/component-props.d.ts +11 -2
- package/dist/codegen/component-props.js +50 -5
- package/dist/codegen/components.d.ts +6 -5
- package/dist/codegen/components.js +44 -21
- package/dist/codegen/control-flow.d.ts +2 -0
- package/dist/codegen/control-flow.js +23 -9
- package/dist/codegen/control-statements.d.ts +10 -2
- package/dist/codegen/control-statements.js +56 -19
- package/dist/codegen/creation.js +5 -2
- package/dist/codegen/decode.d.ts +14 -0
- package/dist/codegen/decode.js +93 -0
- package/dist/codegen/early-returns.d.ts +4 -3
- package/dist/codegen/early-returns.js +17 -5
- package/dist/codegen/emitter.d.ts +13 -41
- package/dist/codegen/emitter.js +27 -8
- package/dist/codegen/expressions.d.ts +9 -0
- package/dist/codegen/expressions.js +15 -26
- package/dist/codegen/functions.d.ts +11 -1
- package/dist/codegen/functions.js +31 -3
- package/dist/codegen/js.d.ts +2 -2
- package/dist/codegen/js.js +5 -2
- package/dist/codegen/live.d.ts +60 -0
- package/dist/codegen/live.js +29 -0
- package/dist/codegen/mutations.d.ts +7 -0
- package/dist/codegen/mutations.js +43 -2
- package/dist/codegen/options.d.ts +42 -0
- package/dist/codegen/options.js +1 -0
- package/dist/codegen/reactive.d.ts +4 -59
- package/dist/codegen/reactive.js +34 -72
- package/dist/codegen/runtime.d.ts +7 -3
- package/dist/codegen/runtime.js +33 -14
- package/dist/codegen/shared.d.ts +22 -0
- package/dist/codegen/shared.js +93 -0
- package/dist/codegen/simple-statements.d.ts +11 -5
- package/dist/codegen/simple-statements.js +40 -15
- package/dist/codegen/statements.d.ts +5 -0
- package/dist/codegen/statements.js +99 -4
- package/dist/codegen/try.d.ts +5 -0
- package/dist/codegen/try.js +40 -0
- package/dist/codegen/updates.d.ts +11 -3
- package/dist/codegen/updates.js +52 -4
- package/dist/codegen/web-components.d.ts +2 -2
- package/dist/codegen/web-components.js +2 -2
- package/dist/compile.d.ts +9 -0
- package/dist/compile.js +25 -8
- package/dist/completion.d.ts +4 -2
- package/dist/completion.js +10 -5
- package/dist/declarations.js +32 -6
- package/dist/decorator-copy.d.ts +1 -1
- package/dist/decorator-copy.js +6 -2
- package/dist/decorator-names.d.ts +14 -0
- package/dist/decorator-names.js +56 -0
- package/dist/decorators.d.ts +2 -3
- package/dist/decorators.js +36 -14
- package/dist/goroutines.d.ts +1 -1
- package/dist/goroutines.js +5 -4
- package/dist/index.d.ts +6 -2
- package/dist/index.js +14 -5
- package/dist/inline-packages.d.ts +2 -1
- package/dist/inline-packages.js +9 -4
- package/dist/lexer/base.d.ts +2 -0
- package/dist/lexer/base.js +6 -1
- package/dist/lexer/lexer.js +3 -0
- package/dist/lexer/literals.d.ts +5 -0
- package/dist/lexer/literals.js +51 -1
- package/dist/lexer/markup.js +3 -4
- package/dist/lexer/tables.js +2 -0
- package/dist/lexer/token.d.ts +4 -3
- package/dist/lexer/token.js +0 -2
- package/dist/loader.js +30 -8
- package/dist/names.d.ts +4 -0
- package/dist/names.js +29 -0
- package/dist/parser/base.d.ts +5 -0
- package/dist/parser/base.js +2 -1
- package/dist/parser/classes.d.ts +2 -0
- package/dist/parser/classes.js +88 -23
- package/dist/parser/control-flow.d.ts +8 -1
- package/dist/parser/control-flow.js +49 -26
- package/dist/parser/declarations.d.ts +5 -12
- package/dist/parser/declarations.js +14 -79
- package/dist/parser/decorators.d.ts +6 -2
- package/dist/parser/decorators.js +43 -19
- package/dist/parser/expressions.js +27 -3
- package/dist/parser/jumps.d.ts +11 -0
- package/dist/parser/jumps.js +77 -0
- package/dist/parser/literals.d.ts +9 -0
- package/dist/parser/literals.js +45 -6
- package/dist/parser/markup.js +1 -1
- package/dist/parser/params.d.ts +21 -0
- package/dist/parser/params.js +143 -0
- package/dist/parser/simple-statements.d.ts +2 -1
- package/dist/parser/simple-statements.js +38 -15
- package/dist/parser/statements.d.ts +5 -5
- package/dist/parser/statements.js +39 -35
- package/dist/parser/syntax.d.ts +11 -1
- package/dist/parser/syntax.js +16 -2
- package/dist/parser/types.js +8 -3
- package/dist/project-modules.d.ts +12 -0
- package/dist/project-modules.js +106 -0
- package/dist/run-file.d.ts +6 -0
- package/dist/run-file.js +111 -0
- package/dist/runtime/sources.js +13 -6
- package/dist/source-map.d.ts +17 -0
- package/dist/source-map.js +75 -0
- package/dist/ts-types.js +4 -1
- package/dist/typescript/convert-objects.d.ts +2 -1
- package/dist/typescript/convert-objects.js +18 -9
- package/dist/vite.js +12 -4
- package/dist/walk-types.js +1 -0
- package/dist/walk.d.ts +0 -2
- package/dist/walk.js +14 -31
- package/package.json +2 -2
- package/src/runtime/chan.js +15 -5
- package/src/runtime/channel.js +28 -0
- package/src/runtime/classes.js +32 -0
- package/src/runtime/core.js +10 -1
- package/src/runtime/decode.js +221 -0
- package/src/runtime/go.js +3 -0
- package/src/runtime/index.js +7 -0
- package/src/runtime/markup.js +27 -17
- package/src/runtime/select.js +14 -9
- package/src/runtime/shared.js +17 -0
- package/src/runtime/signals.js +59 -0
- package/src/runtime/threads.js +32 -5
- package/src/runtime/topic.js +118 -0
- package/src/runtime/try.js +45 -0
package/dist/build.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
-
import { dirname, join, relative, resolve, sep } from 'node:path';
|
|
2
|
+
import { basename, dirname, join, relative, resolve, sep } from 'node:path';
|
|
3
3
|
import { compile } from "./index.js";
|
|
4
|
+
import { sourceMapComment } from "./source-map.js";
|
|
4
5
|
import { SourceFile } from "./source.js";
|
|
5
6
|
/**
|
|
6
7
|
* Compiles `.rk` files and directories (recursively), together with every `.rk` file they
|
|
@@ -47,6 +48,8 @@ export function build(entries, options = {}) {
|
|
|
47
48
|
code: result.code,
|
|
48
49
|
declarations: result.declarations,
|
|
49
50
|
worker: result.worker,
|
|
51
|
+
map: result.map,
|
|
52
|
+
workerMap: result.workerMap,
|
|
50
53
|
});
|
|
51
54
|
}
|
|
52
55
|
for (const specifier of result.dependencies) {
|
|
@@ -62,15 +65,33 @@ export function build(entries, options = {}) {
|
|
|
62
65
|
return { outputs: [], errors, warnings };
|
|
63
66
|
// The output keeps the folder structure below the given directories and all the files.
|
|
64
67
|
const root = commonDirectory(queue, directories);
|
|
65
|
-
const outputs = compiled.map((
|
|
68
|
+
const outputs = compiled.map((each) => {
|
|
69
|
+
const { source, declarations: types } = each;
|
|
66
70
|
const target = options.outDir ? join(resolve(options.outDir), relative(root, source)) : source;
|
|
67
71
|
const base = target.replace(/\.rk$/, '');
|
|
72
|
+
const maps = [];
|
|
73
|
+
// The code ends with where its map is, and the map says where the source is, from there.
|
|
74
|
+
const mapped = (code, output, map) => {
|
|
75
|
+
if (options.sourceMap === false || map === undefined)
|
|
76
|
+
return code;
|
|
77
|
+
const sources = [relative(dirname(output), source).split(sep).join('/')];
|
|
78
|
+
maps.push({
|
|
79
|
+
output: `${output}.map`,
|
|
80
|
+
code: JSON.stringify({ ...map, file: basename(output), sources }),
|
|
81
|
+
});
|
|
82
|
+
return `${code}${sourceMapComment(`${basename(output)}.map`)}`;
|
|
83
|
+
};
|
|
84
|
+
const code = mapped(each.code, `${base}.js`, each.map);
|
|
85
|
+
const worker = each.worker === undefined
|
|
86
|
+
? undefined
|
|
87
|
+
: mapped(each.worker, `${base}.worker.js`, each.workerMap);
|
|
68
88
|
return {
|
|
69
89
|
source,
|
|
70
90
|
output: `${base}.js`,
|
|
71
91
|
code,
|
|
72
92
|
...(types === undefined ? {} : { declarations: { output: `${base}.d.ts`, code: types } }),
|
|
73
93
|
...(worker === undefined ? {} : { worker: { output: `${base}.worker.js`, code: worker } }),
|
|
94
|
+
...(maps.length > 0 ? { maps } : {}),
|
|
74
95
|
};
|
|
75
96
|
});
|
|
76
97
|
return { outputs, errors, warnings };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type * as ast from '../ast.ts';
|
|
2
|
+
import { ExpressionChecker } from './expressions.ts';
|
|
3
|
+
import { type Type } from './types.ts';
|
|
4
|
+
export declare abstract class ArrayLiteralChecker extends ExpressionChecker {
|
|
5
|
+
protected checkArrayLiteral(node: ast.ArrayLiteral, expected: Type | null): Type;
|
|
6
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ExpressionChecker } from "./expressions.js";
|
|
2
|
+
import { ANY, arrayOf, commonType, NEVER, nonNull, typeToString, UNKNOWN, } from "./types.js";
|
|
3
|
+
// Array literals: the type of their elements is the expected one, or the common type of all of
|
|
4
|
+
// them, `[1, null]` is a `[]?number`. A literal without spreads also keeps the types of its
|
|
5
|
+
// elements by position, so that a call infers `K` and `V` of a tuple `[K, V]` of a `.d.ts` from
|
|
6
|
+
// `["a", 1]`: `new Map([["a", 1]])` is a `Map<string, number>` (types/params.ts).
|
|
7
|
+
export class ArrayLiteralChecker extends ExpressionChecker {
|
|
8
|
+
checkArrayLiteral(node, expected) {
|
|
9
|
+
const context = expected ? nonNull(expected) : null;
|
|
10
|
+
const expectedElement = context?.kind === 'array' ? context.element : context?.kind === 'any' ? ANY : null;
|
|
11
|
+
let element = expectedElement ?? NEVER;
|
|
12
|
+
const positions = [];
|
|
13
|
+
for (const item of node.elements) {
|
|
14
|
+
let type;
|
|
15
|
+
if (item.kind === 'SpreadElement') {
|
|
16
|
+
const spread = this.checkValue(item.argument, expectedElement && arrayOf(expectedElement));
|
|
17
|
+
if (spread.kind === 'array')
|
|
18
|
+
type = spread.element;
|
|
19
|
+
else if (spread.kind === 'any' || spread.kind === 'unknown')
|
|
20
|
+
type = spread;
|
|
21
|
+
// JS spreads any iterable: a Set, a typed array, a `[]byte`.
|
|
22
|
+
else if (spread.kind === 'object' && spread.iterates)
|
|
23
|
+
type = spread.iterates;
|
|
24
|
+
else {
|
|
25
|
+
this.error(`cannot spread ${typeToString(spread)}: it is not an array or an iterable`, item.argument);
|
|
26
|
+
type = UNKNOWN;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
type = this.checkValue(item, expectedElement);
|
|
31
|
+
positions.push(type);
|
|
32
|
+
}
|
|
33
|
+
if (expectedElement) {
|
|
34
|
+
this.expectAssignable(type, expectedElement, item.kind === 'SpreadElement' ? item.argument : item);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
element = commonElement(element, type);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return positions.length === node.elements.length && positions.length > 0
|
|
41
|
+
? arrayOf(element, positions)
|
|
42
|
+
: arrayOf(element);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The common type of the elements so far and the next one; of arrays with positions, by position,
|
|
47
|
+
* so that `[["a", 1], ["b", "x"]]` gives a `V` of `number | string`.
|
|
48
|
+
*/
|
|
49
|
+
function commonElement(element, next) {
|
|
50
|
+
const common = commonType(element, next);
|
|
51
|
+
if (common.kind !== 'array' ||
|
|
52
|
+
element.kind !== 'array' ||
|
|
53
|
+
next.kind !== 'array' ||
|
|
54
|
+
element.positions?.length !== next.positions?.length ||
|
|
55
|
+
!element.positions) {
|
|
56
|
+
return common;
|
|
57
|
+
}
|
|
58
|
+
return arrayOf(common.element, element.positions.map((each, i) => commonType(each, next.positions[i])));
|
|
59
|
+
}
|
|
@@ -2,6 +2,8 @@ import type * as ast from '../ast.ts';
|
|
|
2
2
|
import { InitializerChecker } from './initializers.ts';
|
|
3
3
|
import { type Type } from './types.ts';
|
|
4
4
|
export declare abstract class AssignmentChecker extends InitializerChecker {
|
|
5
|
+
/** The name of the `derived` whose block is being checked: it computes, so it changes no state. */
|
|
6
|
+
protected derivedBlock: string | null;
|
|
5
7
|
/**
|
|
6
8
|
* `a, b = f()` or `return f()`: the call must return exactly `count` values. Returns their
|
|
7
9
|
* types.
|
|
@@ -10,6 +12,8 @@ export declare abstract class AssignmentChecker extends InitializerChecker {
|
|
|
10
12
|
protected checkAssignment(node: ast.AssignmentStatement): void;
|
|
11
13
|
/** The type a value assigned to `target` must have. */
|
|
12
14
|
protected checkTarget(target: ast.Expression): Type;
|
|
15
|
+
/** `this.id = id` in the constructor of the class that has the readonly field `id`. */
|
|
16
|
+
private initializes;
|
|
13
17
|
protected checkReturn(node: ast.ReturnStatement): void;
|
|
14
18
|
/**
|
|
15
19
|
* `return null, err` in a function of `(User, error)`: null takes the place of a value only
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { placeOf } from "./context.js";
|
|
1
2
|
import { guardedByError, unpackedResults } from "./error-results.js";
|
|
2
3
|
import { isUntyped, countValues, callName, isCall } from "./helpers.js";
|
|
3
4
|
import { InitializerChecker } from "./initializers.js";
|
|
@@ -5,6 +6,8 @@ import { BOOL, ANY, isAssignable, isNullable, nonNull, nullable, typeToString, U
|
|
|
5
6
|
// Where values go: assignments to variables, fields and elements, and `return`. Both unpack a call
|
|
6
7
|
// that returns several values.
|
|
7
8
|
export class AssignmentChecker extends InitializerChecker {
|
|
9
|
+
/** The name of the `derived` whose block is being checked: it computes, so it changes no state. */
|
|
10
|
+
derivedBlock = null;
|
|
8
11
|
/**
|
|
9
12
|
* `a, b = f()` or `return f()`: the call must return exactly `count` values. Returns their
|
|
10
13
|
* types.
|
|
@@ -66,13 +69,13 @@ export class AssignmentChecker extends InitializerChecker {
|
|
|
66
69
|
const binding = this.narrowableBinding(target);
|
|
67
70
|
const declared = binding?.type;
|
|
68
71
|
if (binding && declared && isNullable(declared) && !isNullable(valueTypes[i] ?? UNKNOWN)) {
|
|
69
|
-
this.flow.set(binding, nonNull(declared));
|
|
72
|
+
this.flow.set(placeOf(binding), nonNull(declared));
|
|
70
73
|
}
|
|
71
74
|
});
|
|
72
75
|
}
|
|
73
76
|
/** The type a value assigned to `target` must have. */
|
|
74
77
|
checkTarget(target) {
|
|
75
|
-
if (this.checkDecoratorWrite(target))
|
|
78
|
+
if (this.checkDecoratorWrite(target) || this.checkSharedWrite(target))
|
|
76
79
|
return UNKNOWN;
|
|
77
80
|
switch (target.kind) {
|
|
78
81
|
case 'Identifier': {
|
|
@@ -92,13 +95,16 @@ export class AssignmentChecker extends InitializerChecker {
|
|
|
92
95
|
else if (binding.kind === 'ref') {
|
|
93
96
|
this.error(`cannot assign to "${target.name}": a ref always points to its element`, target);
|
|
94
97
|
}
|
|
98
|
+
else if (binding.kind === 'state' && this.derivedBlock !== null) {
|
|
99
|
+
this.error(`derived "${this.derivedBlock}" cannot change the state "${target.name}": change it in an event handler or an effect`, target);
|
|
100
|
+
}
|
|
95
101
|
else if (binding.kind === 'loop') {
|
|
96
102
|
this.error(`cannot assign to loop variable "${target.name}"`, target);
|
|
97
103
|
}
|
|
98
104
|
else if (binding.kind === 'prop') {
|
|
99
105
|
this.error(this.decorator
|
|
100
106
|
? `cannot assign to "${target.name}": parameters of a decorator are read-only`
|
|
101
|
-
: `cannot assign to "${target.name}": component properties are read-only; to change
|
|
107
|
+
: `cannot assign to "${target.name}": component properties are read-only; to change it, declare it with bind, e.g. comp ${this.component?.node.name.name ?? 'Tabs'}(bind ${target.name} ...)`, target);
|
|
102
108
|
}
|
|
103
109
|
else if (binding.kind !== 'let' &&
|
|
104
110
|
binding.kind !== 'state' &&
|
|
@@ -119,6 +125,12 @@ export class AssignmentChecker extends InitializerChecker {
|
|
|
119
125
|
this.error(`cannot assign to method "${target.property.name}"`, target);
|
|
120
126
|
return UNKNOWN;
|
|
121
127
|
}
|
|
128
|
+
if (member.accessor === 'get') {
|
|
129
|
+
this.error(`cannot assign to "${target.property.name}": it has a getter and no setter`, target);
|
|
130
|
+
}
|
|
131
|
+
else if (member.readonly && !this.initializes(target, member)) {
|
|
132
|
+
this.error(`cannot assign to "${target.property.name}": it is readonly, so only the constructor of ${member.owner?.name ?? 'its class'} assigns it`, target);
|
|
133
|
+
}
|
|
122
134
|
return member.type;
|
|
123
135
|
}
|
|
124
136
|
case 'IndexExpression': {
|
|
@@ -141,6 +153,14 @@ export class AssignmentChecker extends InitializerChecker {
|
|
|
141
153
|
return UNKNOWN;
|
|
142
154
|
}
|
|
143
155
|
}
|
|
156
|
+
/** `this.id = id` in the constructor of the class that has the readonly field `id`. */
|
|
157
|
+
initializes(target, member) {
|
|
158
|
+
return (target.object.kind === 'ThisExpression' &&
|
|
159
|
+
this.fn?.isConstructor === true &&
|
|
160
|
+
this.cls !== null &&
|
|
161
|
+
!this.cls.isStatic &&
|
|
162
|
+
this.cls.info === member.owner);
|
|
163
|
+
}
|
|
144
164
|
checkReturn(node) {
|
|
145
165
|
const fn = this.fn;
|
|
146
166
|
if (!fn)
|
package/dist/checker/base.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type * as ast from '../ast.ts';
|
|
2
2
|
import type { Diagnostic } from '../diagnostics.ts';
|
|
3
|
-
import { type CheckOptions, type BindingKind, type Binding, type ComponentInfo, type DecoratorInfo, type AliasEntry, Scope, type FunctionContext, type ClassContext, type Flow } from './context.ts';
|
|
3
|
+
import { type CheckOptions, type BindingKind, type Binding, type ComponentInfo, type DecoratorInfo, type AliasEntry, Scope, type FunctionContext, type ClassContext, type Flow, type Place } from './context.ts';
|
|
4
4
|
import { type ClassInfo, type FunctionType, type Member, type Type, type TypeParam } from './types.ts';
|
|
5
5
|
export declare abstract class CheckerBase {
|
|
6
6
|
protected abstract checkClassBodies(node: ast.ClassDeclaration, info: ClassInfo): void;
|
|
@@ -18,6 +18,13 @@ export declare abstract class CheckerBase {
|
|
|
18
18
|
protected abstract checkTry(node: ast.TryStatement): void;
|
|
19
19
|
protected abstract forget(target: ast.Expression): void;
|
|
20
20
|
protected abstract narrowableBinding(node: ast.Expression): Binding | undefined;
|
|
21
|
+
/** A variable or a path of data fields that conditions narrow, with its type at this point. */
|
|
22
|
+
protected abstract narrowable(node: ast.Expression): {
|
|
23
|
+
place: Place;
|
|
24
|
+
type: Type;
|
|
25
|
+
} | undefined;
|
|
26
|
+
/** Whether an assignment to `field` of `place` may change the member of a union it is. */
|
|
27
|
+
protected abstract isTagOf(place: Place, field: string): boolean;
|
|
21
28
|
/** The error for a value that may be null where it must not be. */
|
|
22
29
|
protected abstract nullError(node: ast.Expression): void;
|
|
23
30
|
protected abstract checkFunction(params: readonly ast.Parameter[], type: FunctionType, results: Type[] | null, body: ast.BlockStatement | ast.Expression, options?: {
|
|
@@ -44,7 +51,6 @@ export declare abstract class CheckerBase {
|
|
|
44
51
|
protected abstract orderByNeeds(decorators: readonly DecoratorInfo[]): DecoratorInfo[];
|
|
45
52
|
protected abstract wrappedType(info: ComponentInfo, own: Type, seen: Set<ComponentInfo>): Type;
|
|
46
53
|
protected abstract checkDecoratorMember(node: ast.DecoratorMember): Type;
|
|
47
|
-
protected abstract checkDecoratorGet(node: ast.DecoratorGet): Type;
|
|
48
54
|
protected abstract checkDecoratorWrite(target: ast.Expression): boolean;
|
|
49
55
|
protected abstract checkComponentUse(node: ast.ElementExpression, tag: ast.Identifier): Type;
|
|
50
56
|
protected abstract checkRefDeclaration(node: ast.VariableDeclaration): void;
|
package/dist/checker/base.js
CHANGED
|
@@ -4,13 +4,13 @@ import { Scope, globalScope, } from "./context.js";
|
|
|
4
4
|
import { DOCUMENT_FRAGMENT, eventType, HTML_ELEMENT, NODE } from "./dom.js";
|
|
5
5
|
import { explainMismatch, isAssignable, LazyMap, memberNames, nonNull, property, typeToString, UNKNOWN, } from "./types.js";
|
|
6
6
|
// The type checker is a chain of layers, one per area, like the code generator: base → imports →
|
|
7
|
-
// declarations → classes → resolution → initializers → assignments → statements → control flow →
|
|
8
|
-
// functions → expressions → operators → markup → forwarding → components → component values →
|
|
9
|
-
// refs → styles → decorator wrappers → decorator exports → decorators → decorator members →
|
|
10
|
-
// members → calls → goroutines → channels → queues → bytes → loops → go for →
|
|
11
|
-
// completion → Checker (checker.ts). Statements, expressions and markup
|
|
12
|
-
// recursively, so methods of later layers that earlier ones call are declared
|
|
13
|
-
// This layer has the state of a check, diagnostics, scopes and names, and the DOM types.
|
|
7
|
+
// shared state → declarations → class members → classes → resolution → initializers → assignments → statements → control flow →
|
|
8
|
+
// tagged unions → functions → expressions → array literals → operators → markup → forwarding → component bindings → components → component values →
|
|
9
|
+
// refs → styles → decorator wrappers → decorator exports → decorators → decorator includes → decorator members →
|
|
10
|
+
// members → calls → goroutines → channels → signals → queues → topics → decode → try → bytes → loops → go for →
|
|
11
|
+
// iterators → closing → completion → Checker (checker.ts). Statements, expressions and markup
|
|
12
|
+
// check each other recursively, so methods of later layers that earlier ones call are declared
|
|
13
|
+
// here as abstract. This layer has the state of a check, diagnostics, scopes and names, and the DOM types.
|
|
14
14
|
export class CheckerBase {
|
|
15
15
|
program;
|
|
16
16
|
options;
|
package/dist/checker/builtins.js
CHANGED
|
@@ -3,10 +3,11 @@ import { BIGINT_OBJECT } from "./bigint.js";
|
|
|
3
3
|
import { DOM_TYPES, EVENT } from "./dom.js";
|
|
4
4
|
import { SVG_TYPES } from "./dom-svg.js";
|
|
5
5
|
import { chanOf } from "./channel.js";
|
|
6
|
+
import { CHANNEL_OBJECT, TOPIC_OBJECT } from "./channel-objects.js";
|
|
6
7
|
import { PROMISE } from "./promise.js";
|
|
7
8
|
import { BUFFERS } from "./transfer.js";
|
|
8
|
-
import { UNTYPED_GLOBALS } from "./untyped-globals.js";
|
|
9
|
-
import { ANY, arrayOf, BIGINT, BOOL, ERROR, ERROR_CLASS, func, method, NUMBER, nullable, property, STRING, } from "./types.js";
|
|
9
|
+
import { UNTYPED_CLASSES, UNTYPED_GLOBALS } from "./untyped-globals.js";
|
|
10
|
+
import { ANY, arrayOf, BIGINT, BOOL, ERROR, ERROR_CLASS, func, method, NUMBER, nullable, property, STRING, topicOf, union, } from "./types.js";
|
|
10
11
|
// Types of the JS standard library that Rook programs use most. Anything not listed here
|
|
11
12
|
// can still be reached through the untyped globals below.
|
|
12
13
|
function object(name, members, call) {
|
|
@@ -226,20 +227,32 @@ export const GLOBAL_VALUES = new Map([
|
|
|
226
227
|
['NaN', NUMBER],
|
|
227
228
|
['Infinity', NUMBER],
|
|
228
229
|
['Error', ERROR_CLASS.value],
|
|
229
|
-
// `error("message")` creates an Error, so the result is never null
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
230
|
+
// `error("message")` creates an Error, so the result is never null; `error("load user", err)`
|
|
231
|
+
// wraps an error that is not null.
|
|
232
|
+
['error', func([STRING, ERROR_CLASS.instance], [ERROR_CLASS.instance], { required: 1 })],
|
|
233
|
+
// `close(ch)`: the channel takes no more values (a `<-chan T` cannot be closed: channels.ts);
|
|
234
|
+
// `close(t)`: the topic stops listening and sending.
|
|
235
|
+
['close', func([union([chanOf(ANY), topicOf(ANY)])], [])],
|
|
233
236
|
// `transfer(buffer)`: the buffer moves to the thread it goes to (what it takes: channels.ts).
|
|
234
237
|
['transfer', func([MOVED], [MOVED], { typeParams: [MOVED] })],
|
|
235
238
|
// Channels of time, as in Go: the time once after `ms`, or every `ms` until `close(ticks)`.
|
|
236
239
|
['after', func([NUMBER], [chanOf(NUMBER, 'receive')])],
|
|
237
240
|
['tick', func([NUMBER], [chanOf(NUMBER)])],
|
|
241
|
+
// Signals of the process, until `close(stop)`; an AbortSignal as a channel, and a channel as one
|
|
242
|
+
// (what they take and give: signals.ts).
|
|
243
|
+
['signal', func([], [chanOf(STRING)], { rest: STRING })],
|
|
244
|
+
['done', func([ANY], [chanOf(ANY, 'receive')])],
|
|
245
|
+
['abortSignal', func([chanOf(ANY, 'receive')], [ANY])],
|
|
238
246
|
// `stream(ch)`: a ReadableStream of the values of a channel (its type: channels.ts).
|
|
239
247
|
['stream', func([chanOf(ANY, 'receive')], [ANY])],
|
|
240
248
|
// `cancel(task)` stops a goroutine: it takes the Promise of `go` (channels.ts).
|
|
241
249
|
['cancel', func([ANY], [])],
|
|
242
|
-
|
|
250
|
+
['Channel', CHANNEL_OBJECT],
|
|
251
|
+
['Topic', TOPIC_OBJECT],
|
|
252
|
+
...[...UNTYPED_GLOBALS, ...UNTYPED_CLASSES, ...BUFFERS].map((name) => [
|
|
253
|
+
name,
|
|
254
|
+
ANY,
|
|
255
|
+
]),
|
|
243
256
|
]);
|
|
244
257
|
/** Built-in types that a library from TypeScript's lib files replaces: untyped ones and the DOM. */
|
|
245
258
|
export const LIBRARY_TYPES = new Set([
|
|
@@ -250,6 +263,7 @@ export const LIBRARY_TYPES = new Set([
|
|
|
250
263
|
'Promise',
|
|
251
264
|
'URL',
|
|
252
265
|
'Event',
|
|
266
|
+
...UNTYPED_CLASSES,
|
|
253
267
|
...BUFFERS,
|
|
254
268
|
...[...DOM_TYPES.keys()].filter((name) => name !== 'Content'),
|
|
255
269
|
...SVG_TYPES.keys(),
|
|
@@ -268,7 +282,7 @@ export const GLOBAL_TYPES = new Map([
|
|
|
268
282
|
['any', ANY],
|
|
269
283
|
['error', ERROR],
|
|
270
284
|
['Error', ERROR_CLASS.instance],
|
|
271
|
-
...['Date', 'RegExp', 'Map', 'Set', 'URL', ...BUFFERS].map((name) => [name, ANY]),
|
|
285
|
+
...['Date', 'RegExp', 'Map', 'Set', 'URL', ...UNTYPED_CLASSES, ...BUFFERS].map((name) => [name, ANY]),
|
|
272
286
|
['Promise', PROMISE],
|
|
273
287
|
...DOM_TYPES,
|
|
274
288
|
...SVG_TYPES,
|
package/dist/checker/bytes.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type * as ast from '../ast.ts';
|
|
2
|
-
import {
|
|
2
|
+
import { TryChecker } from './try.ts';
|
|
3
3
|
import { type Type } from './types.ts';
|
|
4
|
-
export declare abstract class BytesChecker extends
|
|
4
|
+
export declare abstract class BytesChecker extends TryChecker {
|
|
5
5
|
protected checkArrayLiteral(node: ast.ArrayLiteral, expected: Type | null): Type;
|
|
6
6
|
private isElements;
|
|
7
7
|
/** `make([]float64, n)`: a typed array of `n` zeros. */
|
package/dist/checker/bytes.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TryChecker } from "./try.js";
|
|
2
2
|
import { isUntyped } from "./helpers.js";
|
|
3
3
|
import { holdsBigInts } from "../typed-arrays.js";
|
|
4
4
|
import { BIGINT, isBytes, nonNull, NUMBER, STRING, typedArrayOf, typeToString, UNKNOWN, } from "./types.js";
|
|
5
5
|
// `[]byte`, a Uint8Array, and the other typed arrays, `[]float64`: an array literal where one is
|
|
6
6
|
// expected, `let data []byte = [1, 2]`; and the conversions of Go between text and its UTF-8
|
|
7
7
|
// bytes, `[]byte("rook")` and `string(data)`.
|
|
8
|
-
export class BytesChecker extends
|
|
8
|
+
export class BytesChecker extends TryChecker {
|
|
9
9
|
checkArrayLiteral(node, expected) {
|
|
10
10
|
const typed = expected && nonNull(expected);
|
|
11
11
|
const name = typed && typedArrayOf(typed);
|
package/dist/checker/calls.js
CHANGED
|
@@ -59,6 +59,9 @@ export class CallChecker extends MemberChecker {
|
|
|
59
59
|
}
|
|
60
60
|
const { info } = callee;
|
|
61
61
|
this.ensureClassResolved(info);
|
|
62
|
+
if (info.abstract) {
|
|
63
|
+
this.error(`cannot create an instance of the abstract class "${info.name}": create one of a class that extends it`, node);
|
|
64
|
+
}
|
|
62
65
|
const { type, owner } = constructorOf(info);
|
|
63
66
|
if (owner && owner.ctorVisibility !== 'public') {
|
|
64
67
|
const current = this.cls?.info;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { chanOf } from "./channel.js";
|
|
2
|
+
import { PROMISE } from "./promise.js";
|
|
3
|
+
import { ANY, arrayOf, BOOL, ERROR, func, instantiate, method, nullable, NUMBER, property, STRING, topicOf, VOID, } from "./types.js";
|
|
4
|
+
// `Channel` and `Topic`: what `<-`, `close` and the rest compile to, by name, for code that needs
|
|
5
|
+
// them as functions. `Channel.EMPTY` and `Topic.EMPTY` are the zero values of their types: closed,
|
|
6
|
+
// and a `chan any` and a `topic any`, so that they go where a channel or topic of any values is
|
|
7
|
+
// expected (src/runtime/channel.js, src/runtime/topic.js).
|
|
8
|
+
const T = { kind: 'param', name: 'T' };
|
|
9
|
+
function generic(params, results) {
|
|
10
|
+
return method(func(params, results, { typeParams: [T] }));
|
|
11
|
+
}
|
|
12
|
+
function object(name, members) {
|
|
13
|
+
return { kind: 'object', name, members: new Map(Object.entries(members)), call: null };
|
|
14
|
+
}
|
|
15
|
+
export const CHANNEL_OBJECT = object('Channel', {
|
|
16
|
+
EMPTY: property(chanOf(ANY)),
|
|
17
|
+
make: generic([NUMBER, T], [chanOf(T)]),
|
|
18
|
+
send: generic([chanOf(T, 'send'), T], [instantiate(PROMISE, [VOID])]),
|
|
19
|
+
// The zero value of its type after close; null from Channel.EMPTY.
|
|
20
|
+
receive: generic([chanOf(T, 'receive')], [instantiate(PROMISE, [nullable(T)])]),
|
|
21
|
+
close: method(func([chanOf(ANY)], [])),
|
|
22
|
+
// An async iterator of the values, for `for await` of JS.
|
|
23
|
+
range: generic([chanOf(T, 'receive')], [ANY]),
|
|
24
|
+
select: method(func([arrayOf(ANY), BOOL], [instantiate(PROMISE, [arrayOf(ANY)])])),
|
|
25
|
+
try: method(func([chanOf(ANY), BOOL, ANY], [nullable(arrayOf(ANY))], { required: 2 })),
|
|
26
|
+
fromStream: method(func([ANY, NUMBER, ANY], [chanOf(ANY), chanOf(ERROR, 'receive')])),
|
|
27
|
+
stream: generic([chanOf(T, 'receive')], [ANY]),
|
|
28
|
+
after: method(func([NUMBER], [chanOf(NUMBER, 'receive')])),
|
|
29
|
+
tick: method(func([NUMBER], [chanOf(NUMBER)])),
|
|
30
|
+
signal: method(func([], [chanOf(STRING)], { rest: STRING })),
|
|
31
|
+
done: method(func([ANY], [chanOf(ANY, 'receive')])),
|
|
32
|
+
abortSignal: method(func([chanOf(ANY, 'receive')], [ANY])),
|
|
33
|
+
});
|
|
34
|
+
export const TOPIC_OBJECT = object('Topic', {
|
|
35
|
+
EMPTY: property(topicOf(ANY)),
|
|
36
|
+
make: generic([STRING, T], [topicOf(T)]),
|
|
37
|
+
publish: generic([topicOf(T), T], []),
|
|
38
|
+
listen: generic([topicOf(T)], [chanOf(T, 'receive')]),
|
|
39
|
+
close: method(func([topicOf(ANY)], [])),
|
|
40
|
+
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type * as ast from '../ast.ts';
|
|
2
2
|
import { GoroutineChecker } from './goroutines.ts';
|
|
3
3
|
import { type Type } from './types.ts';
|
|
4
|
+
export declare function typeName(type: Type): string | null;
|
|
4
5
|
export declare abstract class ChannelChecker extends GoroutineChecker {
|
|
5
6
|
protected checkChan(node: ast.ChanExpression): Type;
|
|
6
7
|
protected checkStreamChan(node: ast.StreamChanExpression): Type;
|
package/dist/checker/channels.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { cannotWait } from "../parser/syntax.js";
|
|
2
2
|
import { chanDirection, chanOf, elementOf, isChan } from "./channel.js";
|
|
3
3
|
import { GoroutineChecker } from "./goroutines.js";
|
|
4
4
|
import { isUntyped } from "./helpers.js";
|
|
5
5
|
import { isPromise } from "./promise.js";
|
|
6
6
|
import { BUFFERS } from "./transfer.js";
|
|
7
|
-
import { ANY, BOOL, ERROR, instantiate, isNullable, nonNull, NUMBER, typeToString, typedArrayOf, UNKNOWN, } from "./types.js";
|
|
7
|
+
import { ANY, BOOL, ERROR, instantiate, isNullable, isTopic, nonNull, NUMBER, topicElement, typeToString, typedArrayOf, UNKNOWN, } from "./types.js";
|
|
8
8
|
// Channels: `chan<number>(10)` makes one, `ch <- v` sends a value, `<-ch` receives one (in
|
|
9
9
|
// operators.ts), `for v in ch` receives them until it is closed (in control-flow.ts), and
|
|
10
10
|
// `select` waits for the first of several. `transfer(x)` moves a buffer to another thread.
|
|
11
11
|
// `chan(stream)` receives the chunks of a ReadableStream, and `stream(ch)` reads a channel.
|
|
12
12
|
/** What `transfer(x)` moves: a buffer, or a view whose buffer moves. */
|
|
13
13
|
const MOVABLE = new Set(BUFFERS);
|
|
14
|
-
function typeName(type) {
|
|
14
|
+
export function typeName(type) {
|
|
15
15
|
if (type.kind === 'class')
|
|
16
16
|
return type.info.name;
|
|
17
17
|
if (type.kind === 'object')
|
|
@@ -50,11 +50,20 @@ export class ChannelChecker extends GoroutineChecker {
|
|
|
50
50
|
this.expectAssignable(this.checkValue(node.size, NUMBER), NUMBER, node.size);
|
|
51
51
|
return { kind: 'tuple', types: [chanOf(element), chanOf(ERROR, 'receive')] };
|
|
52
52
|
}
|
|
53
|
+
// A send to a channel waits until the channel takes the value, and one to a topic waits for
|
|
54
|
+
// nobody, so only the first needs an async function.
|
|
53
55
|
checkSend(node) {
|
|
54
56
|
const nullChannel = this.inSelectCase;
|
|
55
57
|
this.inSelectCase = false;
|
|
56
58
|
const type = this.checkValue(node.channel);
|
|
57
59
|
const channel = nullChannel && isNullable(type) ? nonNull(type) : type;
|
|
60
|
+
if (isTopic(channel)) {
|
|
61
|
+
const element = topicElement(channel);
|
|
62
|
+
this.expectAssignable(this.checkValue(node.value, element), element, node.value);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (node.cannotWait)
|
|
66
|
+
this.error(cannotWait('<-', node.cannotWait.inDefer), node.cannotWait);
|
|
58
67
|
if (!isChan(channel)) {
|
|
59
68
|
if (!isUntyped(channel)) {
|
|
60
69
|
this.error(`"<-" sends to a channel, not to ${typeToString(channel)}`, node.channel);
|
|
@@ -105,7 +114,7 @@ export class ChannelChecker extends GoroutineChecker {
|
|
|
105
114
|
: ANY;
|
|
106
115
|
}
|
|
107
116
|
checkSelect(node) {
|
|
108
|
-
this.dropNarrowing(
|
|
117
|
+
this.dropNarrowing(node);
|
|
109
118
|
const entry = this.flow;
|
|
110
119
|
for (const { names, operation, body } of node.cases) {
|
|
111
120
|
this.flow = new Map(entry);
|
|
@@ -114,6 +123,10 @@ export class ChannelChecker extends GoroutineChecker {
|
|
|
114
123
|
this.inSelectCase = operation !== null;
|
|
115
124
|
if (operation?.kind === 'SendStatement') {
|
|
116
125
|
this.checkSend(operation);
|
|
126
|
+
const channel = this.checkedTypes.get(operation.channel);
|
|
127
|
+
if (channel && isTopic(isNullable(channel) ? nonNull(channel) : channel)) {
|
|
128
|
+
this.error('a send to a topic waits for nobody, so it is not a case of "select": send it before the select, e.g. "cart <- event"', operation.channel);
|
|
129
|
+
}
|
|
117
130
|
}
|
|
118
131
|
else if (operation) {
|
|
119
132
|
// `case v, ok = <-ch:` declares the variables of the case.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type * as ast from '../ast.ts';
|
|
2
2
|
import type { CheckOptions, CheckResult } from './context.ts';
|
|
3
|
-
export type { CheckOptions, CheckResult, Completion, CompletionContext, CompletionResult, ImportResult, Library, ModuleExports, } from './context.ts';
|
|
3
|
+
export type { CheckOptions, CheckResult, Completion, CompletionContext, CompletionResult, ImportResult, Library, ModuleExports, TextEdit, } from './context.ts';
|
|
4
4
|
export declare function check(program: ast.Program, options?: CheckOptions): CheckResult;
|
package/dist/checker/checker.js
CHANGED
|
@@ -6,6 +6,7 @@ class Checker extends CompletionChecker {
|
|
|
6
6
|
this.checkEndlessRecursion();
|
|
7
7
|
this.checkIterators();
|
|
8
8
|
this.checkHtmlTags();
|
|
9
|
+
this.checkClosing();
|
|
9
10
|
this.diagnostics.sort((a, b) => a.start - b.start);
|
|
10
11
|
this.warnings.sort((a, b) => a.start - b.start);
|
|
11
12
|
return {
|
|
@@ -13,9 +14,27 @@ class Checker extends CompletionChecker {
|
|
|
13
14
|
warnings: this.warnings,
|
|
14
15
|
...this.collectDeclarations(),
|
|
15
16
|
types: this.checkedTypes,
|
|
17
|
+
dataClasses: this.dataClasses(),
|
|
16
18
|
...(this.completion ? { completion: this.completion } : {}),
|
|
17
19
|
};
|
|
18
20
|
}
|
|
21
|
+
/** The names of the module that are data classes: its own, and those it imports. */
|
|
22
|
+
dataClasses() {
|
|
23
|
+
const names = new Set();
|
|
24
|
+
for (const statement of this.program.body) {
|
|
25
|
+
const declared = statement.kind === 'ClassDeclaration'
|
|
26
|
+
? [statement.name.name]
|
|
27
|
+
: statement.kind === 'ImportDeclaration'
|
|
28
|
+
? statement.namedImports.map((specifier) => specifier.local.name)
|
|
29
|
+
: [];
|
|
30
|
+
for (const name of declared) {
|
|
31
|
+
const type = this.lookupValue(name)?.type;
|
|
32
|
+
if (type?.kind === 'classValue' && type.info.data === true)
|
|
33
|
+
names.add(name);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return names;
|
|
37
|
+
}
|
|
19
38
|
}
|
|
20
39
|
export function check(program, options = {}) {
|
|
21
40
|
return new Checker(program, options).run();
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type * as ast from '../ast.ts';
|
|
2
|
+
import { DeclarationChecker } from './declarations.ts';
|
|
3
|
+
import { type ClassInfo, type FunctionType, type Member } from './types.ts';
|
|
4
|
+
export declare abstract class ClassMemberChecker extends DeclarationChecker {
|
|
5
|
+
/** The signatures of getters and setters: their members have the type of the value. */
|
|
6
|
+
protected readonly accessorSignatures: Map<ast.MethodDeclaration, FunctionType>;
|
|
7
|
+
protected declareMember(info: ClassInfo, isStatic: boolean, name: ast.Identifier, member: Member): void;
|
|
8
|
+
/** `get area() number` and `set area(v number)` make one member, of the type of the value. */
|
|
9
|
+
protected declareAccessor(info: ClassInfo, node: ast.MethodDeclaration, accessor: 'get' | 'set'): void;
|
|
10
|
+
/** `constructor(private name string)`: the parameters that are properties of the class too. */
|
|
11
|
+
protected declareParamProperties(node: ast.ConstructorDeclaration, info: ClassInfo, ctor: FunctionType): void;
|
|
12
|
+
/**
|
|
13
|
+
* Only an abstract class has abstract methods, and a class that is not abstract implements
|
|
14
|
+
* those of its bases.
|
|
15
|
+
*/
|
|
16
|
+
protected checkAbstract(node: ast.ClassDeclaration, info: ClassInfo): void;
|
|
17
|
+
/**
|
|
18
|
+
* In JS a field is set on each instance and so hides a getter or setter of the prototype: a
|
|
19
|
+
* field and an accessor of the same name in a class and its base would not work together.
|
|
20
|
+
*/
|
|
21
|
+
protected checkAccessorOverride(info: ClassInfo, name: ast.Identifier, isStatic: boolean): void;
|
|
22
|
+
}
|