@thigasdevelopment/luam 0.17.0 → 0.18.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 +11 -5
- package/lua/class.lua +79 -15
- package/luam.mjs +1192 -888
- package/package.json +1 -1
- package/template/luam.manifest +1 -1
package/luam.mjs
CHANGED
|
@@ -4515,6 +4515,10 @@ function parseDecorators(stream) {
|
|
|
4515
4515
|
skipDecoratorArguments(stream);
|
|
4516
4516
|
}
|
|
4517
4517
|
}
|
|
4518
|
+
const last = decorators[decorators.length - 1];
|
|
4519
|
+
if (last !== void 0 && !stream.isEof() && stream.current().position.line === last.position.line) {
|
|
4520
|
+
stream.report("parse-decorator-line", `Expected a line break after "@${last.name}".`, stream.current().position);
|
|
4521
|
+
}
|
|
4518
4522
|
return decorators;
|
|
4519
4523
|
}
|
|
4520
4524
|
function parseClassMethod(stream, token, decorators) {
|
|
@@ -4653,7 +4657,7 @@ function parseInterfaceDeclaration(stream) {
|
|
|
4653
4657
|
stream.eraseFrom(checkpoint, "declaration");
|
|
4654
4658
|
return declaration;
|
|
4655
4659
|
}
|
|
4656
|
-
function parseEnumDeclaration(stream) {
|
|
4660
|
+
function parseEnumDeclaration(stream, isLocal = false) {
|
|
4657
4661
|
const position2 = stream.next().position;
|
|
4658
4662
|
const name = stream.expect("identifier").value;
|
|
4659
4663
|
const members = [];
|
|
@@ -4666,7 +4670,7 @@ function parseEnumDeclaration(stream) {
|
|
|
4666
4670
|
}
|
|
4667
4671
|
}
|
|
4668
4672
|
stream.expect("punctuation", "}");
|
|
4669
|
-
return { kind: "enum-declaration", name, members, position: position2 };
|
|
4673
|
+
return { kind: "enum-declaration", name, isLocal, members, position: position2 };
|
|
4670
4674
|
}
|
|
4671
4675
|
function parseDeclaration(stream) {
|
|
4672
4676
|
const decorators = parseDecorators(stream);
|
|
@@ -4829,7 +4833,10 @@ function parseKeywordStatement(stream, value) {
|
|
|
4829
4833
|
const token = stream.current();
|
|
4830
4834
|
if (value === "local") {
|
|
4831
4835
|
stream.next();
|
|
4832
|
-
|
|
4836
|
+
if (stream.check("keyword", "function")) {
|
|
4837
|
+
return parseFunctionDeclaration(stream, true);
|
|
4838
|
+
}
|
|
4839
|
+
return stream.check("keyword", "enum") ? parseEnumDeclaration(stream, true) : parseLocalStatement(stream, token.position);
|
|
4833
4840
|
}
|
|
4834
4841
|
if (value === "function") {
|
|
4835
4842
|
return parseFunctionDeclaration(stream, false);
|
|
@@ -5558,7 +5565,7 @@ var MANIFEST_FIELDS = [
|
|
|
5558
5565
|
field("author", OPTIONAL_STRING, "Author written to the generated meta.xml.", { owner: "identity" }),
|
|
5559
5566
|
field("version", OPTIONAL_STRING, "Version written to the generated meta.xml.", { owner: "identity" }),
|
|
5560
5567
|
field("description", OPTIONAL_STRING, "Description written to the generated meta.xml.", { owner: "identity" }),
|
|
5561
|
-
table("
|
|
5568
|
+
table("compiler", "Settings that change how the source is checked and emitted.", COMPILER_OPTION_FIELDS, { defaultValue: {}, owner: "compiler" }),
|
|
5562
5569
|
table("sources", "Paths and patterns that make up the project, grouped by the side each file runs on.", SOURCE_FIELDS, {
|
|
5563
5570
|
defaultValue: {},
|
|
5564
5571
|
owner: "sources"
|
|
@@ -5603,7 +5610,8 @@ var MANIFEST_FIELDS = [
|
|
|
5603
5610
|
var MANIFEST_RECORD = recordType("Manifest", MANIFEST_FIELDS);
|
|
5604
5611
|
var ENV_MEMBER_TYPE = OPTIONAL_STRING;
|
|
5605
5612
|
var REMOVED_FIELDS = {
|
|
5606
|
-
oop: 'Move it to "
|
|
5613
|
+
oop: 'Move it to "compiler = { oop = true }".',
|
|
5614
|
+
compilerOptions: 'Rename it to "compiler = { ... }".',
|
|
5607
5615
|
sourceDirs: 'Replace it with "sources = { server = { ... }, client = { ... }, shared = { ... } }", listing paths or patterns per side.',
|
|
5608
5616
|
assetDirs: `Replace it with "assets = { { from = 'assets/**/*', to = 'assets' } }", naming a destination for each entry.`,
|
|
5609
5617
|
mta: `Replace it with "engine = { minVersion = '1.6.0' }".`,
|
|
@@ -6411,7 +6419,7 @@ function flag(source, name, fallback) {
|
|
|
6411
6419
|
return readBoolean(source, name) ?? fallback;
|
|
6412
6420
|
}
|
|
6413
6421
|
function readCompilerOptions(value) {
|
|
6414
|
-
const source = readTable(value, "
|
|
6422
|
+
const source = readTable(value, "compiler") ?? EMPTY;
|
|
6415
6423
|
return {
|
|
6416
6424
|
strict: flag(source, "strict", DEFAULT_COMPILER_OPTIONS.strict),
|
|
6417
6425
|
oop: flag(source, "oop", DEFAULT_COMPILER_OPTIONS.oop),
|
|
@@ -6576,7 +6584,7 @@ import { tmpdir } from "node:os";
|
|
|
6576
6584
|
import { join as join2 } from "node:path";
|
|
6577
6585
|
|
|
6578
6586
|
// src/cli/version.ts
|
|
6579
|
-
var VERSION = true ? "0.
|
|
6587
|
+
var VERSION = true ? "0.18.0" : "0.0.0-dev";
|
|
6580
6588
|
var PROGRAM_NAME = "luam";
|
|
6581
6589
|
var PROGRAM_DESCRIPTION = "luam \u2014 the Luam compiler for Multi Theft Auto resources.";
|
|
6582
6590
|
|
|
@@ -8119,7 +8127,7 @@ function ambientFromRegistry(registry) {
|
|
|
8119
8127
|
return {
|
|
8120
8128
|
classes: registry.allClasses(),
|
|
8121
8129
|
interfaces: registry.allInterfaces(),
|
|
8122
|
-
enums: registry.allEnums(),
|
|
8130
|
+
enums: registry.allEnums().filter((info) => info.isLocal !== true),
|
|
8123
8131
|
globals: registry.allGlobals(),
|
|
8124
8132
|
events: registry.allEvents()
|
|
8125
8133
|
};
|
|
@@ -13425,8 +13433,10 @@ var CheckContext = class {
|
|
|
13425
13433
|
returnStack = [];
|
|
13426
13434
|
methodStack = [];
|
|
13427
13435
|
projectEnvironments = /* @__PURE__ */ new Map();
|
|
13436
|
+
predeclared = /* @__PURE__ */ new Map();
|
|
13428
13437
|
ambientClasses = /* @__PURE__ */ new Set();
|
|
13429
13438
|
ambientInterfaces = /* @__PURE__ */ new Set();
|
|
13439
|
+
ambientEnums = /* @__PURE__ */ new Set();
|
|
13430
13440
|
constructor(mode, environment, ambient = EMPTY_AMBIENT, project = EMPTY_PROJECT_DECLARATIONS, oop = false) {
|
|
13431
13441
|
this.mode = mode;
|
|
13432
13442
|
this.environment = environment;
|
|
@@ -13465,12 +13475,41 @@ var CheckContext = class {
|
|
|
13465
13475
|
}
|
|
13466
13476
|
return null;
|
|
13467
13477
|
}
|
|
13478
|
+
predeclareClass(info) {
|
|
13479
|
+
this.predeclared.set(info.name, info);
|
|
13480
|
+
}
|
|
13481
|
+
takePredeclaredClass(name) {
|
|
13482
|
+
const info = this.predeclared.get(name) ?? null;
|
|
13483
|
+
this.predeclared.delete(name);
|
|
13484
|
+
return info;
|
|
13485
|
+
}
|
|
13486
|
+
isPredeclaredClass(name) {
|
|
13487
|
+
return this.predeclared.has(name);
|
|
13488
|
+
}
|
|
13489
|
+
awaitsDeclaration(name) {
|
|
13490
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13491
|
+
let current = this.declarations.lookupClass(name);
|
|
13492
|
+
while (current !== null && !seen.has(current.name)) {
|
|
13493
|
+
if (this.predeclared.has(current.name)) {
|
|
13494
|
+
return true;
|
|
13495
|
+
}
|
|
13496
|
+
seen.add(current.name);
|
|
13497
|
+
current = current.superClass === null ? null : this.declarations.lookupClass(current.superClass);
|
|
13498
|
+
}
|
|
13499
|
+
return false;
|
|
13500
|
+
}
|
|
13501
|
+
insideFunction() {
|
|
13502
|
+
return this.returnStack.length > 0;
|
|
13503
|
+
}
|
|
13468
13504
|
isAmbientClass(name) {
|
|
13469
13505
|
return this.ambientClasses.has(name);
|
|
13470
13506
|
}
|
|
13471
13507
|
isAmbientInterface(name) {
|
|
13472
13508
|
return this.ambientInterfaces.has(name);
|
|
13473
13509
|
}
|
|
13510
|
+
isAmbientEnum(name) {
|
|
13511
|
+
return this.ambientEnums.has(name);
|
|
13512
|
+
}
|
|
13474
13513
|
noteExternalReference(name, position2) {
|
|
13475
13514
|
if (!this.externalReferences.has(name)) {
|
|
13476
13515
|
this.externalReferences.set(name, position2);
|
|
@@ -13623,6 +13662,7 @@ var CheckContext = class {
|
|
|
13623
13662
|
this.declarations.declareInterface(info);
|
|
13624
13663
|
}
|
|
13625
13664
|
for (const info of ambient.enums) {
|
|
13665
|
+
this.ambientEnums.add(info.name);
|
|
13626
13666
|
this.declarations.declareEnum(info);
|
|
13627
13667
|
this.binder.declareGlobal({ name: info.name, type: createNamed(info.name), isLocal: false, position: info.position });
|
|
13628
13668
|
}
|
|
@@ -13929,23 +13969,168 @@ function accessorNames(fieldName, isBoolean) {
|
|
|
13929
13969
|
};
|
|
13930
13970
|
}
|
|
13931
13971
|
|
|
13932
|
-
// ../compiler/src/checker/
|
|
13972
|
+
// ../compiler/src/checker/decorator-catalog.ts
|
|
13973
|
+
var CONFLICT = "`check-decorator-conflict` when a generated name is already declared by a hand-written member.";
|
|
13974
|
+
var SPREAD = "On a class it applies to every field of that class; on a field it applies to that field alone.";
|
|
13975
|
+
var NOTHING = "Nothing. The emitted Lua is unchanged; the decorator only makes the checker validate how the class is used.";
|
|
13933
13976
|
var KNOWN_DECORATORS = /* @__PURE__ */ new Map([
|
|
13934
|
-
[
|
|
13935
|
-
|
|
13936
|
-
|
|
13937
|
-
|
|
13938
|
-
|
|
13939
|
-
|
|
13940
|
-
|
|
13941
|
-
|
|
13942
|
-
|
|
13943
|
-
|
|
13944
|
-
|
|
13945
|
-
[
|
|
13946
|
-
|
|
13947
|
-
|
|
13977
|
+
[
|
|
13978
|
+
"Getter",
|
|
13979
|
+
{
|
|
13980
|
+
name: "Getter",
|
|
13981
|
+
documentation: "Generates a typed getter for each decorated field.",
|
|
13982
|
+
targets: ["class", "field"],
|
|
13983
|
+
generates: ["`getField(): FieldType`, returning the field unchanged.", "A boolean field uses `isField()`, and a boolean already named `isReady` keeps `isReady()` instead of `isIsReady()`."],
|
|
13984
|
+
rules: [SPREAD, "The accessor is an ordinary member: completion after `:` lists it, and go-to-definition lands on the field that generated it."],
|
|
13985
|
+
diagnostics: [CONFLICT]
|
|
13986
|
+
}
|
|
13987
|
+
],
|
|
13988
|
+
[
|
|
13989
|
+
"Setter",
|
|
13990
|
+
{
|
|
13991
|
+
name: "Setter",
|
|
13992
|
+
documentation: "Generates a typed setter for each decorated field.",
|
|
13993
|
+
targets: ["class", "field"],
|
|
13994
|
+
generates: ["`setField(value: FieldType): void`, assigning the field.", "A boolean field named `isReady` becomes `setReady(value)`."],
|
|
13995
|
+
rules: [SPREAD, "The parameter is typed from the field, so a wrong argument is `check-type-mismatch`, exactly as for a hand-written method."],
|
|
13996
|
+
diagnostics: [CONFLICT]
|
|
13997
|
+
}
|
|
13998
|
+
],
|
|
13999
|
+
[
|
|
14000
|
+
"FluentSetter",
|
|
14001
|
+
{
|
|
14002
|
+
name: "FluentSetter",
|
|
14003
|
+
documentation: "Generates a chainable setter that returns the same instance.",
|
|
14004
|
+
targets: ["field"],
|
|
14005
|
+
generates: ["`withField(value: FieldType): OwnerClass`, assigning the field and returning `self`."],
|
|
14006
|
+
rules: ["The returned value is the same instance, never a copy, so calls chain: `session:withTimeout(60):withTimeout(90)`.", "It does not replace `@Setter`; declare both when `setField` is wanted as well."],
|
|
14007
|
+
diagnostics: [CONFLICT]
|
|
14008
|
+
}
|
|
14009
|
+
],
|
|
14010
|
+
[
|
|
14011
|
+
"ToString",
|
|
14012
|
+
{
|
|
14013
|
+
name: "ToString",
|
|
14014
|
+
documentation: "Generates a shallow string representation.",
|
|
14015
|
+
targets: ["class"],
|
|
14016
|
+
generates: ["`toString(): string`, formatted as `ClassName{field=value, field=value}`."],
|
|
14017
|
+
rules: ["Every field of the class is included, in declaration order, through `tostring`.", "It is shallow: a table field is rendered as its `tostring` address, not as its contents."],
|
|
14018
|
+
diagnostics: [CONFLICT]
|
|
14019
|
+
}
|
|
14020
|
+
],
|
|
14021
|
+
[
|
|
14022
|
+
"Equals",
|
|
14023
|
+
{
|
|
14024
|
+
name: "Equals",
|
|
14025
|
+
documentation: "Generates shallow field equality.",
|
|
14026
|
+
targets: ["class"],
|
|
14027
|
+
generates: ["`equals(other: OwnerClass): boolean`, comparing every field with `==`."],
|
|
14028
|
+
rules: ["It is shallow: two tables with the same contents are equal only when they are the same table.", "It does not overload the Lua `==` operator; call `equals` explicitly."],
|
|
14029
|
+
diagnostics: [CONFLICT]
|
|
14030
|
+
}
|
|
14031
|
+
],
|
|
14032
|
+
[
|
|
14033
|
+
"Clone",
|
|
14034
|
+
{
|
|
14035
|
+
name: "Clone",
|
|
14036
|
+
documentation: "Generates a shallow clone.",
|
|
14037
|
+
targets: ["class"],
|
|
14038
|
+
generates: ["`clone(): OwnerClass`, a new instance carrying the same field values."],
|
|
14039
|
+
rules: ["The copy is built with `new OwnerClass()` and no constructor arguments, then every field is assigned.", "It is shallow: a table stored in a field is the same table in the clone."],
|
|
14040
|
+
diagnostics: [CONFLICT]
|
|
14041
|
+
}
|
|
14042
|
+
],
|
|
14043
|
+
[
|
|
14044
|
+
"Serializable",
|
|
14045
|
+
{
|
|
14046
|
+
name: "Serializable",
|
|
14047
|
+
documentation: "Generates a plain-table view of the fields.",
|
|
14048
|
+
targets: ["class"],
|
|
14049
|
+
generates: ["`toTable(): table`, a plain table keyed by field name."],
|
|
14050
|
+
rules: ["It is shallow: a table stored in a field is the same table in the result.", "It pairs with `@Deserialize` for a round trip."],
|
|
14051
|
+
diagnostics: [CONFLICT]
|
|
14052
|
+
}
|
|
14053
|
+
],
|
|
14054
|
+
[
|
|
14055
|
+
"Deserialize",
|
|
14056
|
+
{
|
|
14057
|
+
name: "Deserialize",
|
|
14058
|
+
documentation: "Generates field assignment from a plain table.",
|
|
14059
|
+
targets: ["class"],
|
|
14060
|
+
generates: ["`fromTable(values: table): void`, assigning the fields of the current instance."],
|
|
14061
|
+
rules: ["It assigns onto the existing instance and returns nothing.", "Every field is assigned, so a key missing from `values` sets that field to `nil`."],
|
|
14062
|
+
diagnostics: [CONFLICT]
|
|
14063
|
+
}
|
|
14064
|
+
],
|
|
14065
|
+
[
|
|
14066
|
+
"Lazy",
|
|
14067
|
+
{
|
|
14068
|
+
name: "Lazy",
|
|
14069
|
+
documentation: "Generates a caching getter for an initialized field.",
|
|
14070
|
+
targets: ["field"],
|
|
14071
|
+
generates: ["`getField(): FieldType`, running the initializer on first access and reusing the result afterwards."],
|
|
14072
|
+
rules: ["The initializer is not emitted as an initial class value; the getter runs it while the field is still `nil`.", "Naming follows `@Getter`, so a boolean field yields `isField()`."],
|
|
14073
|
+
diagnostics: ["`check-lazy-initializer` when the decorated field has no initializer.", CONFLICT]
|
|
14074
|
+
}
|
|
14075
|
+
],
|
|
14076
|
+
[
|
|
14077
|
+
"Observable",
|
|
14078
|
+
{
|
|
14079
|
+
name: "Observable",
|
|
14080
|
+
documentation: "Generates a notifying setter and listener registration.",
|
|
14081
|
+
targets: ["field"],
|
|
14082
|
+
generates: ["`setField(value: FieldType): void`, assigning the field and then calling every listener with the new value.", "`onFieldChanged(listener): void`, appending a listener."],
|
|
14083
|
+
rules: ["Listeners live on the instance and run in registration order.", "Only the generated setter notifies; assigning `instance.field = value` directly does not."],
|
|
14084
|
+
diagnostics: [CONFLICT]
|
|
14085
|
+
}
|
|
14086
|
+
],
|
|
14087
|
+
[
|
|
14088
|
+
"ReadOnly",
|
|
14089
|
+
{
|
|
14090
|
+
name: "ReadOnly",
|
|
14091
|
+
documentation: "Prevents writes outside methods of the declaring class.",
|
|
14092
|
+
targets: ["field"],
|
|
14093
|
+
generates: [NOTHING],
|
|
14094
|
+
rules: ["Reads are always allowed.", "Methods of the declaring class may assign the field; every other place may not."],
|
|
14095
|
+
diagnostics: ["`check-readonly-assignment` on an assignment outside the declaring class."]
|
|
14096
|
+
}
|
|
14097
|
+
],
|
|
14098
|
+
[
|
|
14099
|
+
"Deprecated",
|
|
14100
|
+
{
|
|
14101
|
+
name: "Deprecated",
|
|
14102
|
+
documentation: "Reports a warning when the decorated member is used.",
|
|
14103
|
+
targets: ["field", "method"],
|
|
14104
|
+
generates: [NOTHING],
|
|
14105
|
+
rules: ["The report lands on every use site, not on the declaration.", "It is a warning, so it never blocks the build."],
|
|
14106
|
+
diagnostics: ["`check-deprecated-use` at every read or call of the member."]
|
|
14107
|
+
}
|
|
14108
|
+
],
|
|
14109
|
+
[
|
|
14110
|
+
"Override",
|
|
14111
|
+
{
|
|
14112
|
+
name: "Override",
|
|
14113
|
+
documentation: "Requires a matching superclass method.",
|
|
14114
|
+
targets: ["method"],
|
|
14115
|
+
generates: [NOTHING],
|
|
14116
|
+
rules: ["The class must declare a parent with `extends`, and that parent must declare the method with an identical signature.", "It does not change dispatch; call the parent implementation with `super(...)`."],
|
|
14117
|
+
diagnostics: ["`check-invalid-override` when the superclass has no such method or the signature differs."]
|
|
14118
|
+
}
|
|
14119
|
+
],
|
|
14120
|
+
[
|
|
14121
|
+
"Builder",
|
|
14122
|
+
{
|
|
14123
|
+
name: "Builder",
|
|
14124
|
+
documentation: "Generates a companion builder class.",
|
|
14125
|
+
targets: ["class"],
|
|
14126
|
+
generates: ["`OwnerClassBuilder`, a separate class declared beside the decorated one.", "`withField(value: FieldType): OwnerClassBuilder` for every field, and `build(): OwnerClass`."],
|
|
14127
|
+
rules: ["The builder adds no member to the decorated class; construct it with `new OwnerClassBuilder()`.", "`build()` calls `new OwnerClass()` with no constructor arguments, then assigns the collected fields."],
|
|
14128
|
+
diagnostics: [CONFLICT]
|
|
14129
|
+
}
|
|
14130
|
+
]
|
|
13948
14131
|
]);
|
|
14132
|
+
|
|
14133
|
+
// ../compiler/src/checker/decorators.ts
|
|
13949
14134
|
function memberExpression(field2) {
|
|
13950
14135
|
return {
|
|
13951
14136
|
kind: "member-expression",
|
|
@@ -13988,7 +14173,8 @@ function validateDecorators(context, decorators, target) {
|
|
|
13988
14173
|
const valid = [];
|
|
13989
14174
|
const seen = /* @__PURE__ */ new Set();
|
|
13990
14175
|
for (const decorator of decorators) {
|
|
13991
|
-
|
|
14176
|
+
const definition = KNOWN_DECORATORS.get(decorator.name);
|
|
14177
|
+
if (definition === void 0) {
|
|
13992
14178
|
context.report("check-unknown-decorator", `Unknown decorator "@${decorator.name}".`, decorator.position);
|
|
13993
14179
|
continue;
|
|
13994
14180
|
}
|
|
@@ -13997,13 +14183,7 @@ function validateDecorators(context, decorators, target) {
|
|
|
13997
14183
|
continue;
|
|
13998
14184
|
}
|
|
13999
14185
|
seen.add(decorator.name);
|
|
14000
|
-
|
|
14001
|
-
const methodOnly = /* @__PURE__ */ new Set(["Override"]);
|
|
14002
|
-
const memberOnly = /* @__PURE__ */ new Set(["Deprecated"]);
|
|
14003
|
-
const classAllowed = /* @__PURE__ */ new Set(["Getter", "Setter", ...classOnly]);
|
|
14004
|
-
const fieldAllowed = /* @__PURE__ */ new Set(["Getter", "Setter", "FluentSetter", "Lazy", "Observable", "ReadOnly", ...memberOnly]);
|
|
14005
|
-
const methodAllowed = /* @__PURE__ */ new Set([...methodOnly, ...memberOnly]);
|
|
14006
|
-
if (target === "class" && !classAllowed.has(decorator.name) || target === "field" && !fieldAllowed.has(decorator.name) || target === "method" && !methodAllowed.has(decorator.name)) {
|
|
14186
|
+
if (!definition.targets.includes(target)) {
|
|
14007
14187
|
context.report("check-decorator-target", `Decorator "@${decorator.name}" is not valid on this ${target}.`, decorator.position);
|
|
14008
14188
|
continue;
|
|
14009
14189
|
}
|
|
@@ -17469,7 +17649,7 @@ function isElementType(name) {
|
|
|
17469
17649
|
}
|
|
17470
17650
|
|
|
17471
17651
|
// ../compiler/src/checker/oop-members.ts
|
|
17472
|
-
var OOP_HINT = 'Set "
|
|
17652
|
+
var OOP_HINT = 'Set "compiler = { oop = true }" in .luam.manifest to enable the MTA OOP API.';
|
|
17473
17653
|
function scopeLabel2(environment) {
|
|
17474
17654
|
return environment === "shared" ? "shared" : `${environment}-only`;
|
|
17475
17655
|
}
|
|
@@ -17609,6 +17789,9 @@ function resolveNamedMember(context, name, expression) {
|
|
|
17609
17789
|
if (isMtaElement(context, name)) {
|
|
17610
17790
|
return resolveMtaMember(context, name, expression.property, expression.position)?.type ?? ANY_TYPE;
|
|
17611
17791
|
}
|
|
17792
|
+
if (context.awaitsDeclaration(name)) {
|
|
17793
|
+
return ANY_TYPE;
|
|
17794
|
+
}
|
|
17612
17795
|
const contract = context.declarations.lookupClass(name) === null ? context.declarations.lookupInterface(name) : null;
|
|
17613
17796
|
if (contract === null) {
|
|
17614
17797
|
return null;
|
|
@@ -17638,6 +17821,10 @@ function checkNewExpression(context, expression) {
|
|
|
17638
17821
|
context.report("check-unknown-class", `Class "${expression.className}" is not defined.`, expression.position);
|
|
17639
17822
|
return ANY_TYPE;
|
|
17640
17823
|
}
|
|
17824
|
+
if (context.isPredeclaredClass(expression.className) && !context.insideFunction()) {
|
|
17825
|
+
const message = `Class "${expression.className}" is declared further down this file, so it does not exist yet at this point.`;
|
|
17826
|
+
context.report("check-class-before-declaration", message, expression.position);
|
|
17827
|
+
}
|
|
17641
17828
|
const constructor = context.declarations.lookupMember(expression.className, "constructor");
|
|
17642
17829
|
if (constructor !== null && constructor.type.kind === "function") {
|
|
17643
17830
|
checkSignature(context, expression.args, constructor.type, expression.position);
|
|
@@ -17723,30 +17910,6 @@ function checkUnary(context, operator, operand, expression) {
|
|
|
17723
17910
|
return context.record(expression, NUMBER_TYPE);
|
|
17724
17911
|
}
|
|
17725
17912
|
|
|
17726
|
-
// ../compiler/src/checker/template.ts
|
|
17727
|
-
function parseInterpolation(segment) {
|
|
17728
|
-
const separator = segment.value.indexOf(":");
|
|
17729
|
-
const path = (separator === -1 ? segment.value : segment.value.slice(0, separator)).trim();
|
|
17730
|
-
const fallback = separator === -1 ? null : segment.value.slice(separator + 1).trim();
|
|
17731
|
-
const root = path.split(".")[0] ?? "";
|
|
17732
|
-
return { raw: segment.value, path, root, fallback, position: segment.position };
|
|
17733
|
-
}
|
|
17734
|
-
function collectInterpolations(segments) {
|
|
17735
|
-
return segments.filter((segment) => segment.kind === "interpolation").map(parseInterpolation);
|
|
17736
|
-
}
|
|
17737
|
-
function collectTemplateRoots(segments) {
|
|
17738
|
-
const roots = [];
|
|
17739
|
-
for (const interpolation of collectInterpolations(segments)) {
|
|
17740
|
-
if (interpolation.root.length > 0 && !roots.includes(interpolation.root)) {
|
|
17741
|
-
roots.push(interpolation.root);
|
|
17742
|
-
}
|
|
17743
|
-
}
|
|
17744
|
-
return roots;
|
|
17745
|
-
}
|
|
17746
|
-
function templateLiteralText(segments) {
|
|
17747
|
-
return segments.map((segment) => segment.kind === "text" ? segment.value : `\${${segment.value}}`).join("");
|
|
17748
|
-
}
|
|
17749
|
-
|
|
17750
17913
|
// ../compiler/src/checker/union-members.ts
|
|
17751
17914
|
function isShapeOption(context, option) {
|
|
17752
17915
|
if (option.kind === "record") {
|
|
@@ -17788,990 +17951,1073 @@ function resolveUnionMember(context, type, expression) {
|
|
|
17788
17951
|
return ANY_TYPE;
|
|
17789
17952
|
}
|
|
17790
17953
|
|
|
17791
|
-
// ../compiler/src/checker/
|
|
17792
|
-
|
|
17793
|
-
|
|
17794
|
-
|
|
17795
|
-
string: STRING_TYPE,
|
|
17796
|
-
table: TABLE_TYPE
|
|
17797
|
-
};
|
|
17798
|
-
function isNativeConstruction(context, expression) {
|
|
17799
|
-
const object = expression.object;
|
|
17800
|
-
if (expression.property !== NATIVE_CONSTRUCTOR || object.kind !== "identifier") {
|
|
17801
|
-
return false;
|
|
17802
|
-
}
|
|
17803
|
-
return nativeConstructor(context, object.name) !== null;
|
|
17804
|
-
}
|
|
17805
|
-
function checkMember(context, expression) {
|
|
17806
|
-
if (expression.object.kind === "identifier" && isMtaClassReference(context, expression.object.name)) {
|
|
17807
|
-
context.references.add(expression.object.name);
|
|
17808
|
-
return context.record(expression, resolveMtaStaticMember(context, expression.object.name, expression.property, expression.position)?.type ?? ANY_TYPE);
|
|
17954
|
+
// ../compiler/src/checker/discriminant.ts
|
|
17955
|
+
function literalValue(expression) {
|
|
17956
|
+
if (expression.kind === "string-literal") {
|
|
17957
|
+
return createStringLiteral(expression.value);
|
|
17809
17958
|
}
|
|
17810
|
-
if (
|
|
17811
|
-
|
|
17812
|
-
const message = `Construct "${name}" with "new ${name}(...)". The "${name}.new(...)" form is not part of the language.`;
|
|
17813
|
-
context.report("check-native-constructor", message, expression.position);
|
|
17959
|
+
if (expression.kind === "boolean-literal") {
|
|
17960
|
+
return createBooleanLiteral(expression.value);
|
|
17814
17961
|
}
|
|
17815
|
-
|
|
17816
|
-
|
|
17817
|
-
|
|
17818
|
-
|
|
17962
|
+
return expression.kind === "number-literal" ? createNumberLiteral(expression.value) : null;
|
|
17963
|
+
}
|
|
17964
|
+
function discriminantTest(left, right) {
|
|
17965
|
+
if (left.kind !== "member-expression" || left.object.kind !== "identifier") {
|
|
17966
|
+
return null;
|
|
17819
17967
|
}
|
|
17820
|
-
const
|
|
17821
|
-
|
|
17822
|
-
|
|
17968
|
+
const value = literalValue(right);
|
|
17969
|
+
return value === null ? null : { name: left.object.name, property: left.property, value };
|
|
17970
|
+
}
|
|
17971
|
+
function subjectType(context, name) {
|
|
17972
|
+
const type = context.narrowedType(name) ?? context.binder.lookup(name)?.type ?? null;
|
|
17973
|
+
return type === null ? null : withoutNil(type);
|
|
17974
|
+
}
|
|
17975
|
+
function filterOptions(context, test, keep) {
|
|
17976
|
+
const subject = subjectType(context, test.name);
|
|
17977
|
+
if (subject === null || subject.kind !== "union") {
|
|
17978
|
+
return null;
|
|
17823
17979
|
}
|
|
17824
|
-
|
|
17825
|
-
|
|
17826
|
-
|
|
17827
|
-
|
|
17980
|
+
const matched = [];
|
|
17981
|
+
for (const option of subject.options) {
|
|
17982
|
+
const member = optionMemberType(context, option, test.property);
|
|
17983
|
+
if (member === null) {
|
|
17984
|
+
return null;
|
|
17985
|
+
}
|
|
17986
|
+
if (isAssignable(test.value, member) === keep) {
|
|
17987
|
+
matched.push(option);
|
|
17828
17988
|
}
|
|
17829
17989
|
}
|
|
17830
|
-
|
|
17831
|
-
|
|
17832
|
-
|
|
17833
|
-
const
|
|
17834
|
-
if (
|
|
17835
|
-
return
|
|
17990
|
+
return matched.length === 0 || matched.length === subject.options.length ? null : createUnion(matched);
|
|
17991
|
+
}
|
|
17992
|
+
function discriminantFact(context, left, right, keep) {
|
|
17993
|
+
const test = discriminantTest(left, right) ?? discriminantTest(right, left);
|
|
17994
|
+
if (test === null) {
|
|
17995
|
+
return null;
|
|
17836
17996
|
}
|
|
17837
|
-
const
|
|
17838
|
-
|
|
17839
|
-
|
|
17997
|
+
const narrowed = filterOptions(context, test, keep);
|
|
17998
|
+
return narrowed === null ? null : [test.name, narrowed];
|
|
17999
|
+
}
|
|
18000
|
+
|
|
18001
|
+
// ../compiler/src/checker/narrowing.ts
|
|
18002
|
+
var GUARD_FUNCTION = "type";
|
|
18003
|
+
var GUARD_TYPES = {
|
|
18004
|
+
boolean: BOOLEAN_TYPE,
|
|
18005
|
+
function: createFunction([], ANY_TYPE, 0, true),
|
|
18006
|
+
nil: NIL_TYPE,
|
|
18007
|
+
number: NUMBER_TYPE,
|
|
18008
|
+
string: STRING_TYPE,
|
|
18009
|
+
table: TABLE_TYPE,
|
|
18010
|
+
thread: THREAD_TYPE,
|
|
18011
|
+
userdata: USERDATA_TYPE
|
|
18012
|
+
};
|
|
18013
|
+
function declaredType(context, name) {
|
|
18014
|
+
return context.binder.lookup(name)?.type ?? null;
|
|
18015
|
+
}
|
|
18016
|
+
function guardedName(expression) {
|
|
18017
|
+
if (expression.kind !== "call-expression" || expression.callee.kind !== "identifier" || expression.callee.name !== GUARD_FUNCTION) {
|
|
18018
|
+
return null;
|
|
17840
18019
|
}
|
|
17841
|
-
|
|
18020
|
+
const [argument] = expression.args;
|
|
18021
|
+
return argument !== void 0 && argument.kind === "identifier" ? argument.name : null;
|
|
17842
18022
|
}
|
|
17843
|
-
function
|
|
17844
|
-
|
|
18023
|
+
function guardFact(left, right) {
|
|
18024
|
+
const name = guardedName(left);
|
|
18025
|
+
const value = right.kind === "string-literal" ? GUARD_TYPES[right.value] : void 0;
|
|
18026
|
+
return name === null || value === void 0 ? null : [name, value];
|
|
17845
18027
|
}
|
|
17846
|
-
function
|
|
17847
|
-
|
|
17848
|
-
|
|
17849
|
-
const message = `This call expects at least ${countArguments(signature.minimumArguments)} but received ${argumentTypes.length}.`;
|
|
17850
|
-
context.report("check-argument-count", message, position2);
|
|
18028
|
+
function nilComparison(left, right) {
|
|
18029
|
+
if (left.kind === "identifier" && right.kind === "nil-literal") {
|
|
18030
|
+
return left.name;
|
|
17851
18031
|
}
|
|
17852
|
-
|
|
17853
|
-
|
|
17854
|
-
|
|
18032
|
+
return right.kind === "identifier" && left.kind === "nil-literal" ? right.name : null;
|
|
18033
|
+
}
|
|
18034
|
+
function present2(context, name, facts) {
|
|
18035
|
+
const declared = declaredType(context, name);
|
|
18036
|
+
if (declared !== null) {
|
|
18037
|
+
facts.set(name, withoutNil(declared));
|
|
17855
18038
|
}
|
|
17856
|
-
|
|
17857
|
-
|
|
17858
|
-
|
|
17859
|
-
|
|
17860
|
-
|
|
17861
|
-
|
|
17862
|
-
});
|
|
17863
|
-
if (signature.isVariadic && signature.variadicType !== void 0) {
|
|
17864
|
-
for (let index = signature.parameters.length; index < argumentTypes.length; index += 1) {
|
|
17865
|
-
const argumentType = argumentTypes[index];
|
|
17866
|
-
const argument = args[Math.min(index, args.length - 1)];
|
|
17867
|
-
if (argumentType !== void 0 && argument !== void 0) {
|
|
17868
|
-
context.expectAssignable(argumentType, signature.variadicType, argument.position, `Argument ${index + 1}`);
|
|
17869
|
-
}
|
|
18039
|
+
}
|
|
18040
|
+
function mergeAlternatives(left, right, facts) {
|
|
18041
|
+
for (const [name, type] of left) {
|
|
18042
|
+
const other = right.get(name);
|
|
18043
|
+
if (other !== void 0) {
|
|
18044
|
+
facts.set(name, createUnion([type, other]));
|
|
17870
18045
|
}
|
|
17871
18046
|
}
|
|
17872
|
-
return signature.returnType;
|
|
17873
18047
|
}
|
|
17874
|
-
function
|
|
17875
|
-
|
|
17876
|
-
|
|
17877
|
-
|
|
17878
|
-
return ANY_TYPE;
|
|
18048
|
+
function applyDiscriminant(context, left, right, keep, facts) {
|
|
18049
|
+
const fact = discriminantFact(context, left, right, keep);
|
|
18050
|
+
if (fact !== null) {
|
|
18051
|
+
facts.set(fact[0], fact[1]);
|
|
17879
18052
|
}
|
|
17880
|
-
return checkSignature(context, expression.args, calleeType, expression.position);
|
|
17881
18053
|
}
|
|
17882
|
-
function
|
|
17883
|
-
|
|
17884
|
-
|
|
17885
|
-
|
|
17886
|
-
return expression.method === "super" && expression.callee.kind === "identifier" && expression.callee.name === "self";
|
|
17887
|
-
}
|
|
17888
|
-
function checkMethodCall(context, expression, method, receiver) {
|
|
17889
|
-
if (receiver.kind !== "named") {
|
|
17890
|
-
checkValueList(context, expression.args);
|
|
17891
|
-
return ANY_TYPE;
|
|
18054
|
+
function collectFacts(context, expression, facts) {
|
|
18055
|
+
if (expression.kind === "group-expression") {
|
|
18056
|
+
collectFacts(context, expression.expression, facts);
|
|
18057
|
+
return;
|
|
17892
18058
|
}
|
|
17893
|
-
|
|
17894
|
-
|
|
17895
|
-
|
|
17896
|
-
context.warn("check-deprecated-use", `Member "${method}" is deprecated.`, expression.position);
|
|
17897
|
-
}
|
|
17898
|
-
return checkSignature(context, expression.args, declared.type, expression.position);
|
|
18059
|
+
if (expression.kind === "identifier") {
|
|
18060
|
+
present2(context, expression.name, facts);
|
|
18061
|
+
return;
|
|
17899
18062
|
}
|
|
17900
|
-
if (
|
|
17901
|
-
|
|
17902
|
-
return ANY_TYPE;
|
|
18063
|
+
if (expression.kind !== "binary-expression") {
|
|
18064
|
+
return;
|
|
17903
18065
|
}
|
|
17904
|
-
|
|
17905
|
-
|
|
17906
|
-
|
|
17907
|
-
return
|
|
18066
|
+
if (expression.operator === "and") {
|
|
18067
|
+
collectFacts(context, expression.left, facts);
|
|
18068
|
+
collectFacts(context, expression.right, facts);
|
|
18069
|
+
return;
|
|
17908
18070
|
}
|
|
17909
|
-
|
|
17910
|
-
|
|
17911
|
-
|
|
17912
|
-
if (isLegacySuperCall(expression)) {
|
|
17913
|
-
checkValueList(context, expression.args);
|
|
17914
|
-
context.report("check-invalid-super", 'Call "super(...)" directly instead of "self:super(...)".', expression.position);
|
|
17915
|
-
return context.record(expression, ANY_TYPE);
|
|
18071
|
+
if (expression.operator === "or") {
|
|
18072
|
+
mergeAlternatives(conditionFacts(context, expression.left), conditionFacts(context, expression.right), facts);
|
|
18073
|
+
return;
|
|
17916
18074
|
}
|
|
17917
|
-
if (
|
|
17918
|
-
|
|
18075
|
+
if (expression.operator === "==") {
|
|
18076
|
+
const guard = guardFact(expression.left, expression.right) ?? guardFact(expression.right, expression.left);
|
|
18077
|
+
const missing = nilComparison(expression.left, expression.right);
|
|
18078
|
+
if (guard !== null) {
|
|
18079
|
+
facts.set(guard[0], guard[1]);
|
|
18080
|
+
} else if (missing !== null) {
|
|
18081
|
+
facts.set(missing, NIL_TYPE);
|
|
18082
|
+
} else {
|
|
18083
|
+
applyDiscriminant(context, expression.left, expression.right, true, facts);
|
|
18084
|
+
}
|
|
18085
|
+
return;
|
|
17919
18086
|
}
|
|
17920
|
-
if (expression.
|
|
17921
|
-
|
|
17922
|
-
|
|
17923
|
-
|
|
17924
|
-
|
|
17925
|
-
|
|
18087
|
+
if (expression.operator === "~=") {
|
|
18088
|
+
const name = nilComparison(expression.left, expression.right);
|
|
18089
|
+
if (name !== null) {
|
|
18090
|
+
present2(context, name, facts);
|
|
18091
|
+
} else {
|
|
18092
|
+
applyDiscriminant(context, expression.left, expression.right, false, facts);
|
|
17926
18093
|
}
|
|
17927
|
-
return context.record(expression, checkSignature(context, expression.args, constructor, expression.position));
|
|
17928
18094
|
}
|
|
17929
|
-
|
|
17930
|
-
|
|
18095
|
+
}
|
|
18096
|
+
function conditionFacts(context, condition) {
|
|
18097
|
+
const facts = /* @__PURE__ */ new Map();
|
|
18098
|
+
collectFacts(context, condition, facts);
|
|
18099
|
+
return facts;
|
|
18100
|
+
}
|
|
18101
|
+
function alwaysExits(body) {
|
|
18102
|
+
const last = body[body.length - 1];
|
|
18103
|
+
if (last === void 0) {
|
|
18104
|
+
return false;
|
|
17931
18105
|
}
|
|
17932
|
-
|
|
17933
|
-
|
|
17934
|
-
return context.record(expression, checkMethodCall(context, expression, expression.method, calleeType));
|
|
18106
|
+
if (last.kind === "return-statement" || last.kind === "break-statement") {
|
|
18107
|
+
return true;
|
|
17935
18108
|
}
|
|
17936
|
-
if (
|
|
17937
|
-
|
|
17938
|
-
const specialized = specializeEventCall(context, expression, calleeType);
|
|
17939
|
-
return context.record(expression, checkArguments(context, expression, specialized ?? calleeType));
|
|
18109
|
+
if (last.kind !== "if-statement" || last.alternate === null) {
|
|
18110
|
+
return false;
|
|
17940
18111
|
}
|
|
17941
|
-
return
|
|
18112
|
+
return last.clauses.every((clause) => alwaysExits(clause.body)) && alwaysExits(last.alternate);
|
|
17942
18113
|
}
|
|
17943
|
-
function
|
|
17944
|
-
const
|
|
17945
|
-
|
|
17946
|
-
|
|
17947
|
-
}
|
|
17948
|
-
return checkExpression(context, field2.value);
|
|
17949
|
-
});
|
|
17950
|
-
const isRecord = expression.fields.every((field2) => field2.name !== null);
|
|
17951
|
-
if (isRecord) {
|
|
17952
|
-
const members = /* @__PURE__ */ new Map();
|
|
17953
|
-
expression.fields.forEach((field2, index) => {
|
|
17954
|
-
if (field2.name !== null) {
|
|
17955
|
-
members.set(field2.name, valueTypes[index] ?? ANY_TYPE);
|
|
17956
|
-
}
|
|
17957
|
-
});
|
|
17958
|
-
return context.record(expression, createLiteralRecord(members));
|
|
17959
|
-
}
|
|
17960
|
-
const isArray = expression.fields.every((field2) => field2.key === null && field2.name === null);
|
|
17961
|
-
if (!isArray) {
|
|
17962
|
-
return context.record(expression, TABLE_TYPE);
|
|
18114
|
+
function guardFacts(context, statement) {
|
|
18115
|
+
const [clause] = statement.kind === "if-statement" ? statement.clauses : [];
|
|
18116
|
+
if (statement.kind !== "if-statement" || statement.alternate !== null || statement.clauses.length !== 1 || clause === void 0) {
|
|
18117
|
+
return /* @__PURE__ */ new Map();
|
|
17963
18118
|
}
|
|
17964
|
-
return
|
|
18119
|
+
return alwaysExits(clause.body) ? negatedFacts(context, clause.condition) : /* @__PURE__ */ new Map();
|
|
17965
18120
|
}
|
|
17966
|
-
function
|
|
17967
|
-
|
|
17968
|
-
|
|
17969
|
-
|
|
17970
|
-
|
|
17971
|
-
continue;
|
|
17972
|
-
}
|
|
17973
|
-
if (context.binder.lookup(interpolation.root) === null) {
|
|
17974
|
-
const message = `Template interpolation "${interpolation.path}" refers to "${interpolation.root}", which is not in scope.`;
|
|
17975
|
-
context.report("check-unknown-template-root", message, interpolation.position);
|
|
18121
|
+
function negatedFacts(context, condition) {
|
|
18122
|
+
const facts = /* @__PURE__ */ new Map();
|
|
18123
|
+
if (condition.kind === "binary-expression" && condition.operator === "or") {
|
|
18124
|
+
for (const [name, type] of [...negatedFacts(context, condition.left), ...negatedFacts(context, condition.right)]) {
|
|
18125
|
+
facts.set(name, type);
|
|
17976
18126
|
}
|
|
18127
|
+
return facts;
|
|
17977
18128
|
}
|
|
17978
|
-
|
|
17979
|
-
|
|
17980
|
-
|
|
17981
|
-
|
|
17982
|
-
|
|
17983
|
-
|
|
17984
|
-
case "boolean-literal":
|
|
17985
|
-
return context.record(expression, createBooleanLiteral(expression.value));
|
|
17986
|
-
case "number-literal":
|
|
17987
|
-
return context.record(expression, createNumberLiteral(expression.value));
|
|
17988
|
-
case "string-literal":
|
|
17989
|
-
return context.record(expression, createStringLiteral(expression.value));
|
|
17990
|
-
case "template-literal":
|
|
17991
|
-
return checkTemplate(context, expression);
|
|
17992
|
-
case "vararg-expression":
|
|
17993
|
-
return context.record(expression, ANY_TYPE);
|
|
17994
|
-
case "identifier": {
|
|
17995
|
-
context.references.add(expression.name);
|
|
17996
|
-
const symbol = context.binder.lookup(expression.name);
|
|
17997
|
-
if (symbol === null) {
|
|
17998
|
-
checkGlobalReference(context, expression.name, expression.position);
|
|
17999
|
-
}
|
|
18000
|
-
return context.record(expression, context.narrowedType(expression.name) ?? symbol?.type ?? ANY_TYPE);
|
|
18001
|
-
}
|
|
18002
|
-
case "member-expression":
|
|
18003
|
-
return checkMember(context, expression);
|
|
18004
|
-
case "index-expression": {
|
|
18005
|
-
const objectType = checkExpression(context, expression.object);
|
|
18006
|
-
const indexType = checkExpression(context, expression.index);
|
|
18007
|
-
if (objectType.kind === "map") {
|
|
18008
|
-
context.expectAssignable(indexType, objectType.key, expression.index.position, "Key");
|
|
18009
|
-
return context.record(expression, objectType.value);
|
|
18010
|
-
}
|
|
18011
|
-
return context.record(expression, objectType.kind === "array" ? objectType.element : ANY_TYPE);
|
|
18012
|
-
}
|
|
18013
|
-
case "call-expression":
|
|
18014
|
-
return checkCall(context, expression);
|
|
18015
|
-
case "new-expression":
|
|
18016
|
-
return context.record(expression, checkNewExpression(context, expression));
|
|
18017
|
-
case "table-expression":
|
|
18018
|
-
return checkTable(context, expression);
|
|
18019
|
-
case "function-expression": {
|
|
18020
|
-
const type = buildFunctionType(context, expression.parameters, expression.returnAnnotation, contextualFunction(expected));
|
|
18021
|
-
checkFunctionBody(context, expression.parameters, expression.returnAnnotation, expression.body, type, null);
|
|
18022
|
-
return context.record(expression, type);
|
|
18023
|
-
}
|
|
18024
|
-
case "binary-expression": {
|
|
18025
|
-
const left = checkExpression(context, expression.left);
|
|
18026
|
-
const right = checkExpression(context, expression.right);
|
|
18027
|
-
return checkBinary(context, expression.operator, left, right, expression);
|
|
18129
|
+
if (condition.kind === "binary-expression" && condition.operator === "==") {
|
|
18130
|
+
const name = nilComparison(condition.left, condition.right);
|
|
18131
|
+
if (name !== null) {
|
|
18132
|
+
present2(context, name, facts);
|
|
18133
|
+
} else {
|
|
18134
|
+
applyDiscriminant(context, condition.left, condition.right, false, facts);
|
|
18028
18135
|
}
|
|
18029
|
-
case "unary-expression":
|
|
18030
|
-
return checkUnary(context, expression.operator, checkExpression(context, expression.operand), expression);
|
|
18031
|
-
default:
|
|
18032
|
-
return context.record(expression, checkExpression(context, expression.expression, expected));
|
|
18033
18136
|
}
|
|
18137
|
+
if (condition.kind === "binary-expression" && condition.operator === "~=") {
|
|
18138
|
+
applyDiscriminant(context, condition.left, condition.right, true, facts);
|
|
18139
|
+
}
|
|
18140
|
+
if (condition.kind === "unary-expression" && condition.operator === "not" && condition.operand.kind === "identifier") {
|
|
18141
|
+
present2(context, condition.operand.name, facts);
|
|
18142
|
+
}
|
|
18143
|
+
return facts;
|
|
18034
18144
|
}
|
|
18035
|
-
|
|
18036
|
-
|
|
18145
|
+
|
|
18146
|
+
// ../compiler/src/checker/control-flow.ts
|
|
18147
|
+
var ITERATOR_FUNCTIONS = /* @__PURE__ */ new Set(["pairs", "ipairs"]);
|
|
18148
|
+
function checkBlock(context, body) {
|
|
18149
|
+
context.binder.pushScope();
|
|
18150
|
+
checkStatements(context, body);
|
|
18151
|
+
context.binder.popScope();
|
|
18037
18152
|
}
|
|
18038
|
-
function
|
|
18039
|
-
|
|
18040
|
-
|
|
18041
|
-
|
|
18042
|
-
if (index === values.length - 1) {
|
|
18043
|
-
types.push(...valuesOf(type));
|
|
18044
|
-
return;
|
|
18045
|
-
}
|
|
18046
|
-
types.push(firstValueOf(type));
|
|
18047
|
-
});
|
|
18048
|
-
return types;
|
|
18153
|
+
function checkNarrowedBlock(context, facts, body) {
|
|
18154
|
+
context.pushNarrowing(facts);
|
|
18155
|
+
checkBlock(context, body);
|
|
18156
|
+
context.popNarrowing();
|
|
18049
18157
|
}
|
|
18050
|
-
|
|
18051
|
-
|
|
18052
|
-
|
|
18053
|
-
|
|
18054
|
-
if (member.annotation === null) {
|
|
18055
|
-
return valueType ?? ANY_TYPE;
|
|
18056
|
-
}
|
|
18057
|
-
const declared = context.resolveAnnotation(member.annotation);
|
|
18058
|
-
if (valueType !== null && member.value !== null) {
|
|
18059
|
-
context.expectAssignable(valueType, declared, member.value.position, `Field "${member.name}"`);
|
|
18158
|
+
function checkIf(context, clauses, alternate) {
|
|
18159
|
+
for (const clause of clauses) {
|
|
18160
|
+
checkExpression(context, clause.condition);
|
|
18161
|
+
checkNarrowedBlock(context, conditionFacts(context, clause.condition), clause.body);
|
|
18060
18162
|
}
|
|
18061
|
-
|
|
18062
|
-
}
|
|
18063
|
-
function checkFieldName(context, member) {
|
|
18064
|
-
if (member.name !== "constructor") {
|
|
18163
|
+
if (alternate === null) {
|
|
18065
18164
|
return;
|
|
18066
18165
|
}
|
|
18067
|
-
const
|
|
18068
|
-
context.
|
|
18166
|
+
const [only] = clauses;
|
|
18167
|
+
checkNarrowedBlock(context, clauses.length === 1 && only !== void 0 ? negatedFacts(context, only.condition) : /* @__PURE__ */ new Map(), alternate);
|
|
18069
18168
|
}
|
|
18070
|
-
function
|
|
18071
|
-
const
|
|
18072
|
-
|
|
18073
|
-
|
|
18074
|
-
|
|
18075
|
-
|
|
18076
|
-
|
|
18169
|
+
function checkNumericFor(context, statement) {
|
|
18170
|
+
const bounds = [statement.start, statement.limit, statement.step].filter((value) => value !== null);
|
|
18171
|
+
for (const bound of bounds) {
|
|
18172
|
+
const type = checkExpression(context, bound);
|
|
18173
|
+
if (!isNumeric(type)) {
|
|
18174
|
+
context.report("check-invalid-operand", `Numeric "for" expects "number" but received "${typeToString(type)}".`, bound.position);
|
|
18175
|
+
}
|
|
18077
18176
|
}
|
|
18078
|
-
|
|
18177
|
+
context.binder.pushScope();
|
|
18178
|
+
context.binder.declare({ name: statement.variable.name, type: NUMBER_TYPE, isLocal: true, position: statement.variable.position, origin: "local" });
|
|
18179
|
+
checkStatements(context, statement.body);
|
|
18180
|
+
context.binder.popScope();
|
|
18079
18181
|
}
|
|
18080
|
-
function
|
|
18081
|
-
const
|
|
18082
|
-
|
|
18083
|
-
|
|
18084
|
-
checkFieldName(context, member);
|
|
18085
|
-
fieldTypes.set(member, fieldType(context, member));
|
|
18086
|
-
}
|
|
18182
|
+
function iteratedTypes(context, iterators) {
|
|
18183
|
+
const [iterator] = iterators;
|
|
18184
|
+
if (iterators.length !== 1 || iterator === void 0 || iterator.kind !== "call-expression" || iterator.callee.kind !== "identifier") {
|
|
18185
|
+
return [];
|
|
18087
18186
|
}
|
|
18088
|
-
const
|
|
18089
|
-
|
|
18090
|
-
|
|
18091
|
-
const decorators = member.decorators.map((decorator) => decorator.name);
|
|
18092
|
-
info.members.set(member.name, {
|
|
18093
|
-
name: member.name,
|
|
18094
|
-
type,
|
|
18095
|
-
isMethod: member.kind === "class-method",
|
|
18096
|
-
position: member.position,
|
|
18097
|
-
readOnly: decorators.includes("ReadOnly"),
|
|
18098
|
-
deprecated: decorators.includes("Deprecated")
|
|
18099
|
-
});
|
|
18187
|
+
const [source] = iterator.args;
|
|
18188
|
+
if (!ITERATOR_FUNCTIONS.has(iterator.callee.name) || source === void 0) {
|
|
18189
|
+
return [];
|
|
18100
18190
|
}
|
|
18101
|
-
|
|
18191
|
+
const type = context.typeOf(source);
|
|
18192
|
+
if (type.kind === "map") {
|
|
18193
|
+
return iterator.callee.name === "ipairs" ? [NUMBER_TYPE, type.value] : [type.key, type.value];
|
|
18194
|
+
}
|
|
18195
|
+
return type.kind === "array" ? [NUMBER_TYPE, type.element] : [];
|
|
18102
18196
|
}
|
|
18103
|
-
function
|
|
18104
|
-
|
|
18197
|
+
function checkGenericFor(context, statement) {
|
|
18198
|
+
statement.iterators.forEach((iterator) => checkExpression(context, iterator));
|
|
18199
|
+
const iterated = iteratedTypes(context, statement.iterators);
|
|
18200
|
+
context.binder.pushScope();
|
|
18201
|
+
statement.variables.forEach((variable, index) => {
|
|
18202
|
+
const inferred = iterated[index] ?? ANY_TYPE;
|
|
18203
|
+
const declared = variable.annotation === null ? inferred : context.resolveAnnotation(variable.annotation);
|
|
18204
|
+
context.binder.declare({ name: variable.name, type: declared, isLocal: true, position: variable.position, origin: "local" });
|
|
18205
|
+
});
|
|
18206
|
+
checkStatements(context, statement.body);
|
|
18207
|
+
context.binder.popScope();
|
|
18208
|
+
}
|
|
18209
|
+
|
|
18210
|
+
// ../compiler/src/checker/events.ts
|
|
18211
|
+
function resolveParameters(context, declaration) {
|
|
18212
|
+
const parameters = [];
|
|
18213
|
+
const names = /* @__PURE__ */ new Set();
|
|
18214
|
+
let invalid = false;
|
|
18215
|
+
declaration.parameters.forEach((parameter, index) => {
|
|
18216
|
+
const type = context.resolveAnnotation(parameter.annotation);
|
|
18217
|
+
if (parameter.name.length > 0 && names.has(parameter.name)) {
|
|
18218
|
+
context.report("check-duplicate-event-parameter", `Event "${declaration.name}" declares parameter "${parameter.name}" more than once.`, parameter.position);
|
|
18219
|
+
invalid = true;
|
|
18220
|
+
}
|
|
18221
|
+
if (parameter.isVararg && index !== declaration.parameters.length - 1) {
|
|
18222
|
+
context.report("check-invalid-event-parameter", `Variadic parameter of event "${declaration.name}" must be last.`, parameter.position);
|
|
18223
|
+
invalid = true;
|
|
18224
|
+
}
|
|
18225
|
+
if (parameter.name.length > 0) {
|
|
18226
|
+
names.add(parameter.name);
|
|
18227
|
+
}
|
|
18228
|
+
parameters.push({ name: parameter.name, type, isVariadic: parameter.isVararg, position: parameter.position });
|
|
18229
|
+
});
|
|
18230
|
+
return { parameters, invalid };
|
|
18231
|
+
}
|
|
18232
|
+
function checkEventDeclaration(context, declaration) {
|
|
18233
|
+
if (context.declarations.lookupEvent(declaration.name) !== null) {
|
|
18234
|
+
context.report("check-duplicate-event", `Event "${declaration.name}" is already defined.`, declaration.position);
|
|
18105
18235
|
return;
|
|
18106
18236
|
}
|
|
18107
|
-
|
|
18108
|
-
|
|
18109
|
-
|
|
18110
|
-
if (field2.kind === "class-field") {
|
|
18111
|
-
const type = info.members.get(field2.name)?.type ?? ANY_TYPE;
|
|
18112
|
-
const method = `with${field2.name[0]?.toUpperCase()}${field2.name.slice(1)}`;
|
|
18113
|
-
members.set(method, { name: method, type: { kind: "function", parameters: [type], minimumArguments: 1, isVariadic: false, returnType: createNamed(name) }, isMethod: true, position: field2.position });
|
|
18114
|
-
}
|
|
18237
|
+
let invalid = declaration.name.length === 0;
|
|
18238
|
+
if (invalid) {
|
|
18239
|
+
context.report("check-invalid-event-name", "An event name cannot be empty.", declaration.position);
|
|
18115
18240
|
}
|
|
18116
|
-
|
|
18117
|
-
|
|
18118
|
-
|
|
18119
|
-
}
|
|
18120
|
-
|
|
18121
|
-
const explicitSelf = member.parameters.find((parameter) => parameter.name === "self");
|
|
18122
|
-
if (explicitSelf !== void 0) {
|
|
18123
|
-
context.report("check-explicit-self-parameter", 'Class methods receive "self" automatically; remove it from the parameter list.', explicitSelf.position);
|
|
18241
|
+
const resolved2 = resolveParameters(context, declaration);
|
|
18242
|
+
invalid = invalid || resolved2.invalid;
|
|
18243
|
+
if (declaration.returnAnnotation !== null && context.resolveAnnotation(declaration.returnAnnotation).kind !== "void") {
|
|
18244
|
+
context.report("check-event-return-type", `Event "${declaration.name}" can only declare a "void" return type.`, declaration.returnAnnotation.position);
|
|
18245
|
+
invalid = true;
|
|
18124
18246
|
}
|
|
18125
|
-
|
|
18126
|
-
|
|
18127
|
-
|
|
18247
|
+
if (!invalid) {
|
|
18248
|
+
context.declarations.declareEvent({
|
|
18249
|
+
name: declaration.name,
|
|
18250
|
+
parameters: resolved2.parameters,
|
|
18251
|
+
environment: context.environment,
|
|
18252
|
+
position: declaration.position
|
|
18253
|
+
});
|
|
18128
18254
|
}
|
|
18129
|
-
context.pushClassMethod({ className: info.name, methodName: member.name });
|
|
18130
|
-
checkFunctionBody(context, member.parameters, member.returnAnnotation, member.body, signature, createNamed(info.name));
|
|
18131
|
-
context.popClassMethod();
|
|
18132
18255
|
}
|
|
18133
|
-
|
|
18134
|
-
|
|
18135
|
-
|
|
18256
|
+
|
|
18257
|
+
// ../compiler/src/checker/statements.ts
|
|
18258
|
+
var ORIGIN = { line: 0, column: 0, offset: 0 };
|
|
18259
|
+
function minimumArguments(context, parameters) {
|
|
18260
|
+
const index = parameters.findIndex((parameter) => {
|
|
18261
|
+
const type = context.resolveAnnotation(parameter.annotation);
|
|
18262
|
+
return parameter.isVararg || type.kind === "optional" || type.kind === "any";
|
|
18263
|
+
});
|
|
18264
|
+
return index === -1 ? parameters.length : index;
|
|
18265
|
+
}
|
|
18266
|
+
function buildFunctionType(context, parameters, returnAnnotation, contextual = null) {
|
|
18267
|
+
const isVariadic = parameters.some((parameter) => parameter.isVararg);
|
|
18268
|
+
const types = parameters.filter((parameter) => !parameter.isVararg).map((parameter, index) => parameter.annotation === null ? contextual?.parameters[index] ?? ANY_TYPE : context.resolveAnnotation(parameter.annotation));
|
|
18269
|
+
return createFunction(types, context.resolveAnnotation(returnAnnotation), minimumArguments(context, parameters), isVariadic);
|
|
18270
|
+
}
|
|
18271
|
+
function checkFunctionBody(context, parameters, returnAnnotation, body, signature, selfType) {
|
|
18272
|
+
context.binder.pushScope();
|
|
18273
|
+
context.pushReturnType(returnAnnotation === null ? null : context.resolveAnnotation(returnAnnotation));
|
|
18274
|
+
if (selfType !== null) {
|
|
18275
|
+
context.binder.declare({ name: "self", type: selfType, isLocal: true, position: ORIGIN });
|
|
18136
18276
|
}
|
|
18137
|
-
|
|
18138
|
-
|
|
18139
|
-
|
|
18277
|
+
let parameterIndex = 0;
|
|
18278
|
+
for (const parameter of parameters) {
|
|
18279
|
+
const type = parameter.isVararg ? ANY_TYPE : signature.parameters[parameterIndex] ?? ANY_TYPE;
|
|
18280
|
+
context.binder.declare({ name: parameter.name, type, isLocal: true, position: parameter.position, origin: "parameter" });
|
|
18281
|
+
if (!parameter.isVararg) {
|
|
18282
|
+
parameterIndex += 1;
|
|
18140
18283
|
}
|
|
18141
|
-
return statement.superClass;
|
|
18142
18284
|
}
|
|
18143
|
-
|
|
18144
|
-
|
|
18145
|
-
|
|
18146
|
-
|
|
18285
|
+
checkStatements(context, body);
|
|
18286
|
+
const inferred = context.popReturnType();
|
|
18287
|
+
if (returnAnnotation === null) {
|
|
18288
|
+
signature.returnType = inferred;
|
|
18147
18289
|
}
|
|
18148
|
-
|
|
18149
|
-
context.noteExternalReference(statement.superClass, statement.position);
|
|
18150
|
-
context.report("check-unknown-class", message, statement.position);
|
|
18151
|
-
return null;
|
|
18290
|
+
context.binder.popScope();
|
|
18152
18291
|
}
|
|
18153
|
-
function
|
|
18154
|
-
if (
|
|
18155
|
-
|
|
18156
|
-
return;
|
|
18292
|
+
function valueAt(values, valueTypes, index) {
|
|
18293
|
+
if (values[index] !== void 0) {
|
|
18294
|
+
return values[index];
|
|
18157
18295
|
}
|
|
18158
|
-
|
|
18159
|
-
|
|
18296
|
+
return index < valueTypes.length ? values[values.length - 1] : void 0;
|
|
18297
|
+
}
|
|
18298
|
+
function checkLocal(context, statement) {
|
|
18299
|
+
const valueTypes = checkValueList(context, statement.values);
|
|
18300
|
+
statement.declarations.forEach((declaration, index) => {
|
|
18301
|
+
const value = valueAt(statement.values, valueTypes, index);
|
|
18302
|
+
const valueType = valueTypes[index] ?? NIL_TYPE;
|
|
18303
|
+
context.forgetNarrowing(declaration.name);
|
|
18304
|
+
if (declaration.annotation === null) {
|
|
18305
|
+
const inferred = context.allowNil || value === void 0 ? ANY_TYPE : widenInferred(valueType);
|
|
18306
|
+
context.binder.declare({ name: declaration.name, type: inferred, isLocal: true, position: declaration.position, origin: "local" });
|
|
18307
|
+
return;
|
|
18308
|
+
}
|
|
18309
|
+
const declared = context.resolveAnnotation(declaration.annotation);
|
|
18310
|
+
if (value !== void 0) {
|
|
18311
|
+
context.expectAssignable(valueType, declared, value.position, `Variable "${declaration.name}"`);
|
|
18312
|
+
}
|
|
18313
|
+
context.binder.declare({ name: declaration.name, type: declared, isLocal: true, position: declaration.position, origin: "local" });
|
|
18314
|
+
});
|
|
18315
|
+
}
|
|
18316
|
+
function checkCompoundOperand(context, operator, type, position2) {
|
|
18317
|
+
if (operator === "..=") {
|
|
18318
|
+
if (!isConcatenable(type)) {
|
|
18319
|
+
context.report("check-invalid-operand", `Operator "..=" cannot be applied to "${typeToString(type)}".`, position2);
|
|
18320
|
+
}
|
|
18160
18321
|
return;
|
|
18161
18322
|
}
|
|
18162
|
-
|
|
18163
|
-
|
|
18164
|
-
|
|
18165
|
-
|
|
18166
|
-
|
|
18167
|
-
|
|
18168
|
-
|
|
18169
|
-
|
|
18170
|
-
|
|
18171
|
-
|
|
18172
|
-
|
|
18173
|
-
|
|
18174
|
-
|
|
18175
|
-
|
|
18176
|
-
|
|
18177
|
-
|
|
18178
|
-
|
|
18323
|
+
if (!isNumeric(type)) {
|
|
18324
|
+
context.report("check-invalid-operand", `Operator "${operator}" cannot be applied to "${typeToString(type)}".`, position2);
|
|
18325
|
+
}
|
|
18326
|
+
}
|
|
18327
|
+
function checkAssignment(context, statement) {
|
|
18328
|
+
const valueTypes = checkValueList(context, statement.values);
|
|
18329
|
+
statement.targets.forEach((target, index) => {
|
|
18330
|
+
const targetType = checkExpression(context, target);
|
|
18331
|
+
const valueType = valueTypes[index] ?? NIL_TYPE;
|
|
18332
|
+
const value = valueAt(statement.values, valueTypes, index);
|
|
18333
|
+
if (target.kind === "member-expression") {
|
|
18334
|
+
const object = context.typeOf(target.object);
|
|
18335
|
+
const frame = context.currentClassMethod();
|
|
18336
|
+
if (object.kind === "named") {
|
|
18337
|
+
const member = context.declarations.lookupMember(object.name, target.property);
|
|
18338
|
+
if (member?.readOnly === true && (frame === null || frame.className !== object.name)) {
|
|
18339
|
+
context.report("check-readonly-assignment", `Field "${target.property}" is read-only outside class "${object.name}".`, target.position);
|
|
18340
|
+
}
|
|
18341
|
+
}
|
|
18342
|
+
}
|
|
18343
|
+
if (statement.operator !== "=") {
|
|
18344
|
+
checkCompoundOperand(context, statement.operator, targetType, target.position);
|
|
18345
|
+
if (statement.values.length > 0) {
|
|
18346
|
+
checkCompoundOperand(context, statement.operator, valueType, value?.position ?? target.position);
|
|
18347
|
+
}
|
|
18348
|
+
return;
|
|
18349
|
+
}
|
|
18350
|
+
if (target.kind === "identifier" && context.binder.lookup(target.name) === null) {
|
|
18351
|
+
context.declareModuleGlobal({ name: target.name, type: valueType, isLocal: false, position: target.position });
|
|
18352
|
+
return;
|
|
18353
|
+
}
|
|
18354
|
+
const declared = target.kind === "identifier" ? context.binder.lookup(target.name)?.type ?? targetType : targetType;
|
|
18355
|
+
if (value !== void 0) {
|
|
18356
|
+
context.expectAssignable(valueType, declared, value.position, "Assignment");
|
|
18357
|
+
}
|
|
18358
|
+
if (target.kind === "identifier") {
|
|
18359
|
+
context.forgetNarrowing(target.name);
|
|
18360
|
+
}
|
|
18361
|
+
});
|
|
18362
|
+
}
|
|
18363
|
+
function checkFunctionDeclaration(context, statement) {
|
|
18364
|
+
const type = buildFunctionType(context, statement.parameters, statement.returnAnnotation);
|
|
18365
|
+
if (statement.name.kind === "identifier") {
|
|
18366
|
+
const symbol = { name: statement.name.name, type, isLocal: statement.isLocal, position: statement.position };
|
|
18367
|
+
if (statement.isLocal) {
|
|
18368
|
+
context.binder.declare({ ...symbol, origin: "local" });
|
|
18369
|
+
} else {
|
|
18370
|
+
context.declareModuleGlobal(symbol);
|
|
18179
18371
|
}
|
|
18180
18372
|
}
|
|
18373
|
+
context.record(statement.name, type);
|
|
18374
|
+
checkFunctionBody(context, statement.parameters, statement.returnAnnotation, statement.body, type, statement.isMethod ? ANY_TYPE : null);
|
|
18181
18375
|
}
|
|
18182
|
-
function
|
|
18183
|
-
|
|
18184
|
-
|
|
18376
|
+
function checkReturn(context, statement) {
|
|
18377
|
+
const valueTypes = checkValueList(context, statement.values);
|
|
18378
|
+
const expected = context.currentReturnType();
|
|
18379
|
+
if (expected === null) {
|
|
18380
|
+
context.inferReturnType(createTuple(valueTypes));
|
|
18185
18381
|
return;
|
|
18186
18382
|
}
|
|
18187
|
-
|
|
18188
|
-
|
|
18189
|
-
const parent = context.declarations.lookupInterface(name);
|
|
18190
|
-
if (name === statement.name || context.declarations.interfaceExtends(name, statement.name)) {
|
|
18191
|
-
context.report("check-interface-cycle", `Interface "${statement.name}" creates an inheritance cycle through "${name}".`, statement.position);
|
|
18192
|
-
} else if (parent === null) {
|
|
18193
|
-
context.noteExternalReference(name, statement.position);
|
|
18194
|
-
context.report("check-unknown-interface", `Interface "${statement.name}" extends "${name}", which is not defined.`, statement.position);
|
|
18195
|
-
} else if (context.isAmbientInterface(name)) {
|
|
18196
|
-
context.noteExternalReference(name, statement.position);
|
|
18197
|
-
}
|
|
18198
|
-
}
|
|
18199
|
-
if (new Set(statement.superInterfaces).size !== statement.superInterfaces.length) {
|
|
18200
|
-
context.report("check-duplicate-interface-parent", `Interface "${statement.name}" extends the same interface more than once.`, statement.position);
|
|
18383
|
+
if (expected.kind === "any") {
|
|
18384
|
+
return;
|
|
18201
18385
|
}
|
|
18202
|
-
|
|
18203
|
-
|
|
18204
|
-
|
|
18205
|
-
if (members.has(member.name)) {
|
|
18206
|
-
context.report("check-duplicate-interface-member", `Interface "${statement.name}" declares member "${member.name}" more than once.`, member.position);
|
|
18207
|
-
} else {
|
|
18208
|
-
members.set(member.name, info);
|
|
18386
|
+
if (expected.kind === "void") {
|
|
18387
|
+
if (statement.values.length > 0) {
|
|
18388
|
+
context.report("check-return-mismatch", 'This function is declared "void" and cannot return a value.', statement.position);
|
|
18209
18389
|
}
|
|
18390
|
+
return;
|
|
18210
18391
|
}
|
|
18211
|
-
|
|
18212
|
-
|
|
18213
|
-
|
|
18214
|
-
|
|
18215
|
-
|
|
18216
|
-
context.report("check-conflicting-interface-member", `Interface "${statement.name}" inherits conflicting declarations of "${member.name}".`, statement.position);
|
|
18217
|
-
} else if (existing === void 0) {
|
|
18218
|
-
inherited.set(member.name, member);
|
|
18219
|
-
}
|
|
18392
|
+
if (expected.kind === "tuple") {
|
|
18393
|
+
if (valueTypes.length !== expected.elements.length) {
|
|
18394
|
+
const message = `This function declares ${expected.elements.length} return values but returns ${valueTypes.length}.`;
|
|
18395
|
+
context.report("check-return-mismatch", message, statement.position);
|
|
18396
|
+
return;
|
|
18220
18397
|
}
|
|
18398
|
+
valueTypes.forEach((type, index) => {
|
|
18399
|
+
const value2 = statement.values[Math.min(index, statement.values.length - 1)];
|
|
18400
|
+
context.expectAssignable(type, expected.elements[index] ?? ANY_TYPE, value2?.position ?? statement.position, `Return value ${index + 1}`);
|
|
18401
|
+
});
|
|
18402
|
+
return;
|
|
18221
18403
|
}
|
|
18222
|
-
|
|
18223
|
-
|
|
18224
|
-
|
|
18225
|
-
members,
|
|
18226
|
-
position: statement.position
|
|
18227
|
-
});
|
|
18228
|
-
}
|
|
18229
|
-
function checkEnumDeclaration(context, statement) {
|
|
18230
|
-
if (context.declarations.lookupEnum(statement.name) !== null) {
|
|
18231
|
-
context.report("check-duplicate-enum", `Enum "${statement.name}" is already defined.`, statement.position);
|
|
18404
|
+
if (valueTypes.length > 1) {
|
|
18405
|
+
const message = `This function declares one return value of type "${typeToString(expected)}" but returns ${valueTypes.length} values.`;
|
|
18406
|
+
context.report("check-return-mismatch", message, statement.position);
|
|
18232
18407
|
return;
|
|
18233
18408
|
}
|
|
18234
|
-
const
|
|
18235
|
-
|
|
18236
|
-
|
|
18409
|
+
const first = valueTypes[0];
|
|
18410
|
+
const value = statement.values[0];
|
|
18411
|
+
if (first === void 0 || value === void 0) {
|
|
18412
|
+
context.expectAssignable(NIL_TYPE, expected, statement.position, "Return value");
|
|
18413
|
+
return;
|
|
18414
|
+
}
|
|
18415
|
+
context.expectAssignable(first, expected, value.position, "Return value");
|
|
18237
18416
|
}
|
|
18238
|
-
|
|
18239
|
-
|
|
18240
|
-
|
|
18241
|
-
|
|
18242
|
-
return
|
|
18417
|
+
function checkDeclareStatement(context, statement) {
|
|
18418
|
+
if (!context.isDeclarationFile) {
|
|
18419
|
+
const message = 'A "declare" statement describes code the compiler does not own and belongs in a ".d.luam" file.';
|
|
18420
|
+
context.report("check-declare-outside-declaration-file", message, statement.position);
|
|
18421
|
+
return;
|
|
18243
18422
|
}
|
|
18244
|
-
|
|
18245
|
-
|
|
18423
|
+
const type = context.resolveAnnotation(statement.annotation);
|
|
18424
|
+
context.declareModuleGlobal({ name: statement.name, type, isLocal: false, position: statement.position });
|
|
18425
|
+
context.declarations.declareGlobal({ name: statement.name, type, position: statement.position });
|
|
18426
|
+
}
|
|
18427
|
+
function checkStatement(context, statement) {
|
|
18428
|
+
switch (statement.kind) {
|
|
18429
|
+
case "local-statement":
|
|
18430
|
+
return checkLocal(context, statement);
|
|
18431
|
+
case "assignment-statement":
|
|
18432
|
+
return checkAssignment(context, statement);
|
|
18433
|
+
case "call-statement":
|
|
18434
|
+
checkExpression(context, statement.expression);
|
|
18435
|
+
return;
|
|
18436
|
+
case "function-declaration":
|
|
18437
|
+
return checkFunctionDeclaration(context, statement);
|
|
18438
|
+
case "return-statement":
|
|
18439
|
+
return checkReturn(context, statement);
|
|
18440
|
+
case "do-statement":
|
|
18441
|
+
return checkBlock(context, statement.body);
|
|
18442
|
+
case "while-statement":
|
|
18443
|
+
checkExpression(context, statement.condition);
|
|
18444
|
+
return checkBlock(context, statement.body);
|
|
18445
|
+
case "repeat-statement":
|
|
18446
|
+
checkBlock(context, statement.body);
|
|
18447
|
+
checkExpression(context, statement.condition);
|
|
18448
|
+
return;
|
|
18449
|
+
case "if-statement":
|
|
18450
|
+
return checkIf(context, statement.clauses, statement.alternate);
|
|
18451
|
+
case "numeric-for-statement":
|
|
18452
|
+
return checkNumericFor(context, statement);
|
|
18453
|
+
case "generic-for-statement":
|
|
18454
|
+
return checkGenericFor(context, statement);
|
|
18455
|
+
case "type-alias-statement": {
|
|
18456
|
+
const resolved2 = context.resolveAnnotation(statement.annotation);
|
|
18457
|
+
const shape = statement.annotation.kind === "type-object" || statement.annotation.kind === "type-intersection";
|
|
18458
|
+
const named2 = shape ? renameRecord(resolved2, statement.name) : resolved2;
|
|
18459
|
+
context.noteTypeParameters(statement.typeParameters);
|
|
18460
|
+
context.binder.declareAlias(statement.name, named2, statement.typeParameters);
|
|
18461
|
+
return;
|
|
18462
|
+
}
|
|
18463
|
+
case "declare-statement":
|
|
18464
|
+
return checkDeclareStatement(context, statement);
|
|
18465
|
+
case "event-declaration":
|
|
18466
|
+
return checkEventDeclaration(context, statement);
|
|
18467
|
+
case "class-declaration":
|
|
18468
|
+
return checkClassDeclaration(context, statement);
|
|
18469
|
+
case "interface-declaration":
|
|
18470
|
+
return checkInterfaceDeclaration(context, statement);
|
|
18471
|
+
case "enum-declaration":
|
|
18472
|
+
return checkEnumDeclaration(context, statement);
|
|
18473
|
+
default:
|
|
18474
|
+
return;
|
|
18246
18475
|
}
|
|
18247
|
-
return expression.kind === "number-literal" ? createNumberLiteral(expression.value) : null;
|
|
18248
18476
|
}
|
|
18249
|
-
function
|
|
18250
|
-
|
|
18251
|
-
|
|
18477
|
+
function checkStatements(context, statements) {
|
|
18478
|
+
let guards = 0;
|
|
18479
|
+
for (const statement of statements) {
|
|
18480
|
+
checkStatement(context, statement);
|
|
18481
|
+
const facts = guardFacts(context, statement);
|
|
18482
|
+
if (facts.size > 0) {
|
|
18483
|
+
context.pushNarrowing(facts);
|
|
18484
|
+
guards += 1;
|
|
18485
|
+
}
|
|
18486
|
+
}
|
|
18487
|
+
while (guards > 0) {
|
|
18488
|
+
context.popNarrowing();
|
|
18489
|
+
guards -= 1;
|
|
18252
18490
|
}
|
|
18253
|
-
const value = literalValue(right);
|
|
18254
|
-
return value === null ? null : { name: left.object.name, property: left.property, value };
|
|
18255
18491
|
}
|
|
18256
|
-
|
|
18257
|
-
|
|
18258
|
-
|
|
18492
|
+
|
|
18493
|
+
// ../compiler/src/checker/template.ts
|
|
18494
|
+
function parseInterpolation(segment) {
|
|
18495
|
+
const separator = segment.value.indexOf(":");
|
|
18496
|
+
const path = (separator === -1 ? segment.value : segment.value.slice(0, separator)).trim();
|
|
18497
|
+
const fallback = separator === -1 ? null : segment.value.slice(separator + 1).trim();
|
|
18498
|
+
const root = path.split(".")[0] ?? "";
|
|
18499
|
+
return { raw: segment.value, path, root, fallback, position: segment.position };
|
|
18259
18500
|
}
|
|
18260
|
-
function
|
|
18261
|
-
|
|
18262
|
-
|
|
18263
|
-
|
|
18264
|
-
|
|
18265
|
-
const
|
|
18266
|
-
|
|
18267
|
-
|
|
18268
|
-
if (member === null) {
|
|
18269
|
-
return null;
|
|
18270
|
-
}
|
|
18271
|
-
if (isAssignable(test.value, member) === keep) {
|
|
18272
|
-
matched.push(option);
|
|
18501
|
+
function collectInterpolations(segments) {
|
|
18502
|
+
return segments.filter((segment) => segment.kind === "interpolation").map(parseInterpolation);
|
|
18503
|
+
}
|
|
18504
|
+
function collectTemplateRoots(segments) {
|
|
18505
|
+
const roots = [];
|
|
18506
|
+
for (const interpolation of collectInterpolations(segments)) {
|
|
18507
|
+
if (interpolation.root.length > 0 && !roots.includes(interpolation.root)) {
|
|
18508
|
+
roots.push(interpolation.root);
|
|
18273
18509
|
}
|
|
18274
18510
|
}
|
|
18275
|
-
return
|
|
18511
|
+
return roots;
|
|
18276
18512
|
}
|
|
18277
|
-
function
|
|
18278
|
-
|
|
18279
|
-
if (test === null) {
|
|
18280
|
-
return null;
|
|
18281
|
-
}
|
|
18282
|
-
const narrowed = filterOptions(context, test, keep);
|
|
18283
|
-
return narrowed === null ? null : [test.name, narrowed];
|
|
18513
|
+
function templateLiteralText(segments) {
|
|
18514
|
+
return segments.map((segment) => segment.kind === "text" ? segment.value : `\${${segment.value}}`).join("");
|
|
18284
18515
|
}
|
|
18285
18516
|
|
|
18286
|
-
// ../compiler/src/checker/
|
|
18287
|
-
var
|
|
18288
|
-
var GUARD_TYPES = {
|
|
18517
|
+
// ../compiler/src/checker/expressions.ts
|
|
18518
|
+
var EXTENSION_RESULTS = {
|
|
18289
18519
|
boolean: BOOLEAN_TYPE,
|
|
18290
|
-
function: createFunction([], ANY_TYPE, 0, true),
|
|
18291
|
-
nil: NIL_TYPE,
|
|
18292
18520
|
number: NUMBER_TYPE,
|
|
18293
18521
|
string: STRING_TYPE,
|
|
18294
|
-
table: TABLE_TYPE
|
|
18295
|
-
thread: THREAD_TYPE,
|
|
18296
|
-
userdata: USERDATA_TYPE
|
|
18522
|
+
table: TABLE_TYPE
|
|
18297
18523
|
};
|
|
18298
|
-
function
|
|
18299
|
-
|
|
18524
|
+
function isNativeConstruction(context, expression) {
|
|
18525
|
+
const object = expression.object;
|
|
18526
|
+
if (expression.property !== NATIVE_CONSTRUCTOR || object.kind !== "identifier") {
|
|
18527
|
+
return false;
|
|
18528
|
+
}
|
|
18529
|
+
return nativeConstructor(context, object.name) !== null;
|
|
18300
18530
|
}
|
|
18301
|
-
function
|
|
18302
|
-
if (expression.
|
|
18303
|
-
|
|
18531
|
+
function checkMember(context, expression) {
|
|
18532
|
+
if (expression.object.kind === "identifier" && isMtaClassReference(context, expression.object.name)) {
|
|
18533
|
+
context.references.add(expression.object.name);
|
|
18534
|
+
return context.record(expression, resolveMtaStaticMember(context, expression.object.name, expression.property, expression.position)?.type ?? ANY_TYPE);
|
|
18535
|
+
}
|
|
18536
|
+
if (isNativeConstruction(context, expression) && expression.object.kind === "identifier") {
|
|
18537
|
+
const name = expression.object.name;
|
|
18538
|
+
const message = `Construct "${name}" with "new ${name}(...)". The "${name}.new(...)" form is not part of the language.`;
|
|
18539
|
+
context.report("check-native-constructor", message, expression.position);
|
|
18540
|
+
}
|
|
18541
|
+
const library = resolveLibraryMember(context, expression);
|
|
18542
|
+
if (library !== null) {
|
|
18543
|
+
checkExpression(context, expression.object);
|
|
18544
|
+
return context.record(expression, library);
|
|
18545
|
+
}
|
|
18546
|
+
const objectType = checkExpression(context, expression.object);
|
|
18547
|
+
if (objectType.kind === "record") {
|
|
18548
|
+
return context.record(expression, resolveRecordMember(context, objectType, expression));
|
|
18549
|
+
}
|
|
18550
|
+
if (objectType.kind === "union") {
|
|
18551
|
+
const option = resolveUnionMember(context, objectType, expression);
|
|
18552
|
+
if (option !== null) {
|
|
18553
|
+
return context.record(expression, option);
|
|
18554
|
+
}
|
|
18304
18555
|
}
|
|
18305
|
-
|
|
18306
|
-
|
|
18556
|
+
if (objectType.kind === "map" && isAssignable(STRING_TYPE, objectType.key)) {
|
|
18557
|
+
return context.record(expression, objectType.value);
|
|
18558
|
+
}
|
|
18559
|
+
const named2 = objectType.kind === "named" ? resolveNamedMember(context, objectType.name, expression) : null;
|
|
18560
|
+
if (named2 !== null) {
|
|
18561
|
+
return context.record(expression, named2);
|
|
18562
|
+
}
|
|
18563
|
+
const extension = extensionFor(objectType, expression.property);
|
|
18564
|
+
if (extension !== null) {
|
|
18565
|
+
reportExtensionForm(context, expression, extension);
|
|
18566
|
+
}
|
|
18567
|
+
return context.record(expression, extension === null ? ANY_TYPE : EXTENSION_RESULTS[extension.result]);
|
|
18307
18568
|
}
|
|
18308
|
-
function
|
|
18309
|
-
|
|
18310
|
-
const value = right.kind === "string-literal" ? GUARD_TYPES[right.value] : void 0;
|
|
18311
|
-
return name === null || value === void 0 ? null : [name, value];
|
|
18569
|
+
function countArguments(total) {
|
|
18570
|
+
return total === 1 ? "1 argument" : `${total} arguments`;
|
|
18312
18571
|
}
|
|
18313
|
-
function
|
|
18314
|
-
|
|
18315
|
-
|
|
18572
|
+
function checkSignature(context, args, signature, position2) {
|
|
18573
|
+
const argumentTypes = checkValueList(context, args, signature.parameters);
|
|
18574
|
+
if (argumentTypes.length < signature.minimumArguments) {
|
|
18575
|
+
const message = `This call expects at least ${countArguments(signature.minimumArguments)} but received ${argumentTypes.length}.`;
|
|
18576
|
+
context.report("check-argument-count", message, position2);
|
|
18316
18577
|
}
|
|
18317
|
-
|
|
18318
|
-
}
|
|
18319
|
-
|
|
18320
|
-
const declared = declaredType(context, name);
|
|
18321
|
-
if (declared !== null) {
|
|
18322
|
-
facts.set(name, withoutNil(declared));
|
|
18578
|
+
if (!signature.isVariadic && argumentTypes.length > signature.parameters.length) {
|
|
18579
|
+
const message = `This call expects at most ${countArguments(signature.parameters.length)} but received ${argumentTypes.length}.`;
|
|
18580
|
+
context.report("check-argument-count", message, position2);
|
|
18323
18581
|
}
|
|
18324
|
-
|
|
18325
|
-
|
|
18326
|
-
|
|
18327
|
-
|
|
18328
|
-
|
|
18329
|
-
|
|
18582
|
+
signature.parameters.forEach((parameter, index) => {
|
|
18583
|
+
const argumentType = argumentTypes[index];
|
|
18584
|
+
const argument = args[index];
|
|
18585
|
+
if (argumentType !== void 0 && argument !== void 0) {
|
|
18586
|
+
context.expectAssignable(argumentType, parameter, argument.position, `Argument ${index + 1}`);
|
|
18587
|
+
}
|
|
18588
|
+
});
|
|
18589
|
+
if (signature.isVariadic && signature.variadicType !== void 0) {
|
|
18590
|
+
for (let index = signature.parameters.length; index < argumentTypes.length; index += 1) {
|
|
18591
|
+
const argumentType = argumentTypes[index];
|
|
18592
|
+
const argument = args[Math.min(index, args.length - 1)];
|
|
18593
|
+
if (argumentType !== void 0 && argument !== void 0) {
|
|
18594
|
+
context.expectAssignable(argumentType, signature.variadicType, argument.position, `Argument ${index + 1}`);
|
|
18595
|
+
}
|
|
18330
18596
|
}
|
|
18331
18597
|
}
|
|
18598
|
+
return signature.returnType;
|
|
18332
18599
|
}
|
|
18333
|
-
function
|
|
18334
|
-
|
|
18335
|
-
|
|
18336
|
-
|
|
18600
|
+
function checkArguments(context, expression, calleeType) {
|
|
18601
|
+
if (calleeType.kind !== "function") {
|
|
18602
|
+
checkValueList(context, expression.args);
|
|
18603
|
+
reportNotCallable(context, expression, calleeType);
|
|
18604
|
+
return ANY_TYPE;
|
|
18337
18605
|
}
|
|
18606
|
+
return checkSignature(context, expression.args, calleeType, expression.position);
|
|
18338
18607
|
}
|
|
18339
|
-
function
|
|
18340
|
-
|
|
18341
|
-
|
|
18342
|
-
|
|
18608
|
+
function isSuperCall(expression) {
|
|
18609
|
+
return expression.method === null && expression.callee.kind === "identifier" && expression.callee.name === "super";
|
|
18610
|
+
}
|
|
18611
|
+
function isLegacySuperCall(expression) {
|
|
18612
|
+
return expression.method === "super" && expression.callee.kind === "identifier" && expression.callee.name === "self";
|
|
18613
|
+
}
|
|
18614
|
+
function checkMethodCall(context, expression, method, receiver) {
|
|
18615
|
+
if (receiver.kind !== "named") {
|
|
18616
|
+
checkValueList(context, expression.args);
|
|
18617
|
+
return ANY_TYPE;
|
|
18343
18618
|
}
|
|
18344
|
-
|
|
18345
|
-
|
|
18346
|
-
|
|
18619
|
+
const declared = context.declarations.lookupMember(receiver.name, method);
|
|
18620
|
+
if (declared?.type.kind === "function") {
|
|
18621
|
+
if (declared.deprecated === true) {
|
|
18622
|
+
context.warn("check-deprecated-use", `Member "${method}" is deprecated.`, expression.position);
|
|
18623
|
+
}
|
|
18624
|
+
return checkSignature(context, expression.args, declared.type, expression.position);
|
|
18347
18625
|
}
|
|
18348
|
-
if (
|
|
18349
|
-
|
|
18626
|
+
if (!isMtaElement(context, receiver.name)) {
|
|
18627
|
+
checkValueList(context, expression.args);
|
|
18628
|
+
return ANY_TYPE;
|
|
18350
18629
|
}
|
|
18351
|
-
|
|
18352
|
-
|
|
18353
|
-
|
|
18354
|
-
return;
|
|
18630
|
+
const member = resolveMtaMember(context, receiver.name, method, expression.position);
|
|
18631
|
+
if (member === null || member.type.kind !== "function") {
|
|
18632
|
+
checkValueList(context, expression.args);
|
|
18633
|
+
return ANY_TYPE;
|
|
18355
18634
|
}
|
|
18356
|
-
|
|
18357
|
-
|
|
18358
|
-
|
|
18635
|
+
return checkSignature(context, expression.args, member.type, expression.position);
|
|
18636
|
+
}
|
|
18637
|
+
function checkCall(context, expression) {
|
|
18638
|
+
if (isLegacySuperCall(expression)) {
|
|
18639
|
+
checkValueList(context, expression.args);
|
|
18640
|
+
context.report("check-invalid-super", 'Call "super(...)" directly instead of "self:super(...)".', expression.position);
|
|
18641
|
+
return context.record(expression, ANY_TYPE);
|
|
18359
18642
|
}
|
|
18360
|
-
if (expression
|
|
18361
|
-
|
|
18362
|
-
const missing = nilComparison(expression.left, expression.right);
|
|
18363
|
-
if (guard !== null) {
|
|
18364
|
-
facts.set(guard[0], guard[1]);
|
|
18365
|
-
} else if (missing !== null) {
|
|
18366
|
-
facts.set(missing, NIL_TYPE);
|
|
18367
|
-
} else {
|
|
18368
|
-
applyDiscriminant(context, expression.left, expression.right, true, facts);
|
|
18369
|
-
}
|
|
18370
|
-
return;
|
|
18643
|
+
if (isSuperCall(expression)) {
|
|
18644
|
+
return context.record(expression, checkSuperCall(context, expression));
|
|
18371
18645
|
}
|
|
18372
|
-
if (expression.
|
|
18373
|
-
|
|
18374
|
-
|
|
18375
|
-
|
|
18376
|
-
|
|
18377
|
-
|
|
18646
|
+
if (expression.method === null && expression.callee.kind === "identifier" && isMtaClassReference(context, expression.callee.name)) {
|
|
18647
|
+
context.references.add(expression.callee.name);
|
|
18648
|
+
const constructor = resolveMtaConstructor(context, expression.callee.name, expression.position);
|
|
18649
|
+
if (constructor === null) {
|
|
18650
|
+
checkValueList(context, expression.args);
|
|
18651
|
+
return context.record(expression, ANY_TYPE);
|
|
18378
18652
|
}
|
|
18653
|
+
return context.record(expression, checkSignature(context, expression.args, constructor, expression.position));
|
|
18379
18654
|
}
|
|
18380
|
-
|
|
18381
|
-
|
|
18382
|
-
const facts = /* @__PURE__ */ new Map();
|
|
18383
|
-
collectFacts(context, condition, facts);
|
|
18384
|
-
return facts;
|
|
18385
|
-
}
|
|
18386
|
-
function alwaysExits(body) {
|
|
18387
|
-
const last = body[body.length - 1];
|
|
18388
|
-
if (last === void 0) {
|
|
18389
|
-
return false;
|
|
18655
|
+
if (expression.method === null) {
|
|
18656
|
+
context.calledMembers.add(expression.callee);
|
|
18390
18657
|
}
|
|
18391
|
-
|
|
18392
|
-
|
|
18658
|
+
const calleeType = checkExpression(context, expression.callee);
|
|
18659
|
+
if (expression.method !== null) {
|
|
18660
|
+
return context.record(expression, checkMethodCall(context, expression, expression.method, calleeType));
|
|
18393
18661
|
}
|
|
18394
|
-
if (
|
|
18395
|
-
|
|
18662
|
+
if (expression.callee.kind === "identifier" && context.binder.isBuiltinReference(expression.callee.name)) {
|
|
18663
|
+
checkEventUsage(context, expression);
|
|
18664
|
+
const specialized = specializeEventCall(context, expression, calleeType);
|
|
18665
|
+
return context.record(expression, checkArguments(context, expression, specialized ?? calleeType));
|
|
18396
18666
|
}
|
|
18397
|
-
return
|
|
18667
|
+
return context.record(expression, checkArguments(context, expression, calleeType));
|
|
18398
18668
|
}
|
|
18399
|
-
function
|
|
18400
|
-
const
|
|
18401
|
-
|
|
18402
|
-
|
|
18669
|
+
function checkTable(context, expression) {
|
|
18670
|
+
const valueTypes = expression.fields.map((field2) => {
|
|
18671
|
+
if (field2.key !== null) {
|
|
18672
|
+
checkExpression(context, field2.key);
|
|
18673
|
+
}
|
|
18674
|
+
return checkExpression(context, field2.value);
|
|
18675
|
+
});
|
|
18676
|
+
const isRecord = expression.fields.every((field2) => field2.name !== null);
|
|
18677
|
+
if (isRecord) {
|
|
18678
|
+
const members = /* @__PURE__ */ new Map();
|
|
18679
|
+
expression.fields.forEach((field2, index) => {
|
|
18680
|
+
if (field2.name !== null) {
|
|
18681
|
+
members.set(field2.name, valueTypes[index] ?? ANY_TYPE);
|
|
18682
|
+
}
|
|
18683
|
+
});
|
|
18684
|
+
return context.record(expression, createLiteralRecord(members));
|
|
18403
18685
|
}
|
|
18404
|
-
|
|
18686
|
+
const isArray = expression.fields.every((field2) => field2.key === null && field2.name === null);
|
|
18687
|
+
if (!isArray) {
|
|
18688
|
+
return context.record(expression, TABLE_TYPE);
|
|
18689
|
+
}
|
|
18690
|
+
return context.record(expression, createArray(createUnion(valueTypes)));
|
|
18405
18691
|
}
|
|
18406
|
-
function
|
|
18407
|
-
const
|
|
18408
|
-
|
|
18409
|
-
|
|
18410
|
-
|
|
18692
|
+
function checkTemplate(context, expression) {
|
|
18693
|
+
for (const interpolation of collectInterpolations(expression.segments)) {
|
|
18694
|
+
if (interpolation.root.length === 0) {
|
|
18695
|
+
const message = 'Template interpolation is empty. Write "${name}", "${object.field}", or "${name:fallback}".';
|
|
18696
|
+
context.report("check-empty-interpolation", message, interpolation.position);
|
|
18697
|
+
continue;
|
|
18698
|
+
}
|
|
18699
|
+
if (context.binder.lookup(interpolation.root) === null) {
|
|
18700
|
+
const message = `Template interpolation "${interpolation.path}" refers to "${interpolation.root}", which is not in scope.`;
|
|
18701
|
+
context.report("check-unknown-template-root", message, interpolation.position);
|
|
18411
18702
|
}
|
|
18412
|
-
return facts;
|
|
18413
18703
|
}
|
|
18414
|
-
|
|
18415
|
-
|
|
18416
|
-
|
|
18417
|
-
|
|
18418
|
-
|
|
18419
|
-
|
|
18704
|
+
return context.record(expression, STRING_TYPE);
|
|
18705
|
+
}
|
|
18706
|
+
function checkMultiValueExpression(context, expression, expected = null) {
|
|
18707
|
+
switch (expression.kind) {
|
|
18708
|
+
case "nil-literal":
|
|
18709
|
+
return context.record(expression, NIL_TYPE);
|
|
18710
|
+
case "boolean-literal":
|
|
18711
|
+
return context.record(expression, createBooleanLiteral(expression.value));
|
|
18712
|
+
case "number-literal":
|
|
18713
|
+
return context.record(expression, createNumberLiteral(expression.value));
|
|
18714
|
+
case "string-literal":
|
|
18715
|
+
return context.record(expression, createStringLiteral(expression.value));
|
|
18716
|
+
case "template-literal":
|
|
18717
|
+
return checkTemplate(context, expression);
|
|
18718
|
+
case "vararg-expression":
|
|
18719
|
+
return context.record(expression, ANY_TYPE);
|
|
18720
|
+
case "identifier": {
|
|
18721
|
+
context.references.add(expression.name);
|
|
18722
|
+
const symbol = context.binder.lookup(expression.name);
|
|
18723
|
+
if (symbol === null) {
|
|
18724
|
+
checkGlobalReference(context, expression.name, expression.position);
|
|
18725
|
+
}
|
|
18726
|
+
return context.record(expression, context.narrowedType(expression.name) ?? symbol?.type ?? ANY_TYPE);
|
|
18727
|
+
}
|
|
18728
|
+
case "member-expression":
|
|
18729
|
+
return checkMember(context, expression);
|
|
18730
|
+
case "index-expression": {
|
|
18731
|
+
const objectType = checkExpression(context, expression.object);
|
|
18732
|
+
const indexType = checkExpression(context, expression.index);
|
|
18733
|
+
if (objectType.kind === "map") {
|
|
18734
|
+
context.expectAssignable(indexType, objectType.key, expression.index.position, "Key");
|
|
18735
|
+
return context.record(expression, objectType.value);
|
|
18736
|
+
}
|
|
18737
|
+
return context.record(expression, objectType.kind === "array" ? objectType.element : ANY_TYPE);
|
|
18738
|
+
}
|
|
18739
|
+
case "call-expression":
|
|
18740
|
+
return checkCall(context, expression);
|
|
18741
|
+
case "new-expression":
|
|
18742
|
+
return context.record(expression, checkNewExpression(context, expression));
|
|
18743
|
+
case "table-expression":
|
|
18744
|
+
return checkTable(context, expression);
|
|
18745
|
+
case "function-expression": {
|
|
18746
|
+
const type = buildFunctionType(context, expression.parameters, expression.returnAnnotation, contextualFunction(expected));
|
|
18747
|
+
checkFunctionBody(context, expression.parameters, expression.returnAnnotation, expression.body, type, null);
|
|
18748
|
+
return context.record(expression, type);
|
|
18420
18749
|
}
|
|
18750
|
+
case "binary-expression": {
|
|
18751
|
+
const left = checkExpression(context, expression.left);
|
|
18752
|
+
const right = checkExpression(context, expression.right);
|
|
18753
|
+
return checkBinary(context, expression.operator, left, right, expression);
|
|
18754
|
+
}
|
|
18755
|
+
case "unary-expression":
|
|
18756
|
+
return checkUnary(context, expression.operator, checkExpression(context, expression.operand), expression);
|
|
18757
|
+
default:
|
|
18758
|
+
return context.record(expression, checkExpression(context, expression.expression, expected));
|
|
18421
18759
|
}
|
|
18422
|
-
if (condition.kind === "binary-expression" && condition.operator === "~=") {
|
|
18423
|
-
applyDiscriminant(context, condition.left, condition.right, true, facts);
|
|
18424
|
-
}
|
|
18425
|
-
if (condition.kind === "unary-expression" && condition.operator === "not" && condition.operand.kind === "identifier") {
|
|
18426
|
-
present2(context, condition.operand.name, facts);
|
|
18427
|
-
}
|
|
18428
|
-
return facts;
|
|
18429
18760
|
}
|
|
18430
|
-
|
|
18431
|
-
|
|
18432
|
-
var ITERATOR_FUNCTIONS = /* @__PURE__ */ new Set(["pairs", "ipairs"]);
|
|
18433
|
-
function checkBlock(context, body) {
|
|
18434
|
-
context.binder.pushScope();
|
|
18435
|
-
checkStatements(context, body);
|
|
18436
|
-
context.binder.popScope();
|
|
18761
|
+
function checkExpression(context, expression, expected = null) {
|
|
18762
|
+
return firstValueOf(checkMultiValueExpression(context, expression, expected));
|
|
18437
18763
|
}
|
|
18438
|
-
function
|
|
18439
|
-
|
|
18440
|
-
|
|
18441
|
-
|
|
18764
|
+
function checkValueList(context, values, expected = []) {
|
|
18765
|
+
const types = [];
|
|
18766
|
+
values.forEach((value, index) => {
|
|
18767
|
+
const type = checkMultiValueExpression(context, value, expected[index] ?? null);
|
|
18768
|
+
if (index === values.length - 1) {
|
|
18769
|
+
types.push(...valuesOf(type));
|
|
18770
|
+
return;
|
|
18771
|
+
}
|
|
18772
|
+
types.push(firstValueOf(type));
|
|
18773
|
+
});
|
|
18774
|
+
return types;
|
|
18442
18775
|
}
|
|
18443
|
-
|
|
18444
|
-
|
|
18445
|
-
|
|
18446
|
-
|
|
18776
|
+
|
|
18777
|
+
// ../compiler/src/checker/classes.ts
|
|
18778
|
+
function fieldType(context, member) {
|
|
18779
|
+
const valueType = member.value === null ? null : checkExpression(context, member.value);
|
|
18780
|
+
if (member.annotation === null) {
|
|
18781
|
+
return valueType === null ? ANY_TYPE : widenInferred(valueType);
|
|
18447
18782
|
}
|
|
18448
|
-
|
|
18449
|
-
|
|
18783
|
+
const declared = context.resolveAnnotation(member.annotation);
|
|
18784
|
+
if (valueType !== null && member.value !== null) {
|
|
18785
|
+
context.expectAssignable(valueType, declared, member.value.position, `Field "${member.name}"`);
|
|
18450
18786
|
}
|
|
18451
|
-
|
|
18452
|
-
checkNarrowedBlock(context, clauses.length === 1 && only !== void 0 ? negatedFacts(context, only.condition) : /* @__PURE__ */ new Map(), alternate);
|
|
18787
|
+
return declared;
|
|
18453
18788
|
}
|
|
18454
|
-
function
|
|
18455
|
-
|
|
18456
|
-
|
|
18457
|
-
const type = checkExpression(context, bound);
|
|
18458
|
-
if (!isNumeric(type)) {
|
|
18459
|
-
context.report("check-invalid-operand", `Numeric "for" expects "number" but received "${typeToString(type)}".`, bound.position);
|
|
18460
|
-
}
|
|
18789
|
+
function checkFieldName(context, member) {
|
|
18790
|
+
if (member.name !== "constructor") {
|
|
18791
|
+
return;
|
|
18461
18792
|
}
|
|
18462
|
-
|
|
18463
|
-
context.
|
|
18464
|
-
checkStatements(context, statement.body);
|
|
18465
|
-
context.binder.popScope();
|
|
18793
|
+
const message = 'A "constructor" is the class constructor, not a field. Write it as "constructor = function (...) ... end" or rename the field.';
|
|
18794
|
+
context.report("check-invalid-constructor", message, member.position);
|
|
18466
18795
|
}
|
|
18467
|
-
function
|
|
18468
|
-
const [
|
|
18469
|
-
if (
|
|
18470
|
-
return [];
|
|
18471
|
-
}
|
|
18472
|
-
const [source] = iterator.args;
|
|
18473
|
-
if (!ITERATOR_FUNCTIONS.has(iterator.callee.name) || source === void 0) {
|
|
18474
|
-
return [];
|
|
18796
|
+
function syntheticMethodType(context, member, fieldTypes) {
|
|
18797
|
+
const fieldType2 = [...fieldTypes].find(([field2]) => field2.position.offset === member.position.offset)?.[1] ?? ANY_TYPE;
|
|
18798
|
+
if (member.generated?.kind === "lazy") {
|
|
18799
|
+
return createFunction([], fieldType2);
|
|
18475
18800
|
}
|
|
18476
|
-
|
|
18477
|
-
|
|
18478
|
-
return iterator.callee.name === "ipairs" ? [NUMBER_TYPE, type.value] : [type.key, type.value];
|
|
18801
|
+
if (member.generated === void 0) {
|
|
18802
|
+
return member.parameters.length === 0 ? createFunction([], fieldType2) : createFunction([fieldType2], VOID_TYPE);
|
|
18479
18803
|
}
|
|
18480
|
-
return
|
|
18481
|
-
}
|
|
18482
|
-
function checkGenericFor(context, statement) {
|
|
18483
|
-
statement.iterators.forEach((iterator) => checkExpression(context, iterator));
|
|
18484
|
-
const iterated = iteratedTypes(context, statement.iterators);
|
|
18485
|
-
context.binder.pushScope();
|
|
18486
|
-
statement.variables.forEach((variable, index) => {
|
|
18487
|
-
const inferred = iterated[index] ?? ANY_TYPE;
|
|
18488
|
-
const declared = variable.annotation === null ? inferred : context.resolveAnnotation(variable.annotation);
|
|
18489
|
-
context.binder.declare({ name: variable.name, type: declared, isLocal: true, position: variable.position, origin: "local" });
|
|
18490
|
-
});
|
|
18491
|
-
checkStatements(context, statement.body);
|
|
18492
|
-
context.binder.popScope();
|
|
18804
|
+
return buildFunctionType(context, member.parameters, member.returnAnnotation);
|
|
18493
18805
|
}
|
|
18494
|
-
|
|
18495
|
-
|
|
18496
|
-
|
|
18497
|
-
|
|
18498
|
-
|
|
18499
|
-
|
|
18500
|
-
declaration.parameters.forEach((parameter, index) => {
|
|
18501
|
-
const type = context.resolveAnnotation(parameter.annotation);
|
|
18502
|
-
if (parameter.name.length > 0 && names.has(parameter.name)) {
|
|
18503
|
-
context.report("check-duplicate-event-parameter", `Event "${declaration.name}" declares parameter "${parameter.name}" more than once.`, parameter.position);
|
|
18504
|
-
invalid = true;
|
|
18505
|
-
}
|
|
18506
|
-
if (parameter.isVararg && index !== declaration.parameters.length - 1) {
|
|
18507
|
-
context.report("check-invalid-event-parameter", `Variadic parameter of event "${declaration.name}" must be last.`, parameter.position);
|
|
18508
|
-
invalid = true;
|
|
18509
|
-
}
|
|
18510
|
-
if (parameter.name.length > 0) {
|
|
18511
|
-
names.add(parameter.name);
|
|
18806
|
+
function registerMembers(context, info, statement) {
|
|
18807
|
+
const fieldTypes = /* @__PURE__ */ new Map();
|
|
18808
|
+
for (const member of statement.members) {
|
|
18809
|
+
if (member.kind === "class-field") {
|
|
18810
|
+
checkFieldName(context, member);
|
|
18811
|
+
fieldTypes.set(member, fieldType(context, member));
|
|
18512
18812
|
}
|
|
18513
|
-
parameters.push({ name: parameter.name, type, isVariadic: parameter.isVararg, position: parameter.position });
|
|
18514
|
-
});
|
|
18515
|
-
return { parameters, invalid };
|
|
18516
|
-
}
|
|
18517
|
-
function checkEventDeclaration(context, declaration) {
|
|
18518
|
-
if (context.declarations.lookupEvent(declaration.name) !== null) {
|
|
18519
|
-
context.report("check-duplicate-event", `Event "${declaration.name}" is already defined.`, declaration.position);
|
|
18520
|
-
return;
|
|
18521
|
-
}
|
|
18522
|
-
let invalid = declaration.name.length === 0;
|
|
18523
|
-
if (invalid) {
|
|
18524
|
-
context.report("check-invalid-event-name", "An event name cannot be empty.", declaration.position);
|
|
18525
|
-
}
|
|
18526
|
-
const resolved2 = resolveParameters(context, declaration);
|
|
18527
|
-
invalid = invalid || resolved2.invalid;
|
|
18528
|
-
if (declaration.returnAnnotation !== null && context.resolveAnnotation(declaration.returnAnnotation).kind !== "void") {
|
|
18529
|
-
context.report("check-event-return-type", `Event "${declaration.name}" can only declare a "void" return type.`, declaration.returnAnnotation.position);
|
|
18530
|
-
invalid = true;
|
|
18531
18813
|
}
|
|
18532
|
-
|
|
18533
|
-
|
|
18534
|
-
|
|
18535
|
-
|
|
18536
|
-
|
|
18537
|
-
|
|
18814
|
+
const generated = expandClassDecorators(context, statement, fieldTypes);
|
|
18815
|
+
for (const member of [...statement.members, ...generated]) {
|
|
18816
|
+
const type = member.kind === "class-field" ? fieldTypes.get(member) ?? ANY_TYPE : member.isSynthetic ? syntheticMethodType(context, member, fieldTypes) : buildFunctionType(context, member.parameters, member.returnAnnotation);
|
|
18817
|
+
const decorators = member.decorators.map((decorator) => decorator.name);
|
|
18818
|
+
info.members.set(member.name, {
|
|
18819
|
+
name: member.name,
|
|
18820
|
+
type,
|
|
18821
|
+
isMethod: member.kind === "class-method",
|
|
18822
|
+
position: member.position,
|
|
18823
|
+
readOnly: decorators.includes("ReadOnly"),
|
|
18824
|
+
deprecated: decorators.includes("Deprecated")
|
|
18538
18825
|
});
|
|
18539
18826
|
}
|
|
18827
|
+
return generated;
|
|
18540
18828
|
}
|
|
18541
|
-
|
|
18542
|
-
|
|
18543
|
-
|
|
18544
|
-
function minimumArguments(context, parameters) {
|
|
18545
|
-
const index = parameters.findIndex((parameter) => {
|
|
18546
|
-
const type = context.resolveAnnotation(parameter.annotation);
|
|
18547
|
-
return parameter.isVararg || type.kind === "optional" || type.kind === "any";
|
|
18548
|
-
});
|
|
18549
|
-
return index === -1 ? parameters.length : index;
|
|
18550
|
-
}
|
|
18551
|
-
function buildFunctionType(context, parameters, returnAnnotation, contextual = null) {
|
|
18552
|
-
const isVariadic = parameters.some((parameter) => parameter.isVararg);
|
|
18553
|
-
const types = parameters.filter((parameter) => !parameter.isVararg).map((parameter, index) => parameter.annotation === null ? contextual?.parameters[index] ?? ANY_TYPE : context.resolveAnnotation(parameter.annotation));
|
|
18554
|
-
return createFunction(types, context.resolveAnnotation(returnAnnotation), minimumArguments(context, parameters), isVariadic);
|
|
18555
|
-
}
|
|
18556
|
-
function checkFunctionBody(context, parameters, returnAnnotation, body, signature, selfType) {
|
|
18557
|
-
context.binder.pushScope();
|
|
18558
|
-
context.pushReturnType(returnAnnotation === null ? null : context.resolveAnnotation(returnAnnotation));
|
|
18559
|
-
if (selfType !== null) {
|
|
18560
|
-
context.binder.declare({ name: "self", type: selfType, isLocal: true, position: ORIGIN });
|
|
18829
|
+
function declareBuilder(context, info, statement) {
|
|
18830
|
+
if (!statement.decorators.some((decorator) => decorator.name === "Builder")) {
|
|
18831
|
+
return;
|
|
18561
18832
|
}
|
|
18562
|
-
|
|
18563
|
-
|
|
18564
|
-
|
|
18565
|
-
|
|
18566
|
-
|
|
18567
|
-
|
|
18833
|
+
const name = `${statement.name}Builder`;
|
|
18834
|
+
const members = /* @__PURE__ */ new Map();
|
|
18835
|
+
for (const field2 of statement.members) {
|
|
18836
|
+
if (field2.kind === "class-field") {
|
|
18837
|
+
const type = info.members.get(field2.name)?.type ?? ANY_TYPE;
|
|
18838
|
+
const method = `with${field2.name[0]?.toUpperCase()}${field2.name.slice(1)}`;
|
|
18839
|
+
members.set(method, { name: method, type: { kind: "function", parameters: [type], minimumArguments: 1, isVariadic: false, returnType: createNamed(name) }, isMethod: true, position: field2.position });
|
|
18568
18840
|
}
|
|
18569
18841
|
}
|
|
18570
|
-
|
|
18571
|
-
|
|
18572
|
-
|
|
18573
|
-
signature.returnType = inferred;
|
|
18574
|
-
}
|
|
18575
|
-
context.binder.popScope();
|
|
18842
|
+
members.set("build", { name: "build", type: { kind: "function", parameters: [], minimumArguments: 0, isVariadic: false, returnType: createNamed(statement.name) }, isMethod: true, position: statement.position });
|
|
18843
|
+
context.declarations.declareClass({ name, superClass: null, interfaces: [], members, position: statement.position });
|
|
18844
|
+
context.declareModuleGlobal({ name, type: createNamed(name), isLocal: false, position: statement.position });
|
|
18576
18845
|
}
|
|
18577
|
-
function
|
|
18578
|
-
|
|
18579
|
-
|
|
18846
|
+
function checkMethodBody(context, info, member) {
|
|
18847
|
+
const explicitSelf = member.parameters.find((parameter) => parameter.name === "self");
|
|
18848
|
+
if (explicitSelf !== void 0) {
|
|
18849
|
+
context.report("check-explicit-self-parameter", 'Class methods receive "self" automatically; remove it from the parameter list.', explicitSelf.position);
|
|
18580
18850
|
}
|
|
18581
|
-
|
|
18851
|
+
const signature = info.members.get(member.name)?.type;
|
|
18852
|
+
if (signature?.kind !== "function") {
|
|
18853
|
+
return;
|
|
18854
|
+
}
|
|
18855
|
+
context.pushClassMethod({ className: info.name, methodName: member.name });
|
|
18856
|
+
checkFunctionBody(context, member.parameters, member.returnAnnotation, member.body, signature, createNamed(info.name));
|
|
18857
|
+
context.popClassMethod();
|
|
18582
18858
|
}
|
|
18583
|
-
function
|
|
18584
|
-
|
|
18585
|
-
|
|
18586
|
-
|
|
18587
|
-
|
|
18588
|
-
context.
|
|
18589
|
-
|
|
18590
|
-
const inferred = context.allowNil || value === void 0 ? ANY_TYPE : widenInferred(valueType);
|
|
18591
|
-
context.binder.declare({ name: declaration.name, type: inferred, isLocal: true, position: declaration.position, origin: "local" });
|
|
18592
|
-
return;
|
|
18593
|
-
}
|
|
18594
|
-
const declared = context.resolveAnnotation(declaration.annotation);
|
|
18595
|
-
if (value !== void 0) {
|
|
18596
|
-
context.expectAssignable(valueType, declared, value.position, `Variable "${declaration.name}"`);
|
|
18859
|
+
function resolveSuperClass(context, statement) {
|
|
18860
|
+
if (statement.superClass === null) {
|
|
18861
|
+
return null;
|
|
18862
|
+
}
|
|
18863
|
+
if (context.declarations.lookupClass(statement.superClass) !== null) {
|
|
18864
|
+
if (context.isAmbientClass(statement.superClass)) {
|
|
18865
|
+
context.noteExternalReference(statement.superClass, statement.position);
|
|
18597
18866
|
}
|
|
18598
|
-
|
|
18599
|
-
}
|
|
18867
|
+
return statement.superClass;
|
|
18868
|
+
}
|
|
18869
|
+
if (context.mtaClasses !== null && context.mtaClasses.lookupClass(statement.superClass) !== null) {
|
|
18870
|
+
const message2 = `Class "${statement.name}" cannot extend native MTA class "${statement.superClass}".`;
|
|
18871
|
+
context.report("check-native-class-inheritance", message2, statement.position);
|
|
18872
|
+
return null;
|
|
18873
|
+
}
|
|
18874
|
+
const message = `Class "${statement.name}" extends "${statement.superClass}", which is not defined.`;
|
|
18875
|
+
context.noteExternalReference(statement.superClass, statement.position);
|
|
18876
|
+
context.report("check-unknown-class", message, statement.position);
|
|
18877
|
+
return null;
|
|
18600
18878
|
}
|
|
18601
|
-
function
|
|
18602
|
-
if (
|
|
18603
|
-
|
|
18604
|
-
|
|
18605
|
-
}
|
|
18606
|
-
return;
|
|
18879
|
+
function declareClassInfo(context, statement) {
|
|
18880
|
+
if (context.declarations.lookupClass(statement.name) !== null) {
|
|
18881
|
+
context.report("check-duplicate-class", `Class "${statement.name}" is already defined.`, statement.position);
|
|
18882
|
+
return null;
|
|
18607
18883
|
}
|
|
18608
|
-
if (
|
|
18609
|
-
context.report("check-
|
|
18884
|
+
if (context.mtaClasses !== null && context.mtaClasses.lookupClass(statement.name) !== null) {
|
|
18885
|
+
context.report("check-duplicate-class", `Class "${statement.name}" is reserved by MTA when OOP is enabled.`, statement.position);
|
|
18886
|
+
return null;
|
|
18610
18887
|
}
|
|
18888
|
+
const info = {
|
|
18889
|
+
name: statement.name,
|
|
18890
|
+
superClass: null,
|
|
18891
|
+
interfaces: statement.interfaces,
|
|
18892
|
+
members: /* @__PURE__ */ new Map(),
|
|
18893
|
+
position: statement.position
|
|
18894
|
+
};
|
|
18895
|
+
context.declarations.declareClass(info);
|
|
18896
|
+
context.declareModuleGlobal({ name: info.name, type: createNamed(info.name), isLocal: false, position: statement.position });
|
|
18897
|
+
return info;
|
|
18611
18898
|
}
|
|
18612
|
-
function
|
|
18613
|
-
const
|
|
18614
|
-
|
|
18615
|
-
|
|
18616
|
-
|
|
18617
|
-
|
|
18618
|
-
|
|
18619
|
-
const object = context.typeOf(target.object);
|
|
18620
|
-
const frame = context.currentClassMethod();
|
|
18621
|
-
if (object.kind === "named") {
|
|
18622
|
-
const member = context.declarations.lookupMember(object.name, target.property);
|
|
18623
|
-
if (member?.readOnly === true && (frame === null || frame.className !== object.name)) {
|
|
18624
|
-
context.report("check-readonly-assignment", `Field "${target.property}" is read-only outside class "${object.name}".`, target.position);
|
|
18625
|
-
}
|
|
18626
|
-
}
|
|
18627
|
-
}
|
|
18628
|
-
if (statement.operator !== "=") {
|
|
18629
|
-
checkCompoundOperand(context, statement.operator, targetType, target.position);
|
|
18630
|
-
if (statement.values.length > 0) {
|
|
18631
|
-
checkCompoundOperand(context, statement.operator, valueType, value?.position ?? target.position);
|
|
18632
|
-
}
|
|
18633
|
-
return;
|
|
18634
|
-
}
|
|
18635
|
-
if (target.kind === "identifier" && context.binder.lookup(target.name) === null) {
|
|
18636
|
-
context.declareModuleGlobal({ name: target.name, type: valueType, isLocal: false, position: target.position });
|
|
18637
|
-
return;
|
|
18638
|
-
}
|
|
18639
|
-
const declared = target.kind === "identifier" ? context.binder.lookup(target.name)?.type ?? targetType : targetType;
|
|
18640
|
-
if (value !== void 0) {
|
|
18641
|
-
context.expectAssignable(valueType, declared, value.position, "Assignment");
|
|
18642
|
-
}
|
|
18643
|
-
if (target.kind === "identifier") {
|
|
18644
|
-
context.forgetNarrowing(target.name);
|
|
18645
|
-
}
|
|
18646
|
-
});
|
|
18899
|
+
function declareLocalClass(context, statement) {
|
|
18900
|
+
const info = declareClassInfo(context, statement);
|
|
18901
|
+
if (info === null) {
|
|
18902
|
+
return null;
|
|
18903
|
+
}
|
|
18904
|
+
info.superClass = resolveSuperClass(context, statement);
|
|
18905
|
+
return info;
|
|
18647
18906
|
}
|
|
18648
|
-
function
|
|
18649
|
-
const
|
|
18650
|
-
if (
|
|
18651
|
-
|
|
18652
|
-
|
|
18653
|
-
|
|
18654
|
-
|
|
18655
|
-
|
|
18907
|
+
function checkClassDeclaration(context, statement) {
|
|
18908
|
+
const info = context.takePredeclaredClass(statement.name) ?? declareLocalClass(context, statement);
|
|
18909
|
+
if (info === null) {
|
|
18910
|
+
return;
|
|
18911
|
+
}
|
|
18912
|
+
context.generatedMembers.set(statement, registerMembers(context, info, statement));
|
|
18913
|
+
declareBuilder(context, info, statement);
|
|
18914
|
+
checkInterfaces(context, info);
|
|
18915
|
+
checkOverrides(context, info, statement);
|
|
18916
|
+
for (const member of statement.members) {
|
|
18917
|
+
if (member.kind === "class-method") {
|
|
18918
|
+
checkMethodBody(context, info, member);
|
|
18656
18919
|
}
|
|
18657
18920
|
}
|
|
18658
|
-
context.record(statement.name, type);
|
|
18659
|
-
checkFunctionBody(context, statement.parameters, statement.returnAnnotation, statement.body, type, statement.isMethod ? ANY_TYPE : null);
|
|
18660
18921
|
}
|
|
18661
|
-
function
|
|
18662
|
-
|
|
18663
|
-
|
|
18664
|
-
if (expected === null) {
|
|
18665
|
-
context.inferReturnType(createTuple(valueTypes));
|
|
18922
|
+
function checkInterfaceDeclaration(context, statement) {
|
|
18923
|
+
if (context.declarations.lookupInterface(statement.name) !== null) {
|
|
18924
|
+
context.report("check-duplicate-interface", `Interface "${statement.name}" is already defined.`, statement.position);
|
|
18666
18925
|
return;
|
|
18667
18926
|
}
|
|
18668
|
-
|
|
18669
|
-
|
|
18927
|
+
const members = /* @__PURE__ */ new Map();
|
|
18928
|
+
for (const name of new Set(statement.superInterfaces)) {
|
|
18929
|
+
const parent = context.declarations.lookupInterface(name);
|
|
18930
|
+
if (name === statement.name || context.declarations.interfaceExtends(name, statement.name)) {
|
|
18931
|
+
context.report("check-interface-cycle", `Interface "${statement.name}" creates an inheritance cycle through "${name}".`, statement.position);
|
|
18932
|
+
} else if (parent === null) {
|
|
18933
|
+
context.noteExternalReference(name, statement.position);
|
|
18934
|
+
context.report("check-unknown-interface", `Interface "${statement.name}" extends "${name}", which is not defined.`, statement.position);
|
|
18935
|
+
} else if (context.isAmbientInterface(name)) {
|
|
18936
|
+
context.noteExternalReference(name, statement.position);
|
|
18937
|
+
}
|
|
18670
18938
|
}
|
|
18671
|
-
if (
|
|
18672
|
-
|
|
18673
|
-
|
|
18939
|
+
if (new Set(statement.superInterfaces).size !== statement.superInterfaces.length) {
|
|
18940
|
+
context.report("check-duplicate-interface-parent", `Interface "${statement.name}" extends the same interface more than once.`, statement.position);
|
|
18941
|
+
}
|
|
18942
|
+
for (const member of statement.members) {
|
|
18943
|
+
const type = member.kind === "interface-field" ? context.resolveAnnotation(member.annotation) : buildFunctionType(context, member.parameters, member.returnAnnotation);
|
|
18944
|
+
const info = { name: member.name, type, isMethod: member.kind === "interface-method", position: member.position };
|
|
18945
|
+
if (members.has(member.name)) {
|
|
18946
|
+
context.report("check-duplicate-interface-member", `Interface "${statement.name}" declares member "${member.name}" more than once.`, member.position);
|
|
18947
|
+
} else {
|
|
18948
|
+
members.set(member.name, info);
|
|
18674
18949
|
}
|
|
18675
|
-
return;
|
|
18676
18950
|
}
|
|
18677
|
-
|
|
18678
|
-
|
|
18679
|
-
|
|
18680
|
-
|
|
18681
|
-
|
|
18951
|
+
const inherited = /* @__PURE__ */ new Map();
|
|
18952
|
+
for (const parent of statement.superInterfaces) {
|
|
18953
|
+
for (const member of context.declarations.collectInterfaceContract(parent)) {
|
|
18954
|
+
const existing = members.get(member.name) ?? inherited.get(member.name);
|
|
18955
|
+
if (existing !== void 0 && (existing.isMethod !== member.isMethod || typeToString(existing.type) !== typeToString(member.type))) {
|
|
18956
|
+
context.report("check-conflicting-interface-member", `Interface "${statement.name}" inherits conflicting declarations of "${member.name}".`, statement.position);
|
|
18957
|
+
} else if (existing === void 0) {
|
|
18958
|
+
inherited.set(member.name, member);
|
|
18959
|
+
}
|
|
18682
18960
|
}
|
|
18683
|
-
valueTypes.forEach((type, index) => {
|
|
18684
|
-
const value2 = statement.values[Math.min(index, statement.values.length - 1)];
|
|
18685
|
-
context.expectAssignable(type, expected.elements[index] ?? ANY_TYPE, value2?.position ?? statement.position, `Return value ${index + 1}`);
|
|
18686
|
-
});
|
|
18687
|
-
return;
|
|
18688
18961
|
}
|
|
18689
|
-
|
|
18690
|
-
|
|
18691
|
-
|
|
18962
|
+
context.declarations.declareInterface({
|
|
18963
|
+
name: statement.name,
|
|
18964
|
+
superInterfaces: statement.superInterfaces,
|
|
18965
|
+
members,
|
|
18966
|
+
position: statement.position
|
|
18967
|
+
});
|
|
18968
|
+
}
|
|
18969
|
+
function checkEnumDeclaration(context, statement) {
|
|
18970
|
+
const existing = context.declarations.lookupEnum(statement.name);
|
|
18971
|
+
const shadowsAmbient = statement.isLocal && existing?.isLocal !== true && context.isAmbientEnum(statement.name);
|
|
18972
|
+
if (existing !== null && !shadowsAmbient) {
|
|
18973
|
+
context.report("check-duplicate-enum", `Enum "${statement.name}" is already defined.`, statement.position);
|
|
18692
18974
|
return;
|
|
18693
18975
|
}
|
|
18694
|
-
const
|
|
18695
|
-
|
|
18696
|
-
if (
|
|
18697
|
-
context.
|
|
18698
|
-
|
|
18976
|
+
const members = statement.members.map((member) => member.name);
|
|
18977
|
+
context.declarations.declareEnum({ name: statement.name, members, isLocal: statement.isLocal, position: statement.position });
|
|
18978
|
+
if (statement.isLocal) {
|
|
18979
|
+
context.binder.declare({ name: statement.name, type: createNamed(statement.name), isLocal: true, position: statement.position, origin: "local" });
|
|
18980
|
+
} else {
|
|
18981
|
+
context.declareModuleGlobal({ name: statement.name, type: createNamed(statement.name), isLocal: false, position: statement.position });
|
|
18699
18982
|
}
|
|
18700
|
-
context.expectAssignable(first, expected, value.position, "Return value");
|
|
18701
18983
|
}
|
|
18702
|
-
|
|
18703
|
-
|
|
18704
|
-
|
|
18705
|
-
|
|
18706
|
-
|
|
18984
|
+
|
|
18985
|
+
// ../compiler/src/checker/predeclaration.ts
|
|
18986
|
+
function declareHeaders(context, body) {
|
|
18987
|
+
const declared = [];
|
|
18988
|
+
for (const statement of body) {
|
|
18989
|
+
if (statement.kind !== "class-declaration") {
|
|
18990
|
+
continue;
|
|
18991
|
+
}
|
|
18992
|
+
const info = declareClassInfo(context, statement);
|
|
18993
|
+
if (info === null) {
|
|
18994
|
+
continue;
|
|
18995
|
+
}
|
|
18996
|
+
context.predeclareClass(info);
|
|
18997
|
+
declared.push({ info, statement });
|
|
18707
18998
|
}
|
|
18708
|
-
|
|
18709
|
-
context.declareModuleGlobal({ name: statement.name, type, isLocal: false, position: statement.position });
|
|
18710
|
-
context.declarations.declareGlobal({ name: statement.name, type, position: statement.position });
|
|
18999
|
+
return declared;
|
|
18711
19000
|
}
|
|
18712
|
-
function
|
|
18713
|
-
|
|
18714
|
-
|
|
18715
|
-
|
|
18716
|
-
|
|
18717
|
-
|
|
18718
|
-
|
|
18719
|
-
checkExpression(context, statement.expression);
|
|
18720
|
-
return;
|
|
18721
|
-
case "function-declaration":
|
|
18722
|
-
return checkFunctionDeclaration(context, statement);
|
|
18723
|
-
case "return-statement":
|
|
18724
|
-
return checkReturn(context, statement);
|
|
18725
|
-
case "do-statement":
|
|
18726
|
-
return checkBlock(context, statement.body);
|
|
18727
|
-
case "while-statement":
|
|
18728
|
-
checkExpression(context, statement.condition);
|
|
18729
|
-
return checkBlock(context, statement.body);
|
|
18730
|
-
case "repeat-statement":
|
|
18731
|
-
checkBlock(context, statement.body);
|
|
18732
|
-
checkExpression(context, statement.condition);
|
|
18733
|
-
return;
|
|
18734
|
-
case "if-statement":
|
|
18735
|
-
return checkIf(context, statement.clauses, statement.alternate);
|
|
18736
|
-
case "numeric-for-statement":
|
|
18737
|
-
return checkNumericFor(context, statement);
|
|
18738
|
-
case "generic-for-statement":
|
|
18739
|
-
return checkGenericFor(context, statement);
|
|
18740
|
-
case "type-alias-statement": {
|
|
18741
|
-
const resolved2 = context.resolveAnnotation(statement.annotation);
|
|
18742
|
-
const shape = statement.annotation.kind === "type-object" || statement.annotation.kind === "type-intersection";
|
|
18743
|
-
const named2 = shape ? renameRecord(resolved2, statement.name) : resolved2;
|
|
18744
|
-
context.noteTypeParameters(statement.typeParameters);
|
|
18745
|
-
context.binder.declareAlias(statement.name, named2, statement.typeParameters);
|
|
19001
|
+
function breakCycle(context, info) {
|
|
19002
|
+
const seen = /* @__PURE__ */ new Set([info.name]);
|
|
19003
|
+
let current = info.superClass === null ? null : context.declarations.lookupClass(info.superClass);
|
|
19004
|
+
while (current !== null) {
|
|
19005
|
+
if (seen.has(current.name)) {
|
|
19006
|
+
context.report("check-class-cycle", `Class "${info.name}" creates an inheritance cycle through "${current.name}".`, info.position);
|
|
19007
|
+
info.superClass = null;
|
|
18746
19008
|
return;
|
|
18747
19009
|
}
|
|
18748
|
-
|
|
18749
|
-
|
|
18750
|
-
case "event-declaration":
|
|
18751
|
-
return checkEventDeclaration(context, statement);
|
|
18752
|
-
case "class-declaration":
|
|
18753
|
-
return checkClassDeclaration(context, statement);
|
|
18754
|
-
case "interface-declaration":
|
|
18755
|
-
return checkInterfaceDeclaration(context, statement);
|
|
18756
|
-
case "enum-declaration":
|
|
18757
|
-
return checkEnumDeclaration(context, statement);
|
|
18758
|
-
default:
|
|
18759
|
-
return;
|
|
19010
|
+
seen.add(current.name);
|
|
19011
|
+
current = current.superClass === null ? null : context.declarations.lookupClass(current.superClass);
|
|
18760
19012
|
}
|
|
18761
19013
|
}
|
|
18762
|
-
function
|
|
18763
|
-
|
|
18764
|
-
for (const
|
|
18765
|
-
|
|
18766
|
-
const facts = guardFacts(context, statement);
|
|
18767
|
-
if (facts.size > 0) {
|
|
18768
|
-
context.pushNarrowing(facts);
|
|
18769
|
-
guards += 1;
|
|
18770
|
-
}
|
|
19014
|
+
function predeclareModule(context, body) {
|
|
19015
|
+
const declared = declareHeaders(context, body);
|
|
19016
|
+
for (const entry of declared) {
|
|
19017
|
+
entry.info.superClass = resolveSuperClass(context, entry.statement);
|
|
18771
19018
|
}
|
|
18772
|
-
|
|
18773
|
-
context.
|
|
18774
|
-
guards -= 1;
|
|
19019
|
+
for (const entry of declared) {
|
|
19020
|
+
breakCycle(context, entry.info);
|
|
18775
19021
|
}
|
|
18776
19022
|
}
|
|
18777
19023
|
|
|
@@ -18835,6 +19081,7 @@ function check(program2, mode, environment = DEFAULT_ENVIRONMENT, options = {})
|
|
|
18835
19081
|
context.isDeclarationFile = options.isDeclarationFile === true;
|
|
18836
19082
|
const structure = context.isDeclarationFile ? checkDeclarationFile(program2.body) : checkJumps(program2.body);
|
|
18837
19083
|
const directives = collectDirectives(program2.body, { isDeclarationFile: context.isDeclarationFile });
|
|
19084
|
+
predeclareModule(context, program2.body);
|
|
18838
19085
|
checkStatements(context, program2.body);
|
|
18839
19086
|
reportUnknownTypes(context);
|
|
18840
19087
|
reportUnusedSymbols(context, options.noUnusedLocals === true, options.noUnusedParameters === true);
|
|
@@ -19263,7 +19510,8 @@ function emitEnumDeclaration(state, statement) {
|
|
|
19263
19510
|
}
|
|
19264
19511
|
requireHelper(state, "class");
|
|
19265
19512
|
const names = statement.members.map((member) => emitString(member.name));
|
|
19266
|
-
|
|
19513
|
+
const target = statement.isLocal ? `local ${statement.name}` : statement.name;
|
|
19514
|
+
return names.length === 0 ? `${target} = enum {}` : `${target} = enum { ${names.join(", ")} }`;
|
|
19267
19515
|
}
|
|
19268
19516
|
|
|
19269
19517
|
// ../compiler/src/emitter/loops.ts
|
|
@@ -19714,6 +19962,25 @@ function canonicalEdit(input, statement, span) {
|
|
|
19714
19962
|
return { start: span.start, end: span.end, replacement: padded, lines: emitted.lines };
|
|
19715
19963
|
}
|
|
19716
19964
|
|
|
19965
|
+
// ../compiler/src/emitter/preserve-decorators.ts
|
|
19966
|
+
var MARKER = new RegExp(String.fromCharCode(0) + "luam:\\d+" + String.fromCharCode(0), "g");
|
|
19967
|
+
function compact(text) {
|
|
19968
|
+
return text.replace(MARKER, "").replace(/\s*\n\s*/g, " ").trim();
|
|
19969
|
+
}
|
|
19970
|
+
function injectedMembersText(input, statement) {
|
|
19971
|
+
const generated = input.generatedMembers.get(statement) ?? [];
|
|
19972
|
+
if (generated.length === 0) {
|
|
19973
|
+
return null;
|
|
19974
|
+
}
|
|
19975
|
+
const state = createEmitState(input.types, input.references, input.generatedMembers);
|
|
19976
|
+
return generated.map((member) => compact(emitMethod(state, statement.name, member))).join(", ");
|
|
19977
|
+
}
|
|
19978
|
+
function builderClassText(input, statement) {
|
|
19979
|
+
const state = createEmitState(input.types, input.references, input.generatedMembers);
|
|
19980
|
+
const builder = emitBuilder(state, statement);
|
|
19981
|
+
return builder === null ? null : compact(builder);
|
|
19982
|
+
}
|
|
19983
|
+
|
|
19717
19984
|
// ../compiler/src/emitter/preserve-guards.ts
|
|
19718
19985
|
function isPreservableExpression(expression, types) {
|
|
19719
19986
|
switch (expression.kind) {
|
|
@@ -19834,27 +20101,52 @@ function selfEdit(source, member, span) {
|
|
|
19834
20101
|
return { start: parenthesis + 1, end: parenthesis + 1, replacement: "self" };
|
|
19835
20102
|
}
|
|
19836
20103
|
function isKept(member) {
|
|
19837
|
-
return member.kind === "class-method" || member.value !== null;
|
|
20104
|
+
return !isLazyField(member) && (member.kind === "class-method" || member.value !== null);
|
|
19838
20105
|
}
|
|
19839
|
-
function
|
|
19840
|
-
|
|
20106
|
+
function isLazyField(member) {
|
|
20107
|
+
return member.kind === "class-field" && member.decorators.some((decorator) => decorator.name === "Lazy");
|
|
20108
|
+
}
|
|
20109
|
+
function isLowered(input, statement) {
|
|
20110
|
+
const generated = input.generatedMembers.get(statement)?.length ?? 0;
|
|
20111
|
+
const decorated = statement.decorators.length > 0 || generated > 0 || statement.members.some((member) => member.decorators.length > 0);
|
|
20112
|
+
if (decorated && !input.development) {
|
|
19841
20113
|
return true;
|
|
19842
20114
|
}
|
|
19843
|
-
return statement.members.some(
|
|
19844
|
-
|
|
19845
|
-
|
|
20115
|
+
return statement.members.some(
|
|
20116
|
+
(member) => member.kind === "class-field" && member.value !== null && !isPreservableExpression(member.value, input.types)
|
|
20117
|
+
);
|
|
20118
|
+
}
|
|
20119
|
+
function decoratorCommentEdits(statement) {
|
|
20120
|
+
const firstByLine = /* @__PURE__ */ new Map();
|
|
20121
|
+
const note = (position2) => {
|
|
20122
|
+
const existing = firstByLine.get(position2.line);
|
|
20123
|
+
if (existing === void 0 || position2.offset < existing) {
|
|
20124
|
+
firstByLine.set(position2.line, position2.offset);
|
|
19846
20125
|
}
|
|
19847
|
-
|
|
19848
|
-
|
|
20126
|
+
};
|
|
20127
|
+
for (const decorator of statement.decorators) {
|
|
20128
|
+
note(decorator.position);
|
|
20129
|
+
}
|
|
20130
|
+
for (const member of statement.members) {
|
|
20131
|
+
if (!isKept(member)) {
|
|
20132
|
+
continue;
|
|
20133
|
+
}
|
|
20134
|
+
for (const decorator of member.decorators) {
|
|
20135
|
+
note(decorator.position);
|
|
20136
|
+
}
|
|
20137
|
+
}
|
|
20138
|
+
return [...firstByLine.values()].map((offset) => ({ start: offset, end: offset, replacement: "--" }));
|
|
19849
20139
|
}
|
|
19850
|
-
function classEdits(
|
|
19851
|
-
|
|
20140
|
+
function classEdits(input, statement, spans) {
|
|
20141
|
+
const { source } = input;
|
|
20142
|
+
if (isLowered(input, statement)) {
|
|
19852
20143
|
return null;
|
|
19853
20144
|
}
|
|
19854
20145
|
const header = classHeaderEdit(source, statement);
|
|
19855
20146
|
if (header === null) {
|
|
19856
20147
|
return null;
|
|
19857
20148
|
}
|
|
20149
|
+
const injected = input.development ? injectedMembersText(input, statement) : null;
|
|
19858
20150
|
const edits = [header];
|
|
19859
20151
|
const kept = statement.members.filter(isKept);
|
|
19860
20152
|
const limit = spans.span.end;
|
|
@@ -19866,7 +20158,8 @@ function classEdits(source, statement, generated, types, spans) {
|
|
|
19866
20158
|
const separator = separatorAt(source, span.end, limit);
|
|
19867
20159
|
if (!isKept(member)) {
|
|
19868
20160
|
const end = separator === null ? span.end : separator + 1;
|
|
19869
|
-
|
|
20161
|
+
const text = source.slice(span.start, end);
|
|
20162
|
+
edits.push({ start: span.start, end, replacement: isLazyField(member) ? longComment(text) : blankSpan(text) });
|
|
19870
20163
|
continue;
|
|
19871
20164
|
}
|
|
19872
20165
|
if (member.kind === "class-method") {
|
|
@@ -19876,10 +20169,22 @@ function classEdits(source, statement, generated, types, spans) {
|
|
|
19876
20169
|
}
|
|
19877
20170
|
edits.push(self);
|
|
19878
20171
|
}
|
|
19879
|
-
if (separator === null && member !== kept[kept.length - 1]) {
|
|
20172
|
+
if (separator === null && (member !== kept[kept.length - 1] || injected !== null)) {
|
|
19880
20173
|
edits.push({ start: span.end, end: span.end, replacement: "," });
|
|
19881
20174
|
}
|
|
19882
20175
|
}
|
|
20176
|
+
edits.push(...decoratorCommentEdits(statement));
|
|
20177
|
+
if (injected !== null) {
|
|
20178
|
+
const brace = source.lastIndexOf("}", limit);
|
|
20179
|
+
if (brace <= spans.span.start) {
|
|
20180
|
+
return null;
|
|
20181
|
+
}
|
|
20182
|
+
edits.push({ start: brace, end: brace, replacement: `${injected} ` });
|
|
20183
|
+
}
|
|
20184
|
+
const builder = input.development ? builderClassText(input, statement) : null;
|
|
20185
|
+
if (builder !== null) {
|
|
20186
|
+
edits.push({ start: limit, end: limit, replacement: ` ${builder}` });
|
|
20187
|
+
}
|
|
19883
20188
|
return edits;
|
|
19884
20189
|
}
|
|
19885
20190
|
|
|
@@ -19968,8 +20273,7 @@ function surgery(collector, statement) {
|
|
|
19968
20273
|
return edits2 === null ? null : { edits: edits2, wrapsBody: false };
|
|
19969
20274
|
}
|
|
19970
20275
|
if (statement.kind === "class-declaration") {
|
|
19971
|
-
const
|
|
19972
|
-
const edits2 = classEdits(input.source, statement, generated, input.types, { span, members: input.spans });
|
|
20276
|
+
const edits2 = classEdits(input, statement, { span, members: input.spans });
|
|
19973
20277
|
return edits2 === null ? null : { edits: edits2, wrapsBody: false };
|
|
19974
20278
|
}
|
|
19975
20279
|
if (loopBody(statement) === null || !isPreservableStatement(statement, input.types)) {
|