rooklang 0.3.2 → 0.3.4
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 +32 -7
- package/README.ru.md +32 -7
- package/dist/ast/declarations.d.ts +20 -1
- package/dist/ast/expressions.d.ts +31 -1
- package/dist/ast/markup.d.ts +6 -1
- package/dist/ast/statements.d.ts +25 -1
- 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 +7 -0
- package/dist/checker/assignments.js +48 -8
- package/dist/checker/base.d.ts +2 -1
- package/dist/checker/base.js +6 -12
- package/dist/checker/builtins.js +15 -5
- 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 +16 -2
- 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 +41 -12
- package/dist/checker/closing.d.ts +21 -0
- package/dist/checker/closing.js +164 -0
- package/dist/checker/completion.d.ts +2 -2
- package/dist/checker/completion.js +2 -2
- package/dist/checker/context.d.ts +19 -0
- package/dist/checker/control-flow.d.ts +6 -0
- package/dist/checker/control-flow.js +34 -0
- package/dist/checker/declarations.js +1 -0
- package/dist/checker/decode.d.ts +8 -0
- package/dist/checker/decode.js +32 -0
- package/dist/checker/error-results.d.ts +6 -0
- package/dist/checker/error-results.js +31 -0
- package/dist/checker/expressions.d.ts +5 -1
- package/dist/checker/expressions.js +13 -34
- package/dist/checker/flow.js +8 -4
- package/dist/checker/helpers.d.ts +1 -1
- package/dist/checker/helpers.js +9 -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.js +23 -3
- package/dist/checker/members.js +7 -0
- 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/signals.d.ts +8 -0
- package/dist/checker/signals.js +48 -0
- package/dist/checker/statements.d.ts +5 -0
- package/dist/checker/statements.js +19 -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/cli.js +18 -36
- package/dist/codegen/calls.d.ts +23 -0
- package/dist/codegen/calls.js +88 -7
- package/dist/codegen/control-statements.d.ts +19 -3
- package/dist/codegen/control-statements.js +105 -20
- package/dist/codegen/decode.d.ts +14 -0
- package/dist/codegen/decode.js +93 -0
- package/dist/codegen/emitter.d.ts +10 -41
- package/dist/codegen/emitter.js +9 -4
- package/dist/codegen/expressions.d.ts +9 -0
- package/dist/codegen/expressions.js +15 -24
- package/dist/codegen/js.d.ts +2 -2
- package/dist/codegen/js.js +5 -2
- package/dist/codegen/options.d.ts +40 -0
- package/dist/codegen/options.js +1 -0
- package/dist/codegen/runtime.d.ts +7 -5
- package/dist/codegen/runtime.js +28 -20
- package/dist/codegen/simple-statements.d.ts +10 -4
- package/dist/codegen/simple-statements.js +40 -15
- package/dist/codegen/statements.js +64 -4
- package/dist/codegen/try.d.ts +5 -0
- package/dist/codegen/try.js +40 -0
- package/dist/compile.d.ts +9 -0
- package/dist/compile.js +19 -6
- package/dist/completion.d.ts +4 -2
- package/dist/completion.js +10 -5
- package/dist/declarations.js +32 -6
- package/dist/decorator-copy.js +5 -2
- 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/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 +40 -13
- package/dist/parser/base.d.ts +4 -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 +3 -1
- package/dist/parser/control-flow.js +21 -24
- package/dist/parser/declarations.d.ts +7 -4
- package/dist/parser/declarations.js +45 -6
- 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/simple-statements.d.ts +1 -1
- package/dist/parser/simple-statements.js +16 -9
- package/dist/parser/statements.d.ts +0 -3
- package/dist/parser/statements.js +22 -25
- 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 +11 -5
- package/dist/runtime-module.d.ts +1 -0
- package/dist/runtime-module.js +3 -0
- package/dist/source-map.d.ts +17 -0
- package/dist/source-map.js +75 -0
- package/dist/ts-types.d.ts +4 -0
- package/dist/ts-types.js +20 -3
- package/dist/typescript/convert-objects.d.ts +2 -1
- package/dist/typescript/convert-objects.js +18 -9
- package/dist/vite.js +13 -5
- package/dist/walk-types.js +1 -0
- package/dist/walk.d.ts +0 -2
- package/dist/walk.js +11 -28
- package/package.json +1 -1
- 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 +190 -0
- package/src/runtime/go.js +14 -5
- package/src/runtime/index.js +6 -0
- package/src/runtime/select.js +14 -9
- 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/checker/classes.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { inlinePackages } from "../inline-packages.js";
|
|
2
|
-
import {
|
|
2
|
+
import { ClassMemberChecker } from "./class-members.js";
|
|
3
3
|
import { isSuperCall, assignsField } from "./helpers.js";
|
|
4
|
+
import { notCopied } from "./transfer.js";
|
|
4
5
|
import { explainMismatch, findClassMember, hasZeroValue, isAssignable, isSubclass, typeToString, UNKNOWN, } from "./types.js";
|
|
5
6
|
// Classes: their base class and members, method bodies, overrides and `implements`; and the declarations a module exports.
|
|
6
|
-
export class ClassChecker extends
|
|
7
|
+
export class ClassChecker extends ClassMemberChecker {
|
|
7
8
|
ensureClassResolved(info) {
|
|
8
9
|
const resolve = this.unresolvedClasses.get(info);
|
|
9
10
|
if (!resolve || this.resolvingClasses.has(info))
|
|
@@ -49,6 +50,9 @@ export class ClassChecker extends DeclarationChecker {
|
|
|
49
50
|
this.error(`cannot extend ${typeToString(base)}: it is not a class`, node.superClass);
|
|
50
51
|
}
|
|
51
52
|
}
|
|
53
|
+
if (node.data)
|
|
54
|
+
this.checkDataBase(node, info);
|
|
55
|
+
info.abstract = node.abstract === true;
|
|
52
56
|
for (const member of node.members) {
|
|
53
57
|
switch (member.kind) {
|
|
54
58
|
case 'FieldDeclaration': {
|
|
@@ -62,20 +66,31 @@ export class ClassChecker extends DeclarationChecker {
|
|
|
62
66
|
else
|
|
63
67
|
type = this.inferredType(valueType, value);
|
|
64
68
|
}
|
|
69
|
+
// The fields of a data class are what goes to another thread.
|
|
70
|
+
const reason = node.data && !member.isStatic ? notCopied(type) : null;
|
|
71
|
+
if (reason !== null) {
|
|
72
|
+
this.error(`the fields of a data class go to other threads as copies: ${reason}`, member.type ?? member.name);
|
|
73
|
+
}
|
|
65
74
|
this.declareMember(info, member.isStatic, member.name, {
|
|
66
75
|
type,
|
|
67
76
|
method: false,
|
|
68
77
|
visibility: member.visibility,
|
|
69
78
|
owner: info,
|
|
79
|
+
...(member.readonly ? { readonly: true } : {}),
|
|
70
80
|
});
|
|
71
81
|
break;
|
|
72
82
|
}
|
|
73
83
|
case 'MethodDeclaration':
|
|
84
|
+
if (member.accessor) {
|
|
85
|
+
this.declareAccessor(info, member, member.accessor);
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
74
88
|
this.declareMember(info, member.isStatic, member.name, {
|
|
75
89
|
type: this.signature(member.params, member.results, member.async),
|
|
76
90
|
method: true,
|
|
77
91
|
visibility: member.visibility,
|
|
78
92
|
owner: info,
|
|
93
|
+
...(member.abstract ? { abstract: true } : {}),
|
|
79
94
|
});
|
|
80
95
|
break;
|
|
81
96
|
case 'ConstructorDeclaration':
|
|
@@ -83,15 +98,20 @@ export class ClassChecker extends DeclarationChecker {
|
|
|
83
98
|
this.error('a class can have only one constructor', member);
|
|
84
99
|
info.ctor = this.signature(member.params, []);
|
|
85
100
|
info.ctorVisibility = member.visibility;
|
|
101
|
+
this.declareParamProperties(member, info, info.ctor);
|
|
86
102
|
break;
|
|
87
103
|
}
|
|
88
104
|
}
|
|
89
105
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
106
|
+
/** A data class extends only a data class: its fields go with it, and its class is restored. */
|
|
107
|
+
checkDataBase(node, info) {
|
|
108
|
+
const base = info.superClass;
|
|
109
|
+
if (info.untypedBase) {
|
|
110
|
+
this.error('a data class cannot extend a class of JS: its copy in another thread could not get the class back', node.superClass);
|
|
111
|
+
}
|
|
112
|
+
else if (base && base.data !== true) {
|
|
113
|
+
this.error(`a data class extends only a data class: mark ${base.name} as one, e.g. data class ${base.name}`, node.superClass);
|
|
114
|
+
}
|
|
95
115
|
}
|
|
96
116
|
/** Method bodies and the checks that need every member of the class. */
|
|
97
117
|
checkClassBodies(node, info) {
|
|
@@ -101,13 +121,18 @@ export class ClassChecker extends DeclarationChecker {
|
|
|
101
121
|
let constructorNode = null;
|
|
102
122
|
for (const member of node.members) {
|
|
103
123
|
if (member.kind === 'MethodDeclaration') {
|
|
104
|
-
const
|
|
124
|
+
const { body } = member;
|
|
125
|
+
const type = member.accessor
|
|
126
|
+
? this.accessorSignatures.get(member)
|
|
127
|
+
: (member.isStatic ? info.statics : info.members).get(member.name.name)?.type;
|
|
105
128
|
if (type?.kind !== 'function')
|
|
106
129
|
continue;
|
|
107
|
-
|
|
108
|
-
async: member.async
|
|
109
|
-
}
|
|
130
|
+
if (body) {
|
|
131
|
+
this.withClass({ info, isStatic: member.isStatic }, () => this.checkFunction(member.params, type, type.results, body, { async: member.async }));
|
|
132
|
+
}
|
|
110
133
|
this.checkOverride(info, member);
|
|
134
|
+
if (member.accessor)
|
|
135
|
+
this.checkAccessorOverride(info, member.name, member.isStatic);
|
|
111
136
|
}
|
|
112
137
|
else if (member.kind === 'ConstructorDeclaration' && info.ctor) {
|
|
113
138
|
constructorNode = member;
|
|
@@ -119,9 +144,13 @@ export class ClassChecker extends DeclarationChecker {
|
|
|
119
144
|
}
|
|
120
145
|
}
|
|
121
146
|
}
|
|
147
|
+
this.checkAbstract(node, info);
|
|
122
148
|
// Fields without a zero value must get one at once or in the constructor.
|
|
123
149
|
for (const member of node.members) {
|
|
124
|
-
if (member.kind !== 'FieldDeclaration'
|
|
150
|
+
if (member.kind !== 'FieldDeclaration')
|
|
151
|
+
continue;
|
|
152
|
+
this.checkAccessorOverride(info, member.name, member.isStatic);
|
|
153
|
+
if (member.isStatic || member.value)
|
|
125
154
|
continue;
|
|
126
155
|
const type = info.members.get(member.name.name)?.type;
|
|
127
156
|
if (!type || hasZeroValue(type))
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type * as ast from '../ast.ts';
|
|
2
|
+
import type { Binding, BindingKind } from './context.ts';
|
|
3
|
+
import { IteratorChecker } from './iterators.ts';
|
|
4
|
+
import type { Type } from './types.ts';
|
|
5
|
+
export declare abstract class ClosingChecker extends IteratorChecker {
|
|
6
|
+
/** The identifier that declares a binding of this module. */
|
|
7
|
+
private readonly declarations;
|
|
8
|
+
/** What each name refers to: its declaration, or the name of a predeclared value. */
|
|
9
|
+
private readonly references;
|
|
10
|
+
protected declareValue(id: ast.Identifier, kind: BindingKind, type: Type | null): Binding;
|
|
11
|
+
protected checkIdentifier(node: ast.Identifier): Type;
|
|
12
|
+
/** The functions declared in the module, by their names, which `use` looks arguments up in. */
|
|
13
|
+
private functions;
|
|
14
|
+
protected checkClosing(): void;
|
|
15
|
+
/** `chan<T>()`, `topic<T>()`, `tick(ms)`, `signal(name)` and their `Channel.make` and the like. */
|
|
16
|
+
private made;
|
|
17
|
+
/** `Channel.close` and the like, as a name, when `Channel` is the predeclared one. */
|
|
18
|
+
private predeclaredMember;
|
|
19
|
+
/** What the code around a name of a channel does with it. */
|
|
20
|
+
private use;
|
|
21
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { forEachChild } from "../walk.js";
|
|
2
|
+
import { IteratorChecker } from "./iterators.js";
|
|
3
|
+
/** What `Channel.x(ch)` and `Topic.x(t)` do with a channel without keeping it. */
|
|
4
|
+
const OPERATIONS = new Set(['send', 'receive', 'range', 'stream', 'publish', 'listen', 'try']);
|
|
5
|
+
const MESSAGES = {
|
|
6
|
+
chan: (name) => `"${name}" is never closed: close it when nothing more is sent, e.g. close(${name})`,
|
|
7
|
+
topic: (name) => `"${name}" is never closed, and a topic listens until it is closed: close it when it is no longer needed, e.g. close(${name}), in a component in the function that mount() returns`,
|
|
8
|
+
tick: (name) => `"${name}" is never closed, and a tick runs until it is closed: e.g. close(${name})`,
|
|
9
|
+
signal: (name) => `"${name}" is never closed, and it keeps the program running until it is: close it once the signal has come, e.g. close(${name})`,
|
|
10
|
+
};
|
|
11
|
+
export class ClosingChecker extends IteratorChecker {
|
|
12
|
+
/** The identifier that declares a binding of this module. */
|
|
13
|
+
declarations = new WeakMap();
|
|
14
|
+
/** What each name refers to: its declaration, or the name of a predeclared value. */
|
|
15
|
+
references = new Map();
|
|
16
|
+
declareValue(id, kind, type) {
|
|
17
|
+
const binding = super.declareValue(id, kind, type);
|
|
18
|
+
this.declarations.set(binding, id);
|
|
19
|
+
return binding;
|
|
20
|
+
}
|
|
21
|
+
checkIdentifier(node) {
|
|
22
|
+
const binding = this.lookupValue(node.name);
|
|
23
|
+
const declaration = binding && this.declarations.get(binding);
|
|
24
|
+
if (declaration)
|
|
25
|
+
this.references.set(node, declaration);
|
|
26
|
+
else if (binding && binding === this.globals.values.get(node.name)) {
|
|
27
|
+
this.references.set(node, node.name);
|
|
28
|
+
}
|
|
29
|
+
return super.checkIdentifier(node);
|
|
30
|
+
}
|
|
31
|
+
/** The functions declared in the module, by their names, which `use` looks arguments up in. */
|
|
32
|
+
functions = new Map();
|
|
33
|
+
checkClosing() {
|
|
34
|
+
const made = [];
|
|
35
|
+
const uses = new Map();
|
|
36
|
+
const collect = (node) => {
|
|
37
|
+
if (node.kind === 'FuncDeclaration')
|
|
38
|
+
this.functions.set(node.name, node);
|
|
39
|
+
forEachChild(node, collect);
|
|
40
|
+
};
|
|
41
|
+
const visit = (node, parent) => {
|
|
42
|
+
const what = node.kind === 'VariableDeclaration' && !node.exported && node.names.length === 1
|
|
43
|
+
? node.values[0] && this.made(node.values[0])
|
|
44
|
+
: null;
|
|
45
|
+
if (node.kind === 'VariableDeclaration' && what)
|
|
46
|
+
made.push([node.names[0], what]);
|
|
47
|
+
const declaration = node.kind === 'Identifier' ? this.references.get(node) : undefined;
|
|
48
|
+
if (node.kind === 'Identifier' && typeof declaration === 'object' && parent) {
|
|
49
|
+
if (!uses.has(declaration)) {
|
|
50
|
+
uses.set(declaration, { closed: false, escapes: false, passed: [] });
|
|
51
|
+
}
|
|
52
|
+
this.use(node, parent, uses.get(declaration));
|
|
53
|
+
}
|
|
54
|
+
forEachChild(node, (child) => visit(child, node));
|
|
55
|
+
};
|
|
56
|
+
collect(this.program);
|
|
57
|
+
visit(this.program, null);
|
|
58
|
+
// Whether a variable or a parameter is closed, goes where it is not seen, or neither.
|
|
59
|
+
const fate = (id, seen) => {
|
|
60
|
+
const found = uses.get(id);
|
|
61
|
+
if (!found || seen.has(id))
|
|
62
|
+
return 'none';
|
|
63
|
+
if (found.closed)
|
|
64
|
+
return 'closed';
|
|
65
|
+
seen.add(id);
|
|
66
|
+
let result = found.escapes ? 'escapes' : 'none';
|
|
67
|
+
for (const [fn, i] of found.passed) {
|
|
68
|
+
const param = fn.params[i];
|
|
69
|
+
const inner = param && !param.rest ? fate(param.name, seen) : 'escapes';
|
|
70
|
+
if (inner === 'closed')
|
|
71
|
+
return 'closed';
|
|
72
|
+
if (inner === 'escapes')
|
|
73
|
+
result = 'escapes';
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
};
|
|
77
|
+
for (const [name, what] of made) {
|
|
78
|
+
if (fate(name, new Set()) === 'none')
|
|
79
|
+
this.warn(MESSAGES[what](name.name), name);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/** `chan<T>()`, `topic<T>()`, `tick(ms)`, `signal(name)` and their `Channel.make` and the like. */
|
|
83
|
+
made(value) {
|
|
84
|
+
if (value.kind === 'ChanExpression')
|
|
85
|
+
return 'chan';
|
|
86
|
+
if (value.kind === 'TopicExpression')
|
|
87
|
+
return 'topic';
|
|
88
|
+
if (value.kind !== 'CallExpression')
|
|
89
|
+
return null;
|
|
90
|
+
const { callee } = value;
|
|
91
|
+
if (callee.kind === 'Identifier') {
|
|
92
|
+
const predeclared = this.references.get(callee);
|
|
93
|
+
return predeclared === 'tick' || predeclared === 'signal' ? predeclared : null;
|
|
94
|
+
}
|
|
95
|
+
const member = this.predeclaredMember(callee);
|
|
96
|
+
if (member === 'Channel.make')
|
|
97
|
+
return 'chan';
|
|
98
|
+
if (member === 'Channel.tick')
|
|
99
|
+
return 'tick';
|
|
100
|
+
if (member === 'Channel.signal')
|
|
101
|
+
return 'signal';
|
|
102
|
+
return member === 'Topic.make' ? 'topic' : null;
|
|
103
|
+
}
|
|
104
|
+
/** `Channel.close` and the like, as a name, when `Channel` is the predeclared one. */
|
|
105
|
+
predeclaredMember(node) {
|
|
106
|
+
if (node.kind !== 'MemberExpression' || node.object.kind !== 'Identifier')
|
|
107
|
+
return null;
|
|
108
|
+
const object = this.references.get(node.object);
|
|
109
|
+
return (object === 'Channel' || object === 'Topic') && node.property.kind === 'Identifier'
|
|
110
|
+
? `${object}.${node.property.name}`
|
|
111
|
+
: null;
|
|
112
|
+
}
|
|
113
|
+
/** What the code around a name of a channel does with it. */
|
|
114
|
+
use(node, parent, found) {
|
|
115
|
+
switch (parent.kind) {
|
|
116
|
+
case 'CallExpression': {
|
|
117
|
+
const i = parent.arguments.indexOf(node);
|
|
118
|
+
if (i < 0)
|
|
119
|
+
return;
|
|
120
|
+
const { callee } = parent;
|
|
121
|
+
const ref = callee.kind === 'Identifier' ? this.references.get(callee) : undefined;
|
|
122
|
+
const member = this.predeclaredMember(callee)?.split('.')[1];
|
|
123
|
+
if (ref === 'close' || member === 'close')
|
|
124
|
+
found.closed = true;
|
|
125
|
+
else if (ref === 'stream' || (member !== undefined && OPERATIONS.has(member)))
|
|
126
|
+
return;
|
|
127
|
+
else {
|
|
128
|
+
const fn = callee.kind === 'FuncExpression'
|
|
129
|
+
? callee
|
|
130
|
+
: typeof ref === 'object'
|
|
131
|
+
? this.functions.get(ref)
|
|
132
|
+
: undefined;
|
|
133
|
+
if (fn)
|
|
134
|
+
found.passed.push([fn, i]);
|
|
135
|
+
else
|
|
136
|
+
found.escapes = true;
|
|
137
|
+
}
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
// Sending to it, receiving from it, a loop over it, comparing and assigning it keep it here;
|
|
141
|
+
// sending it through a channel does not.
|
|
142
|
+
case 'SendStatement':
|
|
143
|
+
if (parent.value === node)
|
|
144
|
+
found.escapes = true;
|
|
145
|
+
return;
|
|
146
|
+
case 'UnaryExpression':
|
|
147
|
+
if (parent.operator !== '<-')
|
|
148
|
+
found.escapes = true;
|
|
149
|
+
return;
|
|
150
|
+
case 'ForInStatement':
|
|
151
|
+
return;
|
|
152
|
+
case 'BinaryExpression':
|
|
153
|
+
if (!['==', '!=', '===', '!=='].includes(parent.operator))
|
|
154
|
+
found.escapes = true;
|
|
155
|
+
return;
|
|
156
|
+
case 'AssignmentStatement':
|
|
157
|
+
if (!parent.targets.includes(node))
|
|
158
|
+
found.escapes = true;
|
|
159
|
+
return;
|
|
160
|
+
default:
|
|
161
|
+
found.escapes = true;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type * as ast from '../ast.ts';
|
|
2
|
-
import {
|
|
2
|
+
import { ClosingChecker } from './closing.ts';
|
|
3
3
|
import type { CompletionResult } from './context.ts';
|
|
4
4
|
import { type Type } from './types.ts';
|
|
5
|
-
export declare abstract class CompletionChecker extends
|
|
5
|
+
export declare abstract class CompletionChecker extends ClosingChecker {
|
|
6
6
|
protected completion: CompletionResult | null;
|
|
7
7
|
private atCursor;
|
|
8
8
|
/** The first check of the cursor counts: a component's body, for one, is checked for each use. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ClosingChecker } from "./closing.js";
|
|
2
2
|
import { ARRAY_MEMBERS, ATTRIBUTE_NAMES, BIGINT_MEMBERS, BOOL_MEMBERS, EVENT_ATTRIBUTES, HTML_TAGS, NUMBER_MEMBERS, STRING_MEMBERS, } from "./completion-names.js";
|
|
3
3
|
import { isSubclass, literalBase, memberNames, memberTypeOf, nonNull, typeToString, union, UNKNOWN, } from "./types.js";
|
|
4
4
|
// Completion in editors (src/completion.ts): the editor writes the name `options.completion` where
|
|
@@ -12,7 +12,7 @@ const PRIMITIVE_MEMBERS = {
|
|
|
12
12
|
bigint: BIGINT_MEMBERS,
|
|
13
13
|
bool: BOOL_MEMBERS,
|
|
14
14
|
};
|
|
15
|
-
export class CompletionChecker extends
|
|
15
|
+
export class CompletionChecker extends ClosingChecker {
|
|
16
16
|
completion = null;
|
|
17
17
|
atCursor(node) {
|
|
18
18
|
return node.name === this.options.completion;
|
|
@@ -64,6 +64,8 @@ export interface CheckResult {
|
|
|
64
64
|
declarations: ModuleExports;
|
|
65
65
|
/** Types of checked expressions, for code generation (e.g. how to insert element children). */
|
|
66
66
|
types: WeakMap<ast.Expression, Type>;
|
|
67
|
+
/** The data classes that the module declares or imports, by their names here. */
|
|
68
|
+
dataClasses: Set<string>;
|
|
67
69
|
/** With the `completion` option: what can be written at the cursor, if the check reached it. */
|
|
68
70
|
completion?: CompletionResult;
|
|
69
71
|
}
|
|
@@ -78,6 +80,17 @@ export interface Completion {
|
|
|
78
80
|
kind: 'variable' | 'constant' | 'function' | 'class' | 'component' | 'type' | 'property' | 'method' | 'keyword' | 'tag' | 'attribute';
|
|
79
81
|
/** The type, as Rook writes it. */
|
|
80
82
|
detail?: string;
|
|
83
|
+
/** A name exported by another module of the package: choosing it imports it (auto-import.ts). */
|
|
84
|
+
import?: {
|
|
85
|
+
specifier: string;
|
|
86
|
+
edit: TextEdit;
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/** A change of the text of a module: what is between the offsets becomes `text`. */
|
|
90
|
+
export interface TextEdit {
|
|
91
|
+
start: number;
|
|
92
|
+
end: number;
|
|
93
|
+
text: string;
|
|
81
94
|
}
|
|
82
95
|
export type BindingKind = 'let' | 'const' | 'state' | 'derived' | 'ref' | 'param' | 'prop' | 'function' | 'class' | 'import' | 'loop' | 'catch' | 'builtin' | 'component';
|
|
83
96
|
export interface Binding {
|
|
@@ -87,6 +100,12 @@ export interface Binding {
|
|
|
87
100
|
type: Type | null;
|
|
88
101
|
/** For components, which exist only at compile time and are used as tags. */
|
|
89
102
|
component?: ComponentInfo;
|
|
103
|
+
/**
|
|
104
|
+
* `const user, err = loadUser(id)` of `(User, error)`: the error has the values that are not null
|
|
105
|
+
* once it is null, and each of them has the error (error-results.ts).
|
|
106
|
+
*/
|
|
107
|
+
guards?: Binding[];
|
|
108
|
+
guardedBy?: Binding;
|
|
90
109
|
}
|
|
91
110
|
export interface ComponentInfo {
|
|
92
111
|
node: ast.ComponentDeclaration;
|
|
@@ -5,6 +5,11 @@ export declare abstract class ControlFlowChecker extends StatementChecker {
|
|
|
5
5
|
protected checkIf(node: ast.IfStatement): void;
|
|
6
6
|
protected checkFor(node: ast.ForStatement): void;
|
|
7
7
|
protected checkSimpleStatement(node: ast.SimpleStatement): void;
|
|
8
|
+
/**
|
|
9
|
+
* `if const v = f(); v > 0 { }`: the variables of the initializer are in a scope of their own,
|
|
10
|
+
* around every branch. Returns whether `check` checked the statement inside it.
|
|
11
|
+
*/
|
|
12
|
+
private checkWithInit;
|
|
8
13
|
protected checkSwitch(node: ast.SwitchStatement): void;
|
|
9
14
|
/**
|
|
10
15
|
* `switch filter { case "all": ... default: ... }`: in a case the discriminant has the literals
|
|
@@ -17,6 +22,7 @@ export declare abstract class ControlFlowChecker extends StatementChecker {
|
|
|
17
22
|
protected narrow(node: ast.Expression, assumeTrue: boolean): Narrowing;
|
|
18
23
|
/** `x instanceof Date`: an instance of the class when true, the rest of a union when false. */
|
|
19
24
|
protected narrowInstanceof(left: ast.Expression, right: ast.Expression, assumeTrue: boolean): Narrowing;
|
|
25
|
+
protected nullError(node: ast.Expression): void;
|
|
20
26
|
protected narrowableBinding(node: ast.Expression): Binding | undefined;
|
|
21
27
|
/** An assignment to a variable ends its narrowing. */
|
|
22
28
|
protected forget(target: ast.Expression): void;
|
|
@@ -7,6 +7,8 @@ import { ANY, BOOL, isAssignable, isComparable, isNullable, isSubclass, literal,
|
|
|
7
7
|
// if, for, switch and try, and how conditions narrow the types of variables in the branches.
|
|
8
8
|
export class ControlFlowChecker extends StatementChecker {
|
|
9
9
|
checkIf(node) {
|
|
10
|
+
if (this.checkWithInit(node.init, () => this.checkIf({ ...node, init: null })))
|
|
11
|
+
return;
|
|
10
12
|
this.checkCondition(node.condition);
|
|
11
13
|
const before = this.flow;
|
|
12
14
|
// Both are found from the flow before the `if`, not from the end of a branch.
|
|
@@ -52,7 +54,22 @@ export class ControlFlowChecker extends StatementChecker {
|
|
|
52
54
|
checkSimpleStatement(node) {
|
|
53
55
|
this.checkStatement(node);
|
|
54
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* `if const v = f(); v > 0 { }`: the variables of the initializer are in a scope of their own,
|
|
59
|
+
* around every branch. Returns whether `check` checked the statement inside it.
|
|
60
|
+
*/
|
|
61
|
+
checkWithInit(init, check) {
|
|
62
|
+
if (init === null)
|
|
63
|
+
return false;
|
|
64
|
+
this.withScope(() => {
|
|
65
|
+
this.checkStatement(init);
|
|
66
|
+
check();
|
|
67
|
+
});
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
55
70
|
checkSwitch(node) {
|
|
71
|
+
if (this.checkWithInit(node.init, () => this.checkSwitch({ ...node, init: null })))
|
|
72
|
+
return;
|
|
56
73
|
const discriminant = node.discriminant ? this.checkValue(node.discriminant) : null;
|
|
57
74
|
this.dropNarrowing(assignedNames(node));
|
|
58
75
|
const entry = this.flow;
|
|
@@ -170,6 +187,14 @@ export class ControlFlowChecker extends StatementChecker {
|
|
|
170
187
|
if (binding?.type)
|
|
171
188
|
return [[binding, nonNull(this.flow.get(binding) ?? binding.type)]];
|
|
172
189
|
}
|
|
190
|
+
// `err == null` when true, `err != null` when false: the values returned with `err` are not
|
|
191
|
+
// null (error-results.ts).
|
|
192
|
+
if ((operator === '==' && assumeTrue) || (operator === '!=' && !assumeTrue)) {
|
|
193
|
+
const target = right.kind === 'NullLiteral' ? left : left.kind === 'NullLiteral' ? right : null;
|
|
194
|
+
const guards = target ? this.narrowableBinding(target)?.guards : undefined;
|
|
195
|
+
if (guards)
|
|
196
|
+
return guards.map((value) => [value, nonNull(this.flow.get(value) ?? value.type)]);
|
|
197
|
+
}
|
|
173
198
|
if (operator === 'instanceof')
|
|
174
199
|
return this.narrowInstanceof(left, right, assumeTrue);
|
|
175
200
|
if (operator !== '==' && operator !== '!=')
|
|
@@ -219,6 +244,15 @@ export class ControlFlowChecker extends StatementChecker {
|
|
|
219
244
|
: !isAssignable(member, instance));
|
|
220
245
|
return [[binding, union(isNullable(current) ? [...kept, NULL] : kept)]];
|
|
221
246
|
}
|
|
247
|
+
nullError(node) {
|
|
248
|
+
const name = node.kind === 'Identifier' ? node.name : null;
|
|
249
|
+
const error = name !== null ? this.lookupValue(name)?.guardedBy?.name : undefined;
|
|
250
|
+
this.error(error !== undefined
|
|
251
|
+
? `"${name}" may be null until "${error}" is checked: e.g. if ${error} != null { return }`
|
|
252
|
+
: name !== null
|
|
253
|
+
? `"${name}" may be null: check it with "if ${name} != null" or use "?."`
|
|
254
|
+
: 'this value may be null: check it for null or use "?."', node);
|
|
255
|
+
}
|
|
222
256
|
narrowableBinding(node) {
|
|
223
257
|
if (node.kind !== 'Identifier')
|
|
224
258
|
return undefined;
|
|
@@ -34,6 +34,7 @@ export class DeclarationChecker extends ImportChecker {
|
|
|
34
34
|
break;
|
|
35
35
|
case 'ClassDeclaration': {
|
|
36
36
|
const info = createClass(statement.name.name);
|
|
37
|
+
info.data = statement.data;
|
|
37
38
|
info.typeParams = this.createTypeParams(statement.typeParams);
|
|
38
39
|
this.declareType(statement.name, info.instance);
|
|
39
40
|
this.declareValue(statement.name, 'class', info.value);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type * as ast from '../ast.ts';
|
|
2
|
+
import { TopicChecker } from './topics.ts';
|
|
3
|
+
import { type Type } from './types.ts';
|
|
4
|
+
export declare abstract class DecodeChecker extends TopicChecker {
|
|
5
|
+
protected checkDecode(node: ast.DecodeExpression): Type;
|
|
6
|
+
/** Why decode cannot take a value of the type: it is neither JSON text nor a value from JS. */
|
|
7
|
+
private notDecodable;
|
|
8
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { isUntyped } from "./helpers.js";
|
|
2
|
+
import { TopicChecker } from "./topics.js";
|
|
3
|
+
import { isText, notDecoded } from "./json.js";
|
|
4
|
+
import { ERROR, typedArrayOf, typeToString } from "./types.js";
|
|
5
|
+
// `decode<User>(body)`: the types are erased, so only the compiler knows what a User is. It writes
|
|
6
|
+
// the type into the JS as data (codegen/decode.ts), and the runtime checks JSON text, or a value
|
|
7
|
+
// from JS, against it when the program runs (runtime/decode.js). So the type must be one that JSON
|
|
8
|
+
// can hold (json.ts). The argument is JSON text if it is a string, and a value from JS if it is any.
|
|
9
|
+
export class DecodeChecker extends TopicChecker {
|
|
10
|
+
checkDecode(node) {
|
|
11
|
+
const type = this.resolveType(node.type);
|
|
12
|
+
const reason = notDecoded(type);
|
|
13
|
+
if (reason !== null)
|
|
14
|
+
this.error(`cannot decode ${typeToString(type)}: ${reason}`, node.type);
|
|
15
|
+
const argument = this.checkValue(node.argument);
|
|
16
|
+
if (!isUntyped(argument) && !isText(argument)) {
|
|
17
|
+
this.error(this.notDecodable(argument), node.argument);
|
|
18
|
+
}
|
|
19
|
+
return { kind: 'tuple', types: [type, ERROR] };
|
|
20
|
+
}
|
|
21
|
+
/** Why decode cannot take a value of the type: it is neither JSON text nor a value from JS. */
|
|
22
|
+
notDecodable(type) {
|
|
23
|
+
const prefix = `decode takes JSON text, a string, or a value of type any from JS, not ${typeToString(type)}`;
|
|
24
|
+
if (type.kind === 'nullable' && isText(type.type)) {
|
|
25
|
+
return `${prefix}: the text may be null, so check it first, e.g. "if text != null"`;
|
|
26
|
+
}
|
|
27
|
+
if (type.kind === 'object' && (typedArrayOf(type) === 'Uint8Array' || type.name === 'Buffer')) {
|
|
28
|
+
return `${prefix}: make the text of the bytes first, e.g. decode<User>(string(data))`;
|
|
29
|
+
}
|
|
30
|
+
return `${prefix}; its type is known already`;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type Type } from './types.ts';
|
|
2
|
+
export declare function isErrorType(type: Type): boolean;
|
|
3
|
+
/** Positions of the results that may be null only together with an error: all before it. */
|
|
4
|
+
export declare function guardedByError(results: readonly Type[]): number[];
|
|
5
|
+
/** The types of the unpacked results: those before the error may be null. */
|
|
6
|
+
export declare function unpackedResults(results: readonly Type[]): Type[];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ERROR, isNullable, nullable, typesEqual } from "./types.js";
|
|
2
|
+
// Values returned with an error: in `(User, error)` the function may return `null, err` instead of
|
|
3
|
+
// a User, as long as the error is not null. So where the results are unpacked, the values before
|
|
4
|
+
// the error may be null, and they are not null once the error is found to be null. A result that
|
|
5
|
+
// is nullable itself, as in `(?User, error)`, may be null without an error too.
|
|
6
|
+
export function isErrorType(type) {
|
|
7
|
+
return typesEqual(type, ERROR);
|
|
8
|
+
}
|
|
9
|
+
/** Positions of the results that may be null only together with an error: all before it. */
|
|
10
|
+
export function guardedByError(results) {
|
|
11
|
+
if (results.length < 2 || !isErrorType(results.at(-1)))
|
|
12
|
+
return [];
|
|
13
|
+
return results.flatMap((type, i) => (i < results.length - 1 && canBeGuarded(type) ? [i] : []));
|
|
14
|
+
}
|
|
15
|
+
/** The types of the unpacked results: those before the error may be null. */
|
|
16
|
+
export function unpackedResults(results) {
|
|
17
|
+
const guarded = guardedByError(results);
|
|
18
|
+
return results.map((type, i) => (guarded.includes(i) ? nullable(type) : type));
|
|
19
|
+
}
|
|
20
|
+
function canBeGuarded(type) {
|
|
21
|
+
switch (type.kind) {
|
|
22
|
+
case 'any':
|
|
23
|
+
case 'unknown':
|
|
24
|
+
case 'void':
|
|
25
|
+
case 'never':
|
|
26
|
+
case 'null':
|
|
27
|
+
return false;
|
|
28
|
+
default:
|
|
29
|
+
return !isNullable(type);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -11,6 +11,11 @@ export declare abstract class ExpressionChecker extends FunctionChecker {
|
|
|
11
11
|
protected abstract checkChan(node: ast.ChanExpression): Type;
|
|
12
12
|
protected abstract checkStreamChan(node: ast.StreamChanExpression): Type;
|
|
13
13
|
protected abstract checkQueue(node: ast.QueueExpression): Type;
|
|
14
|
+
protected abstract checkTopic(node: ast.TopicExpression): Type;
|
|
15
|
+
protected abstract checkDecode(node: ast.DecodeExpression): Type;
|
|
16
|
+
/** `[1, 2]`; implemented by ArrayLiteralChecker. */
|
|
17
|
+
protected abstract checkArrayLiteral(node: ast.ArrayLiteral, expected: Type | null): Type;
|
|
18
|
+
protected abstract checkTryExpression(node: ast.TryExpression): Type;
|
|
14
19
|
/** `[]byte("text")`; implemented by BytesChecker. */
|
|
15
20
|
protected abstract checkConversion(node: ast.ConversionExpression): Type;
|
|
16
21
|
protected abstract checkMake(node: ast.MakeExpression): Type;
|
|
@@ -24,7 +29,6 @@ export declare abstract class ExpressionChecker extends FunctionChecker {
|
|
|
24
29
|
protected thisType(node: ast.NodeBase): Type;
|
|
25
30
|
/** `super.method()`: the members of the base class. */
|
|
26
31
|
protected superType(node: ast.NodeBase): Type;
|
|
27
|
-
protected checkArrayLiteral(node: ast.ArrayLiteral, expected: Type | null): Type;
|
|
28
32
|
protected checkObjectLiteral(node: ast.ObjectLiteral, expected: Type | null): Type;
|
|
29
33
|
/**
|
|
30
34
|
* `value as Type`: the types must be related, one assignable to the other, as in TypeScript:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FunctionChecker } from "./functions.js";
|
|
2
2
|
import { expectsLiteral, callName, isUntyped } from "./helpers.js";
|
|
3
|
-
import { ANY, BIGINT,
|
|
3
|
+
import { ANY, BIGINT, BOOL, hasUntypedBase, instanceMembers, isAssignable, literal, nonNull, NULL, nullable, NUMBER, STRING, typeToString, UNKNOWN, } from "./types.js";
|
|
4
4
|
// Expressions: names, `this`, literals of arrays and objects; the operators are in operators.ts.
|
|
5
5
|
export class ExpressionChecker extends FunctionChecker {
|
|
6
6
|
/** The type of an expression that must be a single value (not `void` or several results). */
|
|
@@ -35,6 +35,9 @@ export class ExpressionChecker extends FunctionChecker {
|
|
|
35
35
|
return BIGINT;
|
|
36
36
|
case 'StringLiteral':
|
|
37
37
|
return expectsLiteral(expected) ? literal(node.value) : STRING;
|
|
38
|
+
// A RegExp of lib.es5.d.ts; the playground, which has no lib files, does not know its members.
|
|
39
|
+
case 'RegExpLiteral':
|
|
40
|
+
return this.options.library?.type('RegExp') ?? ANY;
|
|
38
41
|
case 'TemplateLiteral':
|
|
39
42
|
for (const expression of node.expressions)
|
|
40
43
|
this.checkValue(expression);
|
|
@@ -71,6 +74,12 @@ export class ExpressionChecker extends FunctionChecker {
|
|
|
71
74
|
return this.checkStreamChan(node);
|
|
72
75
|
case 'QueueExpression':
|
|
73
76
|
return this.checkQueue(node);
|
|
77
|
+
case 'TopicExpression':
|
|
78
|
+
return this.checkTopic(node);
|
|
79
|
+
case 'DecodeExpression':
|
|
80
|
+
return this.checkDecode(node);
|
|
81
|
+
case 'TryExpression':
|
|
82
|
+
return this.checkTryExpression(node);
|
|
74
83
|
case 'ConversionExpression':
|
|
75
84
|
return this.checkConversion(node);
|
|
76
85
|
case 'MakeExpression':
|
|
@@ -104,7 +113,9 @@ export class ExpressionChecker extends FunctionChecker {
|
|
|
104
113
|
? `"${node.name}" is a type, not a value`
|
|
105
114
|
: node.name === 'make'
|
|
106
115
|
? 'make takes the type of a typed array first, e.g. make([]float64, n)'
|
|
107
|
-
:
|
|
116
|
+
: node.name === 'decode'
|
|
117
|
+
? 'decode takes the type first, e.g. decode<User>(body)'
|
|
118
|
+
: `"${node.name}" is not defined`, node);
|
|
108
119
|
return UNKNOWN;
|
|
109
120
|
}
|
|
110
121
|
// A component as a value: a tag can take it from there, `<section.content />`.
|
|
@@ -142,38 +153,6 @@ export class ExpressionChecker extends FunctionChecker {
|
|
|
142
153
|
this.error(`class "${info.name}" has no base class`, node);
|
|
143
154
|
return UNKNOWN;
|
|
144
155
|
}
|
|
145
|
-
checkArrayLiteral(node, expected) {
|
|
146
|
-
const context = expected ? nonNull(expected) : null;
|
|
147
|
-
const expectedElement = context?.kind === 'array' ? context.element : context?.kind === 'any' ? ANY : null;
|
|
148
|
-
let element = expectedElement ?? NEVER;
|
|
149
|
-
for (const item of node.elements) {
|
|
150
|
-
let type;
|
|
151
|
-
if (item.kind === 'SpreadElement') {
|
|
152
|
-
const spread = this.checkValue(item.argument, expectedElement && arrayOf(expectedElement));
|
|
153
|
-
if (spread.kind === 'array')
|
|
154
|
-
type = spread.element;
|
|
155
|
-
else if (spread.kind === 'any' || spread.kind === 'unknown')
|
|
156
|
-
type = spread;
|
|
157
|
-
// JS spreads any iterable: a Set, a typed array, a `[]byte`.
|
|
158
|
-
else if (spread.kind === 'object' && spread.iterates)
|
|
159
|
-
type = spread.iterates;
|
|
160
|
-
else {
|
|
161
|
-
this.error(`cannot spread ${typeToString(spread)}: it is not an array or an iterable`, item.argument);
|
|
162
|
-
type = UNKNOWN;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
else {
|
|
166
|
-
type = this.checkValue(item, expectedElement);
|
|
167
|
-
}
|
|
168
|
-
if (expectedElement) {
|
|
169
|
-
this.expectAssignable(type, expectedElement, item.kind === 'SpreadElement' ? item.argument : item);
|
|
170
|
-
}
|
|
171
|
-
else {
|
|
172
|
-
element = commonType(element, type);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
return arrayOf(element);
|
|
176
|
-
}
|
|
177
156
|
checkObjectLiteral(node, expected) {
|
|
178
157
|
let target = expected ? nonNull(expected) : null;
|
|
179
158
|
// `?(ScrollIntoViewOptions | bool)`: an object literal can only be the object type in it.
|