rooklang 0.0.0-stage → 0.3.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/LICENSE +21 -0
- package/README.md +177 -2
- package/README.ru.md +178 -0
- package/dist/ast/declarations.d.ts +185 -0
- package/dist/ast/declarations.js +1 -0
- package/dist/ast/expressions.d.ts +201 -0
- package/dist/ast/expressions.js +1 -0
- package/dist/ast/markup.d.ts +141 -0
- package/dist/ast/markup.js +1 -0
- package/dist/ast/statements.d.ts +173 -0
- package/dist/ast/statements.js +1 -0
- package/dist/ast.d.ts +18 -0
- package/dist/ast.js +18 -0
- package/dist/attrs.d.ts +8 -0
- package/dist/attrs.js +23 -0
- package/dist/browser.d.ts +30 -0
- package/dist/browser.js +36 -0
- package/dist/build.d.ts +48 -0
- package/dist/build.js +112 -0
- package/dist/checker/assignments.d.ts +14 -0
- package/dist/checker/assignments.js +197 -0
- package/dist/checker/base.d.ts +124 -0
- package/dist/checker/base.js +180 -0
- package/dist/checker/bigint.d.ts +15 -0
- package/dist/checker/bigint.js +85 -0
- package/dist/checker/builtins.d.ts +9 -0
- package/dist/checker/builtins.js +307 -0
- package/dist/checker/bytes.d.ts +11 -0
- package/dist/checker/bytes.js +72 -0
- package/dist/checker/calls.d.ts +23 -0
- package/dist/checker/calls.js +210 -0
- package/dist/checker/channel.d.ts +14 -0
- package/dist/checker/channel.js +44 -0
- package/dist/checker/channels.d.ts +9 -0
- package/dist/checker/channels.js +95 -0
- package/dist/checker/checker.d.ts +4 -0
- package/dist/checker/checker.js +22 -0
- package/dist/checker/classes.d.ts +24 -0
- package/dist/checker/classes.js +223 -0
- package/dist/checker/completion-names.d.ts +11 -0
- package/dist/checker/completion-names.js +100 -0
- package/dist/checker/completion.d.ts +25 -0
- package/dist/checker/completion.js +222 -0
- package/dist/checker/component-values.d.ts +11 -0
- package/dist/checker/component-values.js +88 -0
- package/dist/checker/components.d.ts +37 -0
- package/dist/checker/components.js +287 -0
- package/dist/checker/context.d.ts +196 -0
- package/dist/checker/context.js +35 -0
- package/dist/checker/control-flow.d.ts +24 -0
- package/dist/checker/control-flow.js +237 -0
- package/dist/checker/declarations.d.ts +26 -0
- package/dist/checker/declarations.js +259 -0
- package/dist/checker/decorator-exports.d.ts +5 -0
- package/dist/checker/decorator-exports.js +40 -0
- package/dist/checker/decorator-members.d.ts +28 -0
- package/dist/checker/decorator-members.js +140 -0
- package/dist/checker/decorator-wrappers.d.ts +20 -0
- package/dist/checker/decorator-wrappers.js +132 -0
- package/dist/checker/decorators.d.ts +28 -0
- package/dist/checker/decorators.js +265 -0
- package/dist/checker/dom-svg.d.ts +7 -0
- package/dist/checker/dom-svg.js +19 -0
- package/dist/checker/dom.d.ts +22 -0
- package/dist/checker/dom.js +361 -0
- package/dist/checker/expressions.d.ts +32 -0
- package/dist/checker/expressions.js +244 -0
- package/dist/checker/flow.d.ts +18 -0
- package/dist/checker/flow.js +134 -0
- package/dist/checker/forwarding.d.ts +15 -0
- package/dist/checker/forwarding.js +78 -0
- package/dist/checker/functions.d.ts +24 -0
- package/dist/checker/functions.js +154 -0
- package/dist/checker/go-for.d.ts +12 -0
- package/dist/checker/go-for.js +93 -0
- package/dist/checker/goroutines.d.ts +19 -0
- package/dist/checker/goroutines.js +110 -0
- package/dist/checker/helpers.d.ts +35 -0
- package/dist/checker/helpers.js +162 -0
- package/dist/checker/imports.d.ts +7 -0
- package/dist/checker/imports.js +83 -0
- package/dist/checker/initializers.d.ts +22 -0
- package/dist/checker/initializers.js +104 -0
- package/dist/checker/iterables.d.ts +5 -0
- package/dist/checker/iterables.js +17 -0
- package/dist/checker/iterators.d.ts +13 -0
- package/dist/checker/iterators.js +95 -0
- package/dist/checker/loops.d.ts +10 -0
- package/dist/checker/loops.js +82 -0
- package/dist/checker/markup.d.ts +28 -0
- package/dist/checker/markup.js +220 -0
- package/dist/checker/members.d.ts +29 -0
- package/dist/checker/members.js +233 -0
- package/dist/checker/operators.d.ts +13 -0
- package/dist/checker/operators.js +177 -0
- package/dist/checker/promise.d.ts +17 -0
- package/dist/checker/promise.js +49 -0
- package/dist/checker/refs.d.ts +26 -0
- package/dist/checker/refs.js +136 -0
- package/dist/checker/resolution.d.ts +23 -0
- package/dist/checker/resolution.js +173 -0
- package/dist/checker/statements.d.ts +28 -0
- package/dist/checker/statements.js +212 -0
- package/dist/checker/styles.d.ts +14 -0
- package/dist/checker/styles.js +49 -0
- package/dist/checker/transfer.d.ts +5 -0
- package/dist/checker/transfer.js +83 -0
- package/dist/checker/types/bytes.d.ts +4 -0
- package/dist/checker/types/bytes.js +51 -0
- package/dist/checker/types/classes.d.ts +22 -0
- package/dist/checker/types/classes.js +93 -0
- package/dist/checker/types/constructors.d.ts +27 -0
- package/dist/checker/types/constructors.js +129 -0
- package/dist/checker/types/generics.d.ts +20 -0
- package/dist/checker/types/generics.js +75 -0
- package/dist/checker/types/lazy.d.ts +30 -0
- package/dist/checker/types/lazy.js +92 -0
- package/dist/checker/types/model.d.ts +149 -0
- package/dist/checker/types/model.js +15 -0
- package/dist/checker/types/params.d.ts +7 -0
- package/dist/checker/types/params.js +220 -0
- package/dist/checker/types/printing.d.ts +6 -0
- package/dist/checker/types/printing.js +109 -0
- package/dist/checker/types/relations.d.ts +16 -0
- package/dist/checker/types/relations.js +294 -0
- package/dist/checker/types.d.ts +9 -0
- package/dist/checker/types.js +10 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +193 -0
- package/dist/codegen/analysis.d.ts +18 -0
- package/dist/codegen/analysis.js +144 -0
- package/dist/codegen/attributes.d.ts +40 -0
- package/dist/codegen/attributes.js +227 -0
- package/dist/codegen/calls.d.ts +22 -0
- package/dist/codegen/calls.js +106 -0
- package/dist/codegen/chan-helpers.d.ts +2 -0
- package/dist/codegen/chan-helpers.js +253 -0
- package/dist/codegen/component-helpers.d.ts +33 -0
- package/dist/codegen/component-helpers.js +18 -0
- package/dist/codegen/component-props.d.ts +42 -0
- package/dist/codegen/component-props.js +173 -0
- package/dist/codegen/component-values.d.ts +27 -0
- package/dist/codegen/component-values.js +146 -0
- package/dist/codegen/components.d.ts +53 -0
- package/dist/codegen/components.js +268 -0
- package/dist/codegen/control-flow.d.ts +25 -0
- package/dist/codegen/control-flow.js +152 -0
- package/dist/codegen/control-statements.d.ts +28 -0
- package/dist/codegen/control-statements.js +226 -0
- package/dist/codegen/creation.d.ts +31 -0
- package/dist/codegen/creation.js +113 -0
- package/dist/codegen/early-returns.d.ts +59 -0
- package/dist/codegen/early-returns.js +244 -0
- package/dist/codegen/element-helpers.d.ts +12 -0
- package/dist/codegen/element-helpers.js +54 -0
- package/dist/codegen/elements.d.ts +71 -0
- package/dist/codegen/elements.js +293 -0
- package/dist/codegen/emitter.d.ts +135 -0
- package/dist/codegen/emitter.js +296 -0
- package/dist/codegen/expressions.d.ts +49 -0
- package/dist/codegen/expressions.js +262 -0
- package/dist/codegen/functions.d.ts +28 -0
- package/dist/codegen/functions.js +170 -0
- package/dist/codegen/go-helpers.d.ts +9 -0
- package/dist/codegen/go-helpers.js +307 -0
- package/dist/codegen/helpers.d.ts +10 -0
- package/dist/codegen/helpers.js +356 -0
- package/dist/codegen/imported.d.ts +38 -0
- package/dist/codegen/imported.js +71 -0
- package/dist/codegen/js.d.ts +20 -0
- package/dist/codegen/js.js +113 -0
- package/dist/codegen/mutations.d.ts +10 -0
- package/dist/codegen/mutations.js +102 -0
- package/dist/codegen/parallel.d.ts +7 -0
- package/dist/codegen/parallel.js +63 -0
- package/dist/codegen/reactive.d.ts +95 -0
- package/dist/codegen/reactive.js +280 -0
- package/dist/codegen/refs.d.ts +62 -0
- package/dist/codegen/refs.js +160 -0
- package/dist/codegen/select-helpers.d.ts +2 -0
- package/dist/codegen/select-helpers.js +135 -0
- package/dist/codegen/simple-statements.d.ts +42 -0
- package/dist/codegen/simple-statements.js +253 -0
- package/dist/codegen/statements.d.ts +22 -0
- package/dist/codegen/statements.js +142 -0
- package/dist/codegen/styles.d.ts +54 -0
- package/dist/codegen/styles.js +157 -0
- package/dist/codegen/syntax.d.ts +18 -0
- package/dist/codegen/syntax.js +70 -0
- package/dist/codegen/thread-helpers.d.ts +2 -0
- package/dist/codegen/thread-helpers.js +198 -0
- package/dist/codegen/updates.d.ts +51 -0
- package/dist/codegen/updates.js +201 -0
- package/dist/codegen/web-components.d.ts +20 -0
- package/dist/codegen/web-components.js +149 -0
- package/dist/compile-file.d.ts +11 -0
- package/dist/compile-file.js +24 -0
- package/dist/compile.d.ts +53 -0
- package/dist/compile.js +187 -0
- package/dist/completion.d.ts +10 -0
- package/dist/completion.js +134 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +44 -0
- package/dist/css/hash.d.ts +1 -0
- package/dist/css/hash.js +10 -0
- package/dist/css/parse.d.ts +42 -0
- package/dist/css/parse.js +138 -0
- package/dist/css/print.d.ts +9 -0
- package/dist/css/print.js +35 -0
- package/dist/css/selectors.d.ts +29 -0
- package/dist/css/selectors.js +168 -0
- package/dist/declarations.d.ts +3 -0
- package/dist/declarations.js +207 -0
- package/dist/decorator-copy.d.ts +18 -0
- package/dist/decorator-copy.js +158 -0
- package/dist/decorators.d.ts +61 -0
- package/dist/decorators.js +241 -0
- package/dist/diagnostics.d.ts +17 -0
- package/dist/diagnostics.js +22 -0
- package/dist/go-for.d.ts +26 -0
- package/dist/go-for.js +155 -0
- package/dist/goroutines.d.ts +37 -0
- package/dist/goroutines.js +253 -0
- package/dist/html-tag.d.ts +21 -0
- package/dist/html-tag.js +103 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +55 -0
- package/dist/inline-packages.d.ts +32 -0
- package/dist/inline-packages.js +129 -0
- package/dist/iterators.d.ts +10 -0
- package/dist/iterators.js +33 -0
- package/dist/lexer/base.d.ts +24 -0
- package/dist/lexer/base.js +87 -0
- package/dist/lexer/lexer.d.ts +8 -0
- package/dist/lexer/lexer.js +134 -0
- package/dist/lexer/literals.d.ts +18 -0
- package/dist/lexer/literals.js +203 -0
- package/dist/lexer/markup.d.ts +10 -0
- package/dist/lexer/markup.js +93 -0
- package/dist/lexer/style.d.ts +17 -0
- package/dist/lexer/style.js +97 -0
- package/dist/lexer/tables.d.ts +53 -0
- package/dist/lexer/tables.js +101 -0
- package/dist/lexer/token.d.ts +42 -0
- package/dist/lexer/token.js +108 -0
- package/dist/loader.d.ts +1 -0
- package/dist/loader.js +50 -0
- package/dist/methods.d.ts +5 -0
- package/dist/methods.js +81 -0
- package/dist/modules.d.ts +3 -0
- package/dist/modules.js +49 -0
- package/dist/names.d.ts +23 -0
- package/dist/names.js +267 -0
- package/dist/parser/base.d.ts +96 -0
- package/dist/parser/base.js +202 -0
- package/dist/parser/classes.d.ts +8 -0
- package/dist/parser/classes.js +164 -0
- package/dist/parser/control-flow.d.ts +29 -0
- package/dist/parser/control-flow.js +232 -0
- package/dist/parser/declarations.d.ts +27 -0
- package/dist/parser/declarations.js +238 -0
- package/dist/parser/decorators.d.ts +30 -0
- package/dist/parser/decorators.js +176 -0
- package/dist/parser/expressions.d.ts +15 -0
- package/dist/parser/expressions.js +255 -0
- package/dist/parser/literals.d.ts +27 -0
- package/dist/parser/literals.js +206 -0
- package/dist/parser/markup.d.ts +22 -0
- package/dist/parser/markup.js +193 -0
- package/dist/parser/parser.d.ts +14 -0
- package/dist/parser/parser.js +22 -0
- package/dist/parser/simple-statements.d.ts +22 -0
- package/dist/parser/simple-statements.js +156 -0
- package/dist/parser/statements.d.ts +37 -0
- package/dist/parser/statements.js +267 -0
- package/dist/parser/syntax.d.ts +51 -0
- package/dist/parser/syntax.js +173 -0
- package/dist/parser/types.d.ts +23 -0
- package/dist/parser/types.js +218 -0
- package/dist/recursion.d.ts +25 -0
- package/dist/recursion.js +170 -0
- package/dist/refs.d.ts +49 -0
- package/dist/refs.js +210 -0
- package/dist/run.d.ts +1 -0
- package/dist/run.js +21 -0
- package/dist/source.d.ts +15 -0
- package/dist/source.js +34 -0
- package/dist/style-modules.d.ts +17 -0
- package/dist/style-modules.js +49 -0
- package/dist/styles.d.ts +36 -0
- package/dist/styles.js +160 -0
- package/dist/svg.d.ts +8 -0
- package/dist/svg.js +77 -0
- package/dist/ts-types.d.ts +11 -0
- package/dist/ts-types.js +133 -0
- package/dist/typed-arrays.d.ts +8 -0
- package/dist/typed-arrays.js +43 -0
- package/dist/typescript/convert-base.d.ts +42 -0
- package/dist/typescript/convert-base.js +135 -0
- package/dist/typescript/convert-objects.d.ts +28 -0
- package/dist/typescript/convert-objects.js +245 -0
- package/dist/typescript/convert.d.ts +14 -0
- package/dist/typescript/convert.js +67 -0
- package/dist/typescript/importer.d.ts +37 -0
- package/dist/typescript/importer.js +152 -0
- package/dist/typescript/library.d.ts +23 -0
- package/dist/typescript/library.js +81 -0
- package/dist/vite.d.ts +11 -0
- package/dist/vite.js +86 -0
- package/dist/walk-types.d.ts +4 -0
- package/dist/walk-types.js +56 -0
- package/dist/walk.d.ts +5 -0
- package/dist/walk.js +315 -0
- package/package.json +104 -4
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { bigintBinary, bigintUnary } from "./bigint.js";
|
|
2
|
+
import { ExpressionChecker } from "./expressions.js";
|
|
3
|
+
import { withNarrowing } from "./flow.js";
|
|
4
|
+
import { isUntyped, isNumeric, instanceTypeOf } from "./helpers.js";
|
|
5
|
+
import { chanDirection, elementOf, isChan, received } from "./channel.js";
|
|
6
|
+
import { awaited } from "./promise.js";
|
|
7
|
+
import { ANY, BOOL, commonType, isAssignable, isComparable, isNullable, nonNull, nullable, NUMBER, STRING, typeToString, UNKNOWN, widenLiterals, } from "./types.js";
|
|
8
|
+
// Unary, binary and conditional operators.
|
|
9
|
+
export class OperatorChecker extends ExpressionChecker {
|
|
10
|
+
/** In the operation of a `select` case, a channel may be null: that case never goes on. */
|
|
11
|
+
inSelectCase = false;
|
|
12
|
+
checkUnary(node) {
|
|
13
|
+
// Only the channel of the case itself may be null, not one inside it.
|
|
14
|
+
const nullChannel = this.inSelectCase;
|
|
15
|
+
this.inSelectCase = false;
|
|
16
|
+
const type = this.checkValue(node.argument);
|
|
17
|
+
switch (node.operator) {
|
|
18
|
+
case 'typeof':
|
|
19
|
+
return STRING;
|
|
20
|
+
// What the Promise gives; the results of an async function are unpacked from it.
|
|
21
|
+
case 'await':
|
|
22
|
+
if (isChan(type)) {
|
|
23
|
+
this.error('"await" does not wait for a channel: receive from it, e.g. "<-ch"', node);
|
|
24
|
+
}
|
|
25
|
+
return awaited(type);
|
|
26
|
+
case '<-': {
|
|
27
|
+
const channel = nullChannel && isNullable(type) ? nonNull(type) : type;
|
|
28
|
+
if (isChan(channel)) {
|
|
29
|
+
if (chanDirection(channel) === 'send') {
|
|
30
|
+
this.error(`"<-" cannot receive from ${typeToString(channel)}: it only sends; receive from a "chan T" or a "<-chan T"`, node.argument);
|
|
31
|
+
}
|
|
32
|
+
return received(elementOf(channel));
|
|
33
|
+
}
|
|
34
|
+
if (!isUntyped(type)) {
|
|
35
|
+
this.error(`"<-" receives from a channel, not from ${typeToString(type)}`, node.argument);
|
|
36
|
+
}
|
|
37
|
+
return type.kind === 'any' ? ANY : UNKNOWN;
|
|
38
|
+
}
|
|
39
|
+
case '!':
|
|
40
|
+
this.expectBool(type, node.argument, '"!"');
|
|
41
|
+
return BOOL;
|
|
42
|
+
case '-':
|
|
43
|
+
case '+':
|
|
44
|
+
case '~': {
|
|
45
|
+
const bigint = bigintUnary(node.operator, type);
|
|
46
|
+
if (bigint) {
|
|
47
|
+
if (bigint.error)
|
|
48
|
+
this.error(bigint.error, node);
|
|
49
|
+
return bigint.type;
|
|
50
|
+
}
|
|
51
|
+
if (!isNumeric(type)) {
|
|
52
|
+
this.error(`"${node.operator}" needs a number, not ${typeToString(type)}`, node.argument);
|
|
53
|
+
}
|
|
54
|
+
return NUMBER;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
expectBool(given, node, operator) {
|
|
59
|
+
const type = widenLiterals(given);
|
|
60
|
+
if (type.kind === 'bool' || type.kind === 'any' || type.kind === 'unknown')
|
|
61
|
+
return;
|
|
62
|
+
const hint = isNullable(type) ? ': compare it with null, e.g. "x != null"' : '';
|
|
63
|
+
this.error(`${operator} needs bool, not ${typeToString(type)}${hint}`, node);
|
|
64
|
+
}
|
|
65
|
+
checkBinary(node, expected) {
|
|
66
|
+
const { operator } = node;
|
|
67
|
+
if (operator === '&&' || operator === '||') {
|
|
68
|
+
this.expectBool(this.checkValue(node.left, BOOL), node.left, `"${operator}"`);
|
|
69
|
+
// `x != null && x.ok`: the right side runs only when the left side allows it.
|
|
70
|
+
const saved = this.flow;
|
|
71
|
+
this.flow = withNarrowing(saved, this.narrow(node.left, operator === '&&'));
|
|
72
|
+
this.expectBool(this.checkValue(node.right, BOOL), node.right, `"${operator}"`);
|
|
73
|
+
this.flow = saved;
|
|
74
|
+
return BOOL;
|
|
75
|
+
}
|
|
76
|
+
if (operator === '??') {
|
|
77
|
+
const left = this.checkValue(node.left, expected && nullable(expected));
|
|
78
|
+
const right = this.checkValue(node.right, expected ?? nonNull(left));
|
|
79
|
+
return this.binaryResult(operator, left, right, node);
|
|
80
|
+
}
|
|
81
|
+
const left = this.checkValue(node.left);
|
|
82
|
+
const right = this.checkValue(node.right, operator === '==' || operator === '!=' ? left : null);
|
|
83
|
+
return this.binaryResult(operator, left, right, node);
|
|
84
|
+
}
|
|
85
|
+
/** The type of `left <operator> right`, also used for compound assignments like `+=`. */
|
|
86
|
+
binaryResult(operator, givenLeft, givenRight, node) {
|
|
87
|
+
// Literals behave as their base types, except where they are compared.
|
|
88
|
+
const keepLiterals = operator === '==' || operator === '!=' || operator === '??';
|
|
89
|
+
const left = keepLiterals ? givenLeft : widenLiterals(givenLeft);
|
|
90
|
+
const right = keepLiterals ? givenRight : widenLiterals(givenRight);
|
|
91
|
+
const untyped = isUntyped(left) || isUntyped(right);
|
|
92
|
+
const nullHint = isNullable(left) || isNullable(right) ? ': a value may be null, check it first' : '';
|
|
93
|
+
const bigint = bigintBinary(operator, left, right);
|
|
94
|
+
if (bigint) {
|
|
95
|
+
if (bigint.error)
|
|
96
|
+
this.error(bigint.error, node);
|
|
97
|
+
return bigint.type;
|
|
98
|
+
}
|
|
99
|
+
switch (operator) {
|
|
100
|
+
case '+':
|
|
101
|
+
if (left.kind === 'number' && right.kind === 'number')
|
|
102
|
+
return NUMBER;
|
|
103
|
+
if (left.kind === 'string' && right.kind === 'string')
|
|
104
|
+
return STRING;
|
|
105
|
+
if (untyped)
|
|
106
|
+
return left.kind === 'string' || right.kind === 'string' ? STRING : ANY;
|
|
107
|
+
this.error(`cannot add ${typeToString(left)} and ${typeToString(right)}` +
|
|
108
|
+
(nullHint ||
|
|
109
|
+
(left.kind === 'string' || right.kind === 'string'
|
|
110
|
+
? ': use a template string, e.g. `${a}${b}`'
|
|
111
|
+
: '')), node);
|
|
112
|
+
return UNKNOWN;
|
|
113
|
+
case '-':
|
|
114
|
+
case '*':
|
|
115
|
+
case '/':
|
|
116
|
+
case '%':
|
|
117
|
+
case '**':
|
|
118
|
+
case '<<':
|
|
119
|
+
case '>>':
|
|
120
|
+
case '>>>':
|
|
121
|
+
case '&':
|
|
122
|
+
case '|':
|
|
123
|
+
case '^':
|
|
124
|
+
if (!isNumeric(left) || !isNumeric(right)) {
|
|
125
|
+
this.error(`"${operator}" needs numbers, not ${typeToString(left)} and ${typeToString(right)}${nullHint}`, node);
|
|
126
|
+
}
|
|
127
|
+
return NUMBER;
|
|
128
|
+
case '<':
|
|
129
|
+
case '>':
|
|
130
|
+
case '<=':
|
|
131
|
+
case '>=': {
|
|
132
|
+
const ordered = untyped ||
|
|
133
|
+
(left.kind === 'number' && right.kind === 'number') ||
|
|
134
|
+
(left.kind === 'string' && right.kind === 'string');
|
|
135
|
+
if (!ordered) {
|
|
136
|
+
this.error(`cannot compare ${typeToString(left)} and ${typeToString(right)} with "${operator}"${nullHint}`, node);
|
|
137
|
+
}
|
|
138
|
+
return BOOL;
|
|
139
|
+
}
|
|
140
|
+
case '==':
|
|
141
|
+
case '!=':
|
|
142
|
+
if (!isComparable(left, right)) {
|
|
143
|
+
this.error(`cannot compare ${typeToString(left)} and ${typeToString(right)}`, node);
|
|
144
|
+
}
|
|
145
|
+
return BOOL;
|
|
146
|
+
case 'instanceof':
|
|
147
|
+
if (!instanceTypeOf(right) && !isUntyped(right)) {
|
|
148
|
+
this.error(`the right side of instanceof must be a class, not ${typeToString(right)}`, node);
|
|
149
|
+
}
|
|
150
|
+
return BOOL;
|
|
151
|
+
case '&&':
|
|
152
|
+
case '||':
|
|
153
|
+
this.expectBool(left, node, `"${operator}"`);
|
|
154
|
+
return BOOL;
|
|
155
|
+
case '??': {
|
|
156
|
+
if (isUntyped(left))
|
|
157
|
+
return left;
|
|
158
|
+
const base = nonNull(left);
|
|
159
|
+
if (isAssignable(right, base))
|
|
160
|
+
return isNullable(right) ? nullable(base) : base;
|
|
161
|
+
return commonType(base, right);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
checkConditional(node, expected) {
|
|
166
|
+
this.checkCondition(node.test);
|
|
167
|
+
const before = this.flow;
|
|
168
|
+
const whenTrue = this.narrow(node.test, true);
|
|
169
|
+
const whenFalse = this.narrow(node.test, false);
|
|
170
|
+
this.flow = withNarrowing(before, whenTrue);
|
|
171
|
+
const consequent = this.checkValue(node.consequent, expected);
|
|
172
|
+
this.flow = withNarrowing(before, whenFalse);
|
|
173
|
+
const alternate = this.checkValue(node.alternate, expected);
|
|
174
|
+
this.flow = before;
|
|
175
|
+
return commonType(consequent, alternate);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type ObjectType, type Type } from './types.ts';
|
|
2
|
+
/**
|
|
3
|
+
* `Promise<T>` where there are no TypeScript lib files (the playground): enough to wait for it
|
|
4
|
+
* and to pass it on. With the lib files, their `Promise` replaces it.
|
|
5
|
+
*/
|
|
6
|
+
export declare const PROMISE: ObjectType;
|
|
7
|
+
/** `Promise<T>` of the global `Promise` type, for the results of an async function. */
|
|
8
|
+
export declare function promiseOf(promise: Type | undefined, results: readonly Type[]): Type;
|
|
9
|
+
/** Whether a type is a `Promise<T>` (or a `PromiseLike<T>` from the lib files). */
|
|
10
|
+
export declare function isPromise(type: Type): type is ObjectType;
|
|
11
|
+
/** What `await` gives: the value of a Promise; a value that is not a Promise is itself. */
|
|
12
|
+
export declare function awaited(type: Type): Type;
|
|
13
|
+
/**
|
|
14
|
+
* The results the body of an async function returns, from the results of its type: none for
|
|
15
|
+
* `Promise<void>`, several for a Promise of a tuple.
|
|
16
|
+
*/
|
|
17
|
+
export declare function awaitedResults(results: readonly Type[]): Type[];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ANY, func, instantiate, method, VOID } from "./types.js";
|
|
2
|
+
// Promises: the type of what an async function returns, and what `await` gives.
|
|
3
|
+
/**
|
|
4
|
+
* `Promise<T>` where there are no TypeScript lib files (the playground): enough to wait for it
|
|
5
|
+
* and to pass it on. With the lib files, their `Promise` replaces it.
|
|
6
|
+
*/
|
|
7
|
+
export const PROMISE = {
|
|
8
|
+
kind: 'object',
|
|
9
|
+
name: 'Promise',
|
|
10
|
+
members: new Map([
|
|
11
|
+
['then', method(func([ANY, ANY], [ANY], { required: 1 }))],
|
|
12
|
+
['catch', method(func([ANY], [ANY]))],
|
|
13
|
+
['finally', method(func([ANY], [ANY]))],
|
|
14
|
+
]),
|
|
15
|
+
call: null,
|
|
16
|
+
typeParams: [{ kind: 'param', name: 'T' }],
|
|
17
|
+
};
|
|
18
|
+
/** `Promise<T>` of the global `Promise` type, for the results of an async function. */
|
|
19
|
+
export function promiseOf(promise, results) {
|
|
20
|
+
if (promise?.kind !== 'object' || promise.typeParams?.length !== 1)
|
|
21
|
+
return ANY;
|
|
22
|
+
const value = results.length === 0
|
|
23
|
+
? VOID
|
|
24
|
+
: results.length === 1
|
|
25
|
+
? results[0]
|
|
26
|
+
: { kind: 'tuple', types: [...results] };
|
|
27
|
+
return instantiate(promise, [value]);
|
|
28
|
+
}
|
|
29
|
+
/** Whether a type is a `Promise<T>` (or a `PromiseLike<T>` from the lib files). */
|
|
30
|
+
export function isPromise(type) {
|
|
31
|
+
return (type.kind === 'object' &&
|
|
32
|
+
(type.instanceOf?.template.name === 'Promise' ||
|
|
33
|
+
type.instanceOf?.template.name === 'PromiseLike'));
|
|
34
|
+
}
|
|
35
|
+
/** What `await` gives: the value of a Promise; a value that is not a Promise is itself. */
|
|
36
|
+
export function awaited(type) {
|
|
37
|
+
return isPromise(type) ? (type.instanceOf.args[0] ?? ANY) : type;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The results the body of an async function returns, from the results of its type: none for
|
|
41
|
+
* `Promise<void>`, several for a Promise of a tuple.
|
|
42
|
+
*/
|
|
43
|
+
export function awaitedResults(results) {
|
|
44
|
+
const [promise] = results;
|
|
45
|
+
if (promise === undefined)
|
|
46
|
+
return [];
|
|
47
|
+
const value = awaited(promise);
|
|
48
|
+
return value.kind === 'void' ? [] : value.kind === 'tuple' ? value.types : [value];
|
|
49
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type * as ast from '../ast.ts';
|
|
2
|
+
import { type Root } from '../refs.ts';
|
|
3
|
+
import { ComponentValueChecker } from './component-values.ts';
|
|
4
|
+
import type { ComponentInfo } from './context.ts';
|
|
5
|
+
export declare abstract class RefChecker extends ComponentValueChecker {
|
|
6
|
+
/** The refs of the body of the component or decorator being checked. */
|
|
7
|
+
private refs;
|
|
8
|
+
protected checkComponentBody(info: ComponentInfo): void;
|
|
9
|
+
/** `<Card ref={card} />` binds a ref, so no component has such a property. */
|
|
10
|
+
protected resolveProps(info: ComponentInfo): void;
|
|
11
|
+
/** Checks a body whose markup binds the refs it declares. */
|
|
12
|
+
protected withRefs<T>(body: ast.BlockStatement, check: () => T): T;
|
|
13
|
+
/** Bindings in markup that may be created more than once, and refs bound twice, are errors. */
|
|
14
|
+
private bindRefs;
|
|
15
|
+
/** `ref input` or `ref input HTMLElement`: without a type, it has that of its element. */
|
|
16
|
+
protected checkRefDeclaration(node: ast.VariableDeclaration): void;
|
|
17
|
+
/** `ref={input}`: the name of a ref, whose type the element must have. */
|
|
18
|
+
protected checkRefAttribute(attribute: ast.JsxAttribute, node: ast.ElementExpression): void;
|
|
19
|
+
/** The type of what an element or a use of a component creates, without checking it. */
|
|
20
|
+
private boundType;
|
|
21
|
+
/**
|
|
22
|
+
* The element at the top of what a component creates, which a ref to it creates in advance; or
|
|
23
|
+
* why it is not known.
|
|
24
|
+
*/
|
|
25
|
+
protected rootOf(info: ComponentInfo, seen?: Set<ComponentInfo>): Root;
|
|
26
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { componentRoot, isComponentUse, refBindings } from "../refs.js";
|
|
2
|
+
import { ComponentValueChecker } from "./component-values.js";
|
|
3
|
+
import { lookupIn } from "./helpers.js";
|
|
4
|
+
import { UNKNOWN } from "./types.js";
|
|
5
|
+
// Refs: `ref input` in the body of a component or a decorator, and `<input ref={input} />` in its
|
|
6
|
+
// markup, which gives the ref its element and its type. The element is created where the ref is
|
|
7
|
+
// declared, so the markup must create it exactly once.
|
|
8
|
+
const OUT_OF = (flow) => `move the element out of {${flow}}, or into a component that has a ref of its own`;
|
|
9
|
+
const IN_MARKUP = 'write the element in the markup the component returns';
|
|
10
|
+
const REPEATED = {
|
|
11
|
+
for: 'inside {for}: the element is created for every item, so a ref cannot point to it; put the element into a component, and each copy gets its own ref',
|
|
12
|
+
if: `inside {if}: the element is created again when the branch changes, so a ref cannot point to it; ${OUT_OF('if')}`,
|
|
13
|
+
switch: `inside {switch}: the element is created again when the case changes, so a ref cannot point to it; ${OUT_OF('switch')}`,
|
|
14
|
+
expression: `inside an expression, which may create the element more than once; ${IN_MARKUP}`,
|
|
15
|
+
function: `inside a function, which may create the element more than once; ${IN_MARKUP}`,
|
|
16
|
+
block: `inside a block of the body, which may create the element more than once; ${IN_MARKUP}`,
|
|
17
|
+
};
|
|
18
|
+
export class RefChecker extends ComponentValueChecker {
|
|
19
|
+
/** The refs of the body of the component or decorator being checked. */
|
|
20
|
+
refs = null;
|
|
21
|
+
checkComponentBody(info) {
|
|
22
|
+
this.withRefs(info.node.body, () => super.checkComponentBody(info));
|
|
23
|
+
// Other modules find what <Card ref={card} /> creates here: the tags are names of this module.
|
|
24
|
+
info.root = this.rootOf(info);
|
|
25
|
+
}
|
|
26
|
+
/** `<Card ref={card} />` binds a ref, so no component has such a property. */
|
|
27
|
+
resolveProps(info) {
|
|
28
|
+
super.resolveProps(info);
|
|
29
|
+
const param = info.node.params.find((each) => each.name.name === 'ref');
|
|
30
|
+
if (param) {
|
|
31
|
+
this.error('a component cannot have the property "ref": ref={...} binds a ref', param.name);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** Checks a body whose markup binds the refs it declares. */
|
|
35
|
+
withRefs(body, check) {
|
|
36
|
+
const saved = this.refs;
|
|
37
|
+
this.refs = this.bindRefs(body);
|
|
38
|
+
try {
|
|
39
|
+
return check();
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
this.refs = saved;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** Bindings in markup that may be created more than once, and refs bound twice, are errors. */
|
|
46
|
+
bindRefs(body) {
|
|
47
|
+
const declared = new Set();
|
|
48
|
+
for (const statement of body.body) {
|
|
49
|
+
if (statement.kind !== 'VariableDeclaration' || statement.keyword !== 'ref')
|
|
50
|
+
continue;
|
|
51
|
+
for (const name of statement.names)
|
|
52
|
+
declared.add(name.name);
|
|
53
|
+
}
|
|
54
|
+
const bound = { targets: new Map(), mentioned: new Set() };
|
|
55
|
+
for (const { attribute, element, repeated } of refBindings(body)) {
|
|
56
|
+
const { value } = attribute;
|
|
57
|
+
// Other names are not refs of this body: checkRefAttribute reports them.
|
|
58
|
+
if (value?.kind !== 'Identifier' || !declared.has(value.name))
|
|
59
|
+
continue;
|
|
60
|
+
const { name } = value;
|
|
61
|
+
bound.mentioned.add(name);
|
|
62
|
+
if (repeated !== null) {
|
|
63
|
+
this.error(`ref={${name}} is ${REPEATED[repeated]}`, attribute);
|
|
64
|
+
}
|
|
65
|
+
else if (bound.targets.has(name)) {
|
|
66
|
+
this.error(`ref "${name}" is already bound to another element; a ref points to one element`, attribute);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
bound.targets.set(name, element);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return bound;
|
|
73
|
+
}
|
|
74
|
+
/** `ref input` or `ref input HTMLElement`: without a type, it has that of its element. */
|
|
75
|
+
checkRefDeclaration(node) {
|
|
76
|
+
const declared = node.type ? this.resolveType(node.type) : null;
|
|
77
|
+
for (const name of node.names) {
|
|
78
|
+
const target = this.refs?.targets.get(name.name);
|
|
79
|
+
if (!target && !this.refs?.mentioned.has(name.name)) {
|
|
80
|
+
this.error(`ref "${name.name}" is not bound to an element; add ref={${name.name}} to one, e.g. <div ref={${name.name}}>`, name);
|
|
81
|
+
}
|
|
82
|
+
this.declareValue(name, 'ref', declared ?? (target ? this.boundType(target) : UNKNOWN));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/** `ref={input}`: the name of a ref, whose type the element must have. */
|
|
86
|
+
checkRefAttribute(attribute, node) {
|
|
87
|
+
const { value } = attribute;
|
|
88
|
+
if (value?.kind !== 'Identifier') {
|
|
89
|
+
if (value && value.kind !== 'StringLiteral' && value.kind !== 'EventHandler') {
|
|
90
|
+
this.checkValue(value);
|
|
91
|
+
}
|
|
92
|
+
this.error('ref={...} takes the name of a ref, e.g. ref={input}', attribute);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const binding = this.lookupValue(value.name);
|
|
96
|
+
if (binding?.kind !== 'ref') {
|
|
97
|
+
if (binding)
|
|
98
|
+
this.error(`"${value.name}" is not a ref; declare it with ref ${value.name}`, value);
|
|
99
|
+
else
|
|
100
|
+
this.checkValue(value);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const type = binding.type ?? UNKNOWN;
|
|
104
|
+
this.checkedTypes.set(value, type);
|
|
105
|
+
this.expectAssignable(this.boundType(node), type, value, ` for ref "${value.name}"`);
|
|
106
|
+
if (!isComponentUse(node))
|
|
107
|
+
return;
|
|
108
|
+
const tag = node.tag.name;
|
|
109
|
+
const info = lookupIn(this.moduleScope, tag)?.component;
|
|
110
|
+
const root = info ? this.rootOf(info) : null;
|
|
111
|
+
if (typeof root === 'string')
|
|
112
|
+
this.error(`ref cannot point to <${tag}>: it ${root}`, attribute);
|
|
113
|
+
}
|
|
114
|
+
/** The type of what an element or a use of a component creates, without checking it. */
|
|
115
|
+
boundType(node) {
|
|
116
|
+
if (node.tag === null)
|
|
117
|
+
return this.dom.fragment;
|
|
118
|
+
if (!isComponentUse(node))
|
|
119
|
+
return this.elementOf(node.tag.name);
|
|
120
|
+
const info = lookupIn(this.moduleScope, node.tag.name)?.component;
|
|
121
|
+
return info ? this.componentResult(info, new Set()) : UNKNOWN;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* The element at the top of what a component creates, which a ref to it creates in advance; or
|
|
125
|
+
* why it is not known.
|
|
126
|
+
*/
|
|
127
|
+
rootOf(info, seen = new Set()) {
|
|
128
|
+
if (info.root)
|
|
129
|
+
return info.root;
|
|
130
|
+
seen.add(info);
|
|
131
|
+
return componentRoot(info.expanded, (tag) => {
|
|
132
|
+
const other = lookupIn(this.moduleScope, tag)?.component;
|
|
133
|
+
return other && !seen.has(other) ? this.rootOf(other, seen) : undefined;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type * as ast from '../ast.ts';
|
|
2
|
+
import { ClassChecker } from './classes.ts';
|
|
3
|
+
import type { AliasEntry } from './context.ts';
|
|
4
|
+
import { type Type } from './types.ts';
|
|
5
|
+
export declare abstract class TypeResolver extends ClassChecker {
|
|
6
|
+
protected resolveType(node: ast.TypeNode): Type;
|
|
7
|
+
/** `comp(title string, children Content)`: properties of type `?T` and `children` may be left out. */
|
|
8
|
+
private resolveComponentType;
|
|
9
|
+
/**
|
|
10
|
+
* The typed array of `[]T` when `T` is `byte`, `float64` or another element of one, unless the
|
|
11
|
+
* program declares a type of that name: `Float64Array`.
|
|
12
|
+
*/
|
|
13
|
+
private typedArrayClass;
|
|
14
|
+
/** A typed array of the lib files, with its default type argument, or the built-in one. */
|
|
15
|
+
protected typedArray(name: string): Type;
|
|
16
|
+
/** `[]byte`, a Uint8Array. */
|
|
17
|
+
protected bytes(): Type;
|
|
18
|
+
protected resolveTypeName(name: ast.Identifier, typeArgs?: readonly ast.TypeNode[]): Type;
|
|
19
|
+
protected resolveAlias(entry: AliasEntry): Type;
|
|
20
|
+
protected resolveAliasType(entry: AliasEntry): Type;
|
|
21
|
+
/** The type of a variable initialized with a value of type `type`. */
|
|
22
|
+
protected inferredType(type: Type, node: ast.Expression): Type;
|
|
23
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { holdsBigInts, TYPED_ELEMENTS } from "../typed-arrays.js";
|
|
2
|
+
import { chanOf } from "./channel.js";
|
|
3
|
+
import { ClassChecker } from "./classes.js";
|
|
4
|
+
import { CONTENT } from "./dom.js";
|
|
5
|
+
import { ANY, arrayOf, BIGINT, bindParams, builtInTypedArray, classInstance, func, instantiate, isAssignable, isNullable, literal, nullable, NUMBER, substitute, typeToString, union, UNKNOWN, } from "./types.js";
|
|
6
|
+
// Type annotations as types: names, aliases, generics with their arguments; and the type a variable gets from its value.
|
|
7
|
+
export class TypeResolver extends ClassChecker {
|
|
8
|
+
resolveType(node) {
|
|
9
|
+
switch (node.kind) {
|
|
10
|
+
case 'TypeReference':
|
|
11
|
+
return this.resolveTypeName(node.name, node.typeArgs);
|
|
12
|
+
case 'ArrayType': {
|
|
13
|
+
const typed = this.typedArrayClass(node.element);
|
|
14
|
+
return typed === null ? arrayOf(this.resolveType(node.element)) : this.typedArray(typed);
|
|
15
|
+
}
|
|
16
|
+
case 'ChanType':
|
|
17
|
+
return chanOf(this.resolveType(node.element), node.direction);
|
|
18
|
+
case 'NullableType':
|
|
19
|
+
return nullable(this.resolveType(node.type));
|
|
20
|
+
case 'FuncType':
|
|
21
|
+
return func(node.params.map((param) => this.resolveType(param)), node.results.map((result) => this.resolveType(result)), { iterator: node.iterator === true });
|
|
22
|
+
case 'ComponentType':
|
|
23
|
+
return this.resolveComponentType(node);
|
|
24
|
+
case 'ObjectType': {
|
|
25
|
+
const object = { kind: 'object', name: null, members: new Map(), call: null };
|
|
26
|
+
this.fillMembers(object, node.members);
|
|
27
|
+
return object;
|
|
28
|
+
}
|
|
29
|
+
case 'UnionType':
|
|
30
|
+
return union(node.types.map((type) => this.resolveType(type)));
|
|
31
|
+
case 'LiteralType':
|
|
32
|
+
return literal(node.value.value);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** `comp(title string, children Content)`: properties of type `?T` and `children` may be left out. */
|
|
36
|
+
resolveComponentType(node) {
|
|
37
|
+
const params = [];
|
|
38
|
+
for (const param of node.params) {
|
|
39
|
+
const name = param.name.name;
|
|
40
|
+
const type = param.type ? this.resolveType(param.type) : UNKNOWN;
|
|
41
|
+
if (name === 'children' && type !== CONTENT && type.kind !== 'unknown') {
|
|
42
|
+
this.error('the "children" property has the type Content', param.type ?? param);
|
|
43
|
+
}
|
|
44
|
+
if (params.some((other) => other.name === name)) {
|
|
45
|
+
this.error(`duplicate property "${name}"`, param.name);
|
|
46
|
+
}
|
|
47
|
+
params.push({ name, type, optional: isNullable(type) || name === 'children' });
|
|
48
|
+
}
|
|
49
|
+
return { kind: 'component', params };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The typed array of `[]T` when `T` is `byte`, `float64` or another element of one, unless the
|
|
53
|
+
* program declares a type of that name: `Float64Array`.
|
|
54
|
+
*/
|
|
55
|
+
typedArrayClass(node) {
|
|
56
|
+
if (node.kind !== 'TypeReference' || node.typeArgs.length > 0)
|
|
57
|
+
return null;
|
|
58
|
+
const typed = TYPED_ELEMENTS.get(node.name.name);
|
|
59
|
+
if (typed === undefined)
|
|
60
|
+
return null;
|
|
61
|
+
return this.lookupType(node.name.name) === (holdsBigInts(typed) ? BIGINT : NUMBER)
|
|
62
|
+
? typed
|
|
63
|
+
: null;
|
|
64
|
+
}
|
|
65
|
+
/** A typed array of the lib files, with its default type argument, or the built-in one. */
|
|
66
|
+
typedArray(name) {
|
|
67
|
+
const library = this.options.library?.type(name);
|
|
68
|
+
if (library?.kind === 'object' && library.typeParams) {
|
|
69
|
+
return instantiate(library, library.typeParams.map((param) => param.default ?? ANY));
|
|
70
|
+
}
|
|
71
|
+
return library ?? builtInTypedArray(name);
|
|
72
|
+
}
|
|
73
|
+
/** `[]byte`, a Uint8Array. */
|
|
74
|
+
bytes() {
|
|
75
|
+
return this.typedArray('Uint8Array');
|
|
76
|
+
}
|
|
77
|
+
resolveTypeName(name, typeArgs = []) {
|
|
78
|
+
const entry = this.lookupType(name.name);
|
|
79
|
+
if (entry === undefined) {
|
|
80
|
+
this.error(this.lookupValue(name.name)
|
|
81
|
+
? `"${name.name}" is a value, not a type`
|
|
82
|
+
: `unknown type "${name.name}"`, name);
|
|
83
|
+
return UNKNOWN;
|
|
84
|
+
}
|
|
85
|
+
const args = typeArgs.map((arg) => this.resolveType(arg));
|
|
86
|
+
const type = entry.kind === 'alias' ? this.resolveAlias(entry) : entry;
|
|
87
|
+
const params = entry.kind === 'alias'
|
|
88
|
+
? entry.params
|
|
89
|
+
: type.kind === 'object'
|
|
90
|
+
? (type.typeParams ?? [])
|
|
91
|
+
: type.kind === 'class'
|
|
92
|
+
? type.info.typeParams
|
|
93
|
+
: [];
|
|
94
|
+
if (params.length === 0) {
|
|
95
|
+
if (args.length > 0)
|
|
96
|
+
this.error(`"${name.name}" is not generic`, typeArgs[0]);
|
|
97
|
+
return type;
|
|
98
|
+
}
|
|
99
|
+
// Parameters with defaults (from a `.d.ts`) may be left out: `Buffer` is `Buffer<ArrayBufferLike>`.
|
|
100
|
+
const required = params.filter((param) => !param.default).length;
|
|
101
|
+
if (args.length >= required && args.length < params.length) {
|
|
102
|
+
args.push(...params.slice(args.length).map((param) => param.default));
|
|
103
|
+
}
|
|
104
|
+
if (args.length !== params.length) {
|
|
105
|
+
this.error(args.length === 0
|
|
106
|
+
? `"${name.name}" needs type arguments: ${name.name}<${params.map((param) => param.name).join(', ')}>`
|
|
107
|
+
: `"${name.name}" takes ${params.length} type argument${params.length === 1 ? '' : 's'}, got ${args.length}`, name);
|
|
108
|
+
return UNKNOWN;
|
|
109
|
+
}
|
|
110
|
+
params.forEach((param, i) => {
|
|
111
|
+
if (param.constraint && !isAssignable(args[i], param.constraint)) {
|
|
112
|
+
this.error(`${typeToString(args[i])} does not satisfy the constraint ${typeToString(param.constraint)} of ${param.name}`, typeArgs[i]);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
if (type.kind === 'class')
|
|
116
|
+
return classInstance(type.info, args);
|
|
117
|
+
if (type.kind === 'object' && type.typeParams)
|
|
118
|
+
return instantiate(type, args);
|
|
119
|
+
return substitute(type, bindParams(params, args));
|
|
120
|
+
}
|
|
121
|
+
resolveAlias(entry) {
|
|
122
|
+
if (entry.resolved)
|
|
123
|
+
return entry.resolved;
|
|
124
|
+
const { node } = entry;
|
|
125
|
+
if (entry.resolving) {
|
|
126
|
+
this.error(`type "${node.name.name}" refers to itself`, node.name);
|
|
127
|
+
return UNKNOWN;
|
|
128
|
+
}
|
|
129
|
+
const saved = this.scope;
|
|
130
|
+
this.scope = entry.scope;
|
|
131
|
+
try {
|
|
132
|
+
return this.withTypeParams(entry.params, node.typeParams, () => this.resolveAliasType(entry));
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
entry.resolving = false;
|
|
136
|
+
this.scope = saved;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
resolveAliasType(entry) {
|
|
140
|
+
const { node } = entry;
|
|
141
|
+
if (node.type.kind === 'ObjectType') {
|
|
142
|
+
// Registered before its members, so that they can refer to the type itself.
|
|
143
|
+
const object = {
|
|
144
|
+
kind: 'object',
|
|
145
|
+
name: node.name.name,
|
|
146
|
+
members: new Map(),
|
|
147
|
+
call: null,
|
|
148
|
+
};
|
|
149
|
+
if (entry.params.length > 0)
|
|
150
|
+
object.typeParams = entry.params;
|
|
151
|
+
entry.resolved = object;
|
|
152
|
+
this.fillMembers(object, node.type.members);
|
|
153
|
+
return object;
|
|
154
|
+
}
|
|
155
|
+
entry.resolving = true;
|
|
156
|
+
const resolved = this.resolveType(node.type);
|
|
157
|
+
// A union keeps the alias name for messages: `cannot use "activ" as Filter`.
|
|
158
|
+
entry.resolved = resolved.kind === 'union' ? { ...resolved, name: node.name.name } : resolved;
|
|
159
|
+
return entry.resolved;
|
|
160
|
+
}
|
|
161
|
+
/** The type of a variable initialized with a value of type `type`. */
|
|
162
|
+
inferredType(type, node) {
|
|
163
|
+
if (type.kind === 'null') {
|
|
164
|
+
this.error('cannot infer a type from null: declare it, e.g. "let x ?User = null"', node);
|
|
165
|
+
return UNKNOWN;
|
|
166
|
+
}
|
|
167
|
+
if (type.kind === 'array' && type.element.kind === 'never') {
|
|
168
|
+
this.error('cannot infer the type of an empty array: declare it, e.g. "let xs []number"', node);
|
|
169
|
+
return UNKNOWN;
|
|
170
|
+
}
|
|
171
|
+
return type;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type * as ast from '../ast.ts';
|
|
2
|
+
import { AssignmentChecker } from './assignments.ts';
|
|
3
|
+
export declare abstract class StatementChecker extends AssignmentChecker {
|
|
4
|
+
/** `ch <- value`; implemented by ChannelChecker. */
|
|
5
|
+
protected abstract checkSend(node: ast.SendStatement): void;
|
|
6
|
+
/** `select { ... }`; implemented by ChannelChecker. */
|
|
7
|
+
protected abstract checkSelect(node: ast.SelectStatement): void;
|
|
8
|
+
protected checkStatement(node: ast.Statement): void;
|
|
9
|
+
/**
|
|
10
|
+
* `effect { ... }`: runs once the markup is created and again when what it reads changes, like
|
|
11
|
+
* a handler. It cleans up with `defer`, so it returns nothing.
|
|
12
|
+
*/
|
|
13
|
+
protected checkEffect(node: ast.EffectStatement): void;
|
|
14
|
+
/**
|
|
15
|
+
* `mount() { ... }`: a function that runs once the markup is in the document, like a handler.
|
|
16
|
+
* It may return the function that cleans up, or nothing.
|
|
17
|
+
*/
|
|
18
|
+
protected checkMount(node: ast.MountStatement): void;
|
|
19
|
+
/**
|
|
20
|
+
* `delete scores[name]` removes a key of an object used as a map, `{ [string]: T }`, or a field
|
|
21
|
+
* that may be missing, of type `?T`; values of type `any` are not checked.
|
|
22
|
+
*/
|
|
23
|
+
private checkDelete;
|
|
24
|
+
protected checkBlock(statements: readonly ast.Statement[]): void;
|
|
25
|
+
protected checkVariableDeclaration(node: ast.VariableDeclaration): void;
|
|
26
|
+
/** The types of the variables of a declaration: the written one, or those of their values. */
|
|
27
|
+
private initializerTypes;
|
|
28
|
+
}
|