@thigasdevelopment/luam 0.19.12 → 1.0.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.
Files changed (2) hide show
  1. package/luam.mjs +670 -89
  2. package/package.json +1 -1
package/luam.mjs CHANGED
@@ -3161,6 +3161,7 @@ var DEFAULT_COMPILER_OPTIONS = {
3161
3161
  oop: false,
3162
3162
  noUnusedLocals: false,
3163
3163
  noUnusedParameters: false,
3164
+ noImplicitGlobals: false,
3164
3165
  warningsAsErrors: false
3165
3166
  };
3166
3167
  var DEFAULT_SOURCE_MAPPING = {
@@ -3838,7 +3839,21 @@ function parseTypeName(stream) {
3838
3839
  }
3839
3840
  return { kind: "type-name", name: token.value, typeArguments, position: token.position };
3840
3841
  }
3842
+ function parseOptionalParameterType(stream) {
3843
+ const name = stream.next().value;
3844
+ const marker = stream.next();
3845
+ stream.next();
3846
+ const annotation = parseTypeAnnotation(stream);
3847
+ if (annotation.kind === "type-optional") {
3848
+ stream.report("parse-redundant-optional", 'The marker on the name already allows "nil". Remove the "?" after the type.', annotation.position);
3849
+ }
3850
+ const element = annotation.kind === "type-optional" ? annotation.element : annotation;
3851
+ return { name, annotation: { kind: "type-optional", element, position: marker.position } };
3852
+ }
3841
3853
  function parseParameterType(stream) {
3854
+ if (stream.check("identifier") && stream.checkAhead(1, "operator", "?") && stream.checkAhead(2, "punctuation", ":")) {
3855
+ return parseOptionalParameterType(stream);
3856
+ }
3842
3857
  let name = null;
3843
3858
  if (stream.check("identifier") && stream.checkAhead(1, "punctuation", ":")) {
3844
3859
  name = stream.next().value;
@@ -3889,7 +3904,7 @@ function parseGroupedType(stream) {
3889
3904
  return { kind: "type-tuple", elements, position: position2 };
3890
3905
  }
3891
3906
  function parseObjectMember(stream) {
3892
- const token = stream.expectName();
3907
+ const token = stream.expectMemberKey();
3893
3908
  const annotation = parseNamedAnnotation(stream, "field");
3894
3909
  if (annotation === null) {
3895
3910
  throw stream.error(`Key "${token.value}" of an object type requires a type annotation.`, "parse-invalid-type");
@@ -4034,9 +4049,16 @@ var TokenStream = class {
4034
4049
  index = 0;
4035
4050
  erased = [];
4036
4051
  spans = /* @__PURE__ */ new Map();
4052
+ breaksIndexAtNewline = false;
4037
4053
  constructor(tokens) {
4038
4054
  this.tokens = tokens;
4039
4055
  }
4056
+ stopIndexAtNewline(active) {
4057
+ this.breaksIndexAtNewline = active;
4058
+ }
4059
+ indexContinues(bracket) {
4060
+ return !this.breaksIndexAtNewline || this.index === 0 || bracket.position.line === this.peek(-1).end.line;
4061
+ }
4040
4062
  peek(offset = 0) {
4041
4063
  const token = this.tokens[Math.min(this.index + offset, this.tokens.length - 1)];
4042
4064
  return token;
@@ -4127,6 +4149,33 @@ var TokenStream = class {
4127
4149
  const token = this.peek(offset);
4128
4150
  return token.kind === "identifier" || token.kind === "keyword" && isLuamKeyword(token.value);
4129
4151
  }
4152
+ checkMemberKey(offset = 0) {
4153
+ return this.checkName(offset) || this.peek(offset).kind === "punctuation" && this.peek(offset).value === "[";
4154
+ }
4155
+ expectMemberKey() {
4156
+ if (!this.check("punctuation", "[")) {
4157
+ return this.expectName();
4158
+ }
4159
+ const open = this.next();
4160
+ const literal2 = this.current();
4161
+ if (literal2.kind !== "string") {
4162
+ throw this.error(`Expected a quoted key but found "${this.describeCurrent()}".`, "parse-unexpected-token");
4163
+ }
4164
+ this.next();
4165
+ const close = this.expect("punctuation", "]");
4166
+ return { ...literal2, position: open.position, end: close.end };
4167
+ }
4168
+ reservedNameAt(subject, offset = 0) {
4169
+ const token = this.peek(offset);
4170
+ const next = this.peek(offset + 1);
4171
+ const continues = next.kind === "punctuation" && (next.value === "," || next.value === ")" || next.value === ":") || next.kind === "operator" && (next.value === "?" || next.value === "=");
4172
+ if (token.kind !== "keyword" || !continues) {
4173
+ return null;
4174
+ }
4175
+ const message = `"${token.value}" is a reserved word and cannot name a ${subject}. Rename it \u2014 a property of the same name is still legal, so "value.${token.value}" keeps working.`;
4176
+ this.report("parse-reserved-name", message, token.position);
4177
+ return this.next();
4178
+ }
4130
4179
  expectName() {
4131
4180
  if (this.checkName()) {
4132
4181
  return this.next();
@@ -4214,7 +4263,8 @@ function parseParameter(stream) {
4214
4263
  }
4215
4264
  return { name: name2, annotation, isVararg: true, position: token.position };
4216
4265
  }
4217
- const name = stream.expect("identifier").value;
4266
+ const reserved = stream.reservedNameAt("parameter");
4267
+ const name = reserved === null ? stream.expect("identifier").value : reserved.value;
4218
4268
  return { name, annotation: parseNamedAnnotation(stream, "parameter"), isVararg: false, position: token.position };
4219
4269
  }
4220
4270
  function parseParameters(stream) {
@@ -4406,6 +4456,12 @@ function parsePrimary(stream) {
4406
4456
  stream.expect("punctuation", ")");
4407
4457
  return { kind: "group-expression", expression, position: token.position };
4408
4458
  }
4459
+ if (token.kind === "keyword" && isLuamKeyword(token.value)) {
4460
+ const message = `"${token.value}" is a reserved word and cannot be read as a value. Rename the declaration \u2014 a property of the same name is still legal, so "value.${token.value}" keeps working.`;
4461
+ stream.report("parse-reserved-name", message, token.position);
4462
+ stream.next();
4463
+ return { kind: "identifier", name: token.value, position: token.position };
4464
+ }
4409
4465
  throw stream.error(`Unexpected "${stream.describeCurrent()}" in expression.`, "parse-unexpected-token");
4410
4466
  }
4411
4467
  function parseArguments(stream) {
@@ -4440,6 +4496,9 @@ function parseSuffixed(stream) {
4440
4496
  continue;
4441
4497
  }
4442
4498
  if (token.kind === "punctuation" && token.value === "[") {
4499
+ if (!stream.indexContinues(token)) {
4500
+ return expression;
4501
+ }
4443
4502
  stream.next();
4444
4503
  const index = parseExpression(stream);
4445
4504
  stream.expect("punctuation", "]");
@@ -4622,7 +4681,11 @@ function isDeclarationStart(stream) {
4622
4681
  if (token.value === "class") {
4623
4682
  return isClassHeader(stream, offset);
4624
4683
  }
4625
- return token.value === "interface" ? stream.checkAhead(offset + 2, "punctuation", "{") || stream.checkAhead(offset + 2, "keyword", "extends") : stream.checkAhead(offset + 2, "punctuation", "{");
4684
+ if (token.value === "interface") {
4685
+ const after = skipTypeParameters(stream, offset + 2);
4686
+ return stream.checkAhead(after, "punctuation", "{") || stream.checkAhead(after, "keyword", "extends");
4687
+ }
4688
+ return stream.checkAhead(offset + 2, "punctuation", "{");
4626
4689
  }
4627
4690
  function skipDecoratorArguments(stream) {
4628
4691
  let depth = 0;
@@ -4692,6 +4755,14 @@ function parseBraceClassMethod(stream, token, decorators, isStatic) {
4692
4755
  function parseFieldAnnotation(stream) {
4693
4756
  return parseNamedAnnotation(stream, "field");
4694
4757
  }
4758
+ function parseFieldValue(stream) {
4759
+ stream.stopIndexAtNewline(true);
4760
+ try {
4761
+ return parseExpression(stream);
4762
+ } finally {
4763
+ stream.stopIndexAtNewline(false);
4764
+ }
4765
+ }
4695
4766
  function expectClassFieldBoundary(stream, token) {
4696
4767
  const current = stream.current();
4697
4768
  const delimiter = current.kind === "punctuation" && (current.value === "}" || current.value === ";" || current.value === ",");
@@ -4717,7 +4788,7 @@ function parseStaticModifier(stream) {
4717
4788
  function parseClassMember(stream) {
4718
4789
  const decorators = parseDecorators(stream);
4719
4790
  const isStatic = parseStaticModifier(stream);
4720
- const token = stream.expectName();
4791
+ const token = stream.expectMemberKey();
4721
4792
  if (stream.check("punctuation", "(")) {
4722
4793
  stream.report("parse-class-method-form", `Write class member "${token.value}" as "${token.value} = function (...) ... end".`, token.position);
4723
4794
  return parseBraceClassMethod(stream, token, decorators, isStatic);
@@ -4726,7 +4797,7 @@ function parseClassMember(stream) {
4726
4797
  return parseClassMethod(stream, token, decorators, isStatic);
4727
4798
  }
4728
4799
  const annotation = parseFieldAnnotation(stream);
4729
- const value = stream.match("operator", "=") ? parseExpression(stream) : null;
4800
+ const value = stream.match("operator", "=") ? parseFieldValue(stream) : null;
4730
4801
  expectClassFieldBoundary(stream, token);
4731
4802
  return { kind: "class-field", name: token.value, annotation, value, decorators, isStatic, position: token.position };
4732
4803
  }
@@ -4739,6 +4810,7 @@ function parseClassModifiers(stream, declaration) {
4739
4810
  }
4740
4811
  do {
4741
4812
  declaration.interfaces.push(stream.expect("identifier").value);
4813
+ declaration.interfaceArguments.push(parseTypeArguments(stream));
4742
4814
  } while (stream.match("punctuation", ","));
4743
4815
  }
4744
4816
  }
@@ -4754,6 +4826,7 @@ function parseClassDeclaration(stream, decorators) {
4754
4826
  superClass: null,
4755
4827
  superClassArguments: [],
4756
4828
  interfaces: [],
4829
+ interfaceArguments: [],
4757
4830
  members: [],
4758
4831
  decorators,
4759
4832
  position: position2
@@ -4780,7 +4853,7 @@ function parseClassDeclaration(stream, decorators) {
4780
4853
  return declaration;
4781
4854
  }
4782
4855
  function parseInterfaceMember(stream) {
4783
- const token = stream.expectName();
4856
+ const token = stream.expectMemberKey();
4784
4857
  if (stream.check("punctuation", "(")) {
4785
4858
  const parameters = parseParameters(stream);
4786
4859
  const returnAnnotation = parseOptionalAnnotation(stream);
@@ -4796,6 +4869,7 @@ function parseInterfaceDeclaration(stream) {
4796
4869
  const checkpoint = stream.checkpoint();
4797
4870
  const position2 = stream.next().position;
4798
4871
  const name = stream.expect("identifier").value;
4872
+ const typeParameters = parseTypeParameters(stream);
4799
4873
  const superInterfaces = [];
4800
4874
  const members = [];
4801
4875
  if (stream.match("keyword", "extends")) {
@@ -4818,7 +4892,15 @@ function parseInterfaceDeclaration(stream) {
4818
4892
  }
4819
4893
  }
4820
4894
  stream.expect("punctuation", "}");
4821
- const declaration = { kind: "interface-declaration", name, superInterfaces, members, position: position2 };
4895
+ const declaration = {
4896
+ kind: "interface-declaration",
4897
+ name,
4898
+ typeParameters: typeParameters.names,
4899
+ typeConstraints: typeParameters.constraints,
4900
+ superInterfaces,
4901
+ members,
4902
+ position: position2
4903
+ };
4822
4904
  stream.eraseFrom(checkpoint, "declaration");
4823
4905
  return declaration;
4824
4906
  }
@@ -4879,7 +4961,7 @@ function parseExpressionList(stream) {
4879
4961
  return values;
4880
4962
  }
4881
4963
  function parseDeclarator(stream) {
4882
- const token = stream.expect("identifier");
4964
+ const token = stream.reservedNameAt("local") ?? stream.expect("identifier");
4883
4965
  return { name: token.value, annotation: parseNamedAnnotation(stream, "local"), position: token.position };
4884
4966
  }
4885
4967
  function parseLocalStatement(stream, position2) {
@@ -4973,7 +5055,30 @@ function invalidStatement(stream, target, start) {
4973
5055
  const message = `${subject} is not a statement. Assign it with "=", pass it to a call, or remove the line.`;
4974
5056
  return stream.errorAt(message, "parse-invalid-statement", start.position, start.end);
4975
5057
  }
5058
+ function parseGlobalDeclaration(stream) {
5059
+ const marked = stream.checkAhead(1, "operator", "?") && stream.checkAhead(2, "punctuation", ":");
5060
+ if (!stream.check("identifier") || !marked && !stream.checkAhead(1, "punctuation", ":")) {
5061
+ return null;
5062
+ }
5063
+ const point = stream.speculate();
5064
+ try {
5065
+ const declaration = parseDeclarator(stream);
5066
+ if (declaration.annotation !== null && stream.match("operator", "=")) {
5067
+ return { kind: "global-statement", declaration, values: parseExpressionList(stream), position: declaration.position };
5068
+ }
5069
+ } catch (error) {
5070
+ if (!(error instanceof ParserError)) {
5071
+ throw error;
5072
+ }
5073
+ }
5074
+ stream.rewind(point);
5075
+ return null;
5076
+ }
4976
5077
  function parseExpressionStatement(stream) {
5078
+ const declaration = parseGlobalDeclaration(stream);
5079
+ if (declaration !== null) {
5080
+ return declaration;
5081
+ }
4977
5082
  const start = stream.current();
4978
5083
  const position2 = start.position;
4979
5084
  const targets = [parseSuffixed(stream)];
@@ -5001,7 +5106,8 @@ function parseKeywordStatement(stream, value) {
5001
5106
  if (stream.check("keyword", "function")) {
5002
5107
  return parseFunctionDeclaration(stream, true);
5003
5108
  }
5004
- return stream.check("keyword", "enum") ? parseEnumDeclaration(stream, true) : parseLocalStatement(stream, token.position);
5109
+ const isEnum = stream.check("keyword", "enum") && stream.checkAhead(1, "identifier");
5110
+ return isEnum ? parseEnumDeclaration(stream, true) : parseLocalStatement(stream, token.position);
5005
5111
  }
5006
5112
  if (value === "function") {
5007
5113
  return parseFunctionDeclaration(stream, false);
@@ -5266,10 +5372,21 @@ function isEmptyLiteral(type) {
5266
5372
  return type.kind === "record" && type.isLiteral === true && type.members.size === 0;
5267
5373
  }
5268
5374
  function widenInferred(type) {
5375
+ if (type.kind === "array") {
5376
+ return createArray(widenInferred(type.element));
5377
+ }
5378
+ if (type.kind === "union") {
5379
+ return createUnion(type.options.map(widenInferred));
5380
+ }
5269
5381
  if (type.kind !== "record" || type.isLiteral !== true) {
5270
5382
  return widenLiteral(type);
5271
5383
  }
5272
- return type.members.size === 0 ? TABLE_TYPE : createRecord(type.name, type.members, type.origin);
5384
+ if (type.members.size === 0) {
5385
+ return TABLE_TYPE;
5386
+ }
5387
+ const widened = new Map([...type.members].map(([name, member]) => [name, widenInferred(member)]));
5388
+ const record2 = createObjectType(widened);
5389
+ return record2.kind === "record" ? { ...record2, isInferred: true } : record2;
5273
5390
  }
5274
5391
  function createFunction(parameters, returnType, minimumArguments2, isVariadic = false, parameterNames, variadicType) {
5275
5392
  return {
@@ -5428,7 +5545,7 @@ function isFunctionAssignable(source, target, options) {
5428
5545
  return target.returnType.kind === "void" || isAssignable(source.returnType, target.returnType, options);
5429
5546
  }
5430
5547
  function isMapAssignable(source, target, options) {
5431
- if (source.kind === "table") {
5548
+ if (source.kind === "table" || isEmptyLiteral(source)) {
5432
5549
  return true;
5433
5550
  }
5434
5551
  if (source.kind === "array") {
@@ -5741,6 +5858,9 @@ var COMPILER_OPTION_FIELDS = [
5741
5858
  }),
5742
5859
  field("oop", BOOLEAN_TYPE, "Enables the MTA OOP API in the checker and the generated meta.xml.", { defaultValue: DEFAULT_COMPILER_OPTIONS.oop }),
5743
5860
  field("noUnusedLocals", BOOLEAN_TYPE, "Reports local declarations that are never read.", { defaultValue: DEFAULT_COMPILER_OPTIONS.noUnusedLocals }),
5861
+ field("noImplicitGlobals", BOOLEAN_TYPE, "Reports an assignment that creates a global the project never declares.", {
5862
+ defaultValue: DEFAULT_COMPILER_OPTIONS.noImplicitGlobals
5863
+ }),
5744
5864
  field("noUnusedParameters", BOOLEAN_TYPE, "Reports function and method parameters that are never read.", {
5745
5865
  defaultValue: DEFAULT_COMPILER_OPTIONS.noUnusedParameters
5746
5866
  }),
@@ -6802,6 +6922,7 @@ function readCompilerOptions(value) {
6802
6922
  strict: flag(source, "strict", DEFAULT_COMPILER_OPTIONS.strict),
6803
6923
  oop: flag(source, "oop", DEFAULT_COMPILER_OPTIONS.oop),
6804
6924
  noUnusedLocals: flag(source, "noUnusedLocals", DEFAULT_COMPILER_OPTIONS.noUnusedLocals),
6925
+ noImplicitGlobals: flag(source, "noImplicitGlobals", DEFAULT_COMPILER_OPTIONS.noImplicitGlobals),
6805
6926
  noUnusedParameters: flag(source, "noUnusedParameters", DEFAULT_COMPILER_OPTIONS.noUnusedParameters),
6806
6927
  warningsAsErrors: flag(source, "warningsAsErrors", DEFAULT_COMPILER_OPTIONS.warningsAsErrors)
6807
6928
  };
@@ -6978,7 +7099,7 @@ import { tmpdir } from "node:os";
6978
7099
  import { join as join2 } from "node:path";
6979
7100
 
6980
7101
  // src/cli/version.ts
6981
- var VERSION = true ? "0.19.12" : "0.0.0-dev";
7102
+ var VERSION = true ? "1.0.0" : "0.0.0-dev";
6982
7103
  var PROGRAM_NAME = "luam";
6983
7104
  var PROGRAM_DESCRIPTION = "luam \u2014 the Luam compiler for Multi Theft Auto resources.";
6984
7105
 
@@ -7042,7 +7163,7 @@ function runEditorCommand(command, args) {
7042
7163
  }
7043
7164
 
7044
7165
  // src/editor/editor-service.ts
7045
- var EXTENSION_ID = "thigasdevelopment.luam";
7166
+ var EXTENSION_ID = "luam.luam";
7046
7167
  var SUPPORTED_EDITORS = [
7047
7168
  { id: "vscode", name: "Visual Studio Code", command: "code" },
7048
7169
  { id: "vscode-insiders", name: "Visual Studio Code Insiders", command: "code-insiders" },
@@ -8862,12 +8983,13 @@ var TEST_DECLARATIONS = [
8862
8983
 
8863
8984
  // ../compiler/src/checker/ambient.ts
8864
8985
  var EVENT_NAME_PREFIX = "event:";
8865
- var EMPTY_AMBIENT = { classes: [], interfaces: [], enums: [], globals: [], events: [] };
8986
+ var EMPTY_AMBIENT = { classes: [], interfaces: [], enums: [], aliases: [], globals: [], events: [] };
8866
8987
  function ambientFromRegistry(registry) {
8867
8988
  return {
8868
8989
  classes: registry.allClasses(),
8869
8990
  interfaces: registry.allInterfaces(),
8870
8991
  enums: registry.allEnums().filter((info) => info.isLocal !== true),
8992
+ aliases: registry.allAliases(),
8871
8993
  globals: registry.allGlobals(),
8872
8994
  events: registry.allEvents()
8873
8995
  };
@@ -8885,6 +9007,7 @@ function ownDeclarations(registry, ambient) {
8885
9007
  classes: withoutNames(all.classes, ambient.classes),
8886
9008
  interfaces: withoutNames(all.interfaces, ambient.interfaces),
8887
9009
  enums: withoutNames(all.enums, ambient.enums),
9010
+ aliases: withoutNames(all.aliases, ambient.aliases),
8888
9011
  globals: withoutNames(all.globals, ambient.globals),
8889
9012
  events: withoutNames(all.events, ambient.events)
8890
9013
  };
@@ -8903,6 +9026,7 @@ function mergeAmbient(sources) {
8903
9026
  classes: mergeByName(sources.map((source) => source.classes)),
8904
9027
  interfaces: mergeByName(sources.map((source) => source.interfaces)),
8905
9028
  enums: mergeByName(sources.map((source) => source.enums)),
9029
+ aliases: mergeByName(sources.map((source) => source.aliases)),
8906
9030
  globals: mergeByName(sources.map((source) => source.globals)),
8907
9031
  events: mergeByName(sources.map((source) => source.events))
8908
9032
  };
@@ -8977,6 +9101,9 @@ function declareAll(catalog, environment, source) {
8977
9101
  function isAvailableIn(declared, environment) {
8978
9102
  return declared === "shared" || declared === environment;
8979
9103
  }
9104
+ function isVisibleIn(declared, environment) {
9105
+ return environment === "shared" || isAvailableIn(declared, environment);
9106
+ }
8980
9107
 
8981
9108
  // ../compiler/src/checker/api-types.ts
8982
9109
  var PRIMITIVES = {
@@ -9195,6 +9322,9 @@ var MAX_DEPTH = 32;
9195
9322
  function classSubstitutions(info, typeArguments) {
9196
9323
  return new Map(info.typeParameters.map((parameter, index) => [parameter, typeArguments[index] ?? ANY_TYPE]));
9197
9324
  }
9325
+ function parameterSubstitutions(typeParameters, typeArguments) {
9326
+ return new Map(typeParameters.map((parameter, index) => [parameter, typeArguments[index] ?? ANY_TYPE]));
9327
+ }
9198
9328
  function parentSubstitutions(registry, info, current) {
9199
9329
  const parent = info.superClass === null ? null : registry.lookupClass(info.superClass);
9200
9330
  if (parent === null) {
@@ -9222,7 +9352,15 @@ function substitutionsFor(registry, receiver, owner) {
9222
9352
  }
9223
9353
  return /* @__PURE__ */ new Map();
9224
9354
  }
9355
+ function interfaceSubstitutions(registry, receiver) {
9356
+ const contract = registry.lookupInterface(receiver.name);
9357
+ return contract === null ? null : parameterSubstitutions(contract.typeParameters, receiver.typeArguments ?? []);
9358
+ }
9225
9359
  function specializeMember(context, receiver, member, property) {
9360
+ const contract = interfaceSubstitutions(context.declarations, receiver);
9361
+ if (contract !== null) {
9362
+ return contract.size === 0 ? member : { ...member, type: substituteType(member.type, contract) };
9363
+ }
9226
9364
  const owner = context.declarations.lookupMemberOwner(receiver.name, property);
9227
9365
  if (owner === null || owner === receiver.name) {
9228
9366
  const own = context.declarations.lookupClass(receiver.name);
@@ -9307,6 +9445,11 @@ function reportConstraintViolations(context, owner, typeParameters, typeConstrai
9307
9445
  });
9308
9446
  }
9309
9447
  function checkTypeConstraints(context, name, typeArguments, position2) {
9448
+ const contract = context.declarations.lookupInterface(name);
9449
+ if (contract !== null) {
9450
+ reportConstraintViolations(context, `interface "${name}"`, contract.typeParameters, contract.typeConstraints, typeArguments, position2);
9451
+ return;
9452
+ }
9310
9453
  const info = context.declarations.lookupClass(name);
9311
9454
  if (info === null) {
9312
9455
  return;
@@ -12233,10 +12376,14 @@ var MTA_EXACT = {
12233
12376
  client: MTA_CLIENT_GLOBALS
12234
12377
  };
12235
12378
  function globalsFor(environment) {
12236
- return DECLARATIONS.flatMap((declaration) => {
12379
+ const resolved2 = /* @__PURE__ */ new Map();
12380
+ for (const declaration of DECLARATIONS) {
12237
12381
  const exact = findDeclaration(declaration.name, environment);
12238
- return exact === null ? [] : [exact];
12239
- });
12382
+ if (exact !== null && !resolved2.has(exact.name)) {
12383
+ resolved2.set(exact.name, exact);
12384
+ }
12385
+ }
12386
+ return [...resolved2.values()];
12240
12387
  }
12241
12388
  function findDeclaration(name, environment) {
12242
12389
  const declaration = BY_NAME.get(name);
@@ -12244,7 +12391,7 @@ function findDeclaration(name, environment) {
12244
12391
  return declaration ?? null;
12245
12392
  }
12246
12393
  if (!isAvailableIn(declaration.environment, environment)) {
12247
- return null;
12394
+ return environment === "shared" ? declaration : null;
12248
12395
  }
12249
12396
  if (declaration.source !== "mta") {
12250
12397
  return declaration;
@@ -14049,11 +14196,15 @@ var DeclarationRegistry = class {
14049
14196
  classes = /* @__PURE__ */ new Map();
14050
14197
  interfaces = /* @__PURE__ */ new Map();
14051
14198
  enums = /* @__PURE__ */ new Map();
14199
+ aliases = /* @__PURE__ */ new Map();
14052
14200
  globals = /* @__PURE__ */ new Map();
14053
14201
  events = /* @__PURE__ */ new Map();
14054
14202
  declareGlobal(info) {
14055
14203
  this.globals.set(info.name, info);
14056
14204
  }
14205
+ lookupGlobal(name) {
14206
+ return this.globals.get(name) ?? null;
14207
+ }
14057
14208
  allGlobals() {
14058
14209
  return [...this.globals.values()];
14059
14210
  }
@@ -14089,6 +14240,15 @@ var DeclarationRegistry = class {
14089
14240
  declareEnum(info) {
14090
14241
  this.enums.set(info.name, info);
14091
14242
  }
14243
+ declareAlias(info) {
14244
+ this.aliases.set(info.name, info);
14245
+ }
14246
+ lookupAlias(name) {
14247
+ return this.aliases.get(name) ?? null;
14248
+ }
14249
+ allAliases() {
14250
+ return [...this.aliases.values()];
14251
+ }
14092
14252
  lookupEnum(name) {
14093
14253
  return this.enums.get(name) ?? null;
14094
14254
  }
@@ -14229,6 +14389,7 @@ function buildRegistry() {
14229
14389
  superClass: declaration.parent,
14230
14390
  superArguments: [],
14231
14391
  interfaces: [],
14392
+ interfaceArguments: [],
14232
14393
  members,
14233
14394
  statics: /* @__PURE__ */ new Map(),
14234
14395
  position: MTA_POSITION
@@ -14435,6 +14596,11 @@ var CheckContext = class {
14435
14596
  ambientClasses = /* @__PURE__ */ new Set();
14436
14597
  ambientInterfaces = /* @__PURE__ */ new Set();
14437
14598
  ambientEnums = /* @__PURE__ */ new Set();
14599
+ ambientAliases = /* @__PURE__ */ new Set();
14600
+ ambientGlobals = /* @__PURE__ */ new Set();
14601
+ obligations = /* @__PURE__ */ new Map();
14602
+ blockDepth = 0;
14603
+ noImplicitGlobals = false;
14438
14604
  constructor(mode, environment, ambient = EMPTY_AMBIENT, project = EMPTY_PROJECT_DECLARATIONS, oop = false) {
14439
14605
  this.mode = mode;
14440
14606
  this.environment = environment;
@@ -14510,6 +14676,24 @@ var CheckContext = class {
14510
14676
  isAmbientEnum(name) {
14511
14677
  return this.ambientEnums.has(name);
14512
14678
  }
14679
+ isAmbientAlias(name) {
14680
+ return this.ambientAliases.has(name);
14681
+ }
14682
+ isAmbientGlobal(name) {
14683
+ return this.ambientGlobals.has(name);
14684
+ }
14685
+ openRecordObligation(obligation) {
14686
+ this.obligations.set(obligation.path, obligation);
14687
+ }
14688
+ recordObligation(path) {
14689
+ return this.obligations.get(path) ?? null;
14690
+ }
14691
+ closeRecordObligation(path) {
14692
+ this.obligations.delete(path);
14693
+ }
14694
+ recordObligationPaths() {
14695
+ return [...this.obligations.keys()];
14696
+ }
14513
14697
  noteExternalReference(name, position2) {
14514
14698
  if (!this.externalReferences.has(name)) {
14515
14699
  this.externalReferences.set(name, position2);
@@ -14679,19 +14863,28 @@ var CheckContext = class {
14679
14863
  assignability() {
14680
14864
  return { allowNil: this.allowNil, resolveNominal: (name) => this.nominalShape(name) };
14681
14865
  }
14682
- expectAssignable(source, target, position2, subject) {
14866
+ resolveValueAnnotation(annotation, subject) {
14867
+ const resolved2 = this.resolveAnnotation(annotation);
14868
+ if (resolved2.kind !== "tuple") {
14869
+ return resolved2;
14870
+ }
14871
+ const message = `A parenthesised list like "${typeToString(resolved2)}" is a return shape and cannot type ${subject}. Name one type instead.`;
14872
+ this.report("check-tuple-position", message, annotation.position);
14873
+ return firstValueOf(resolved2);
14874
+ }
14875
+ expectAssignable(source, target, position2, subject, printed = target) {
14683
14876
  if (isAssignable(source, target, this.assignability())) {
14684
14877
  return;
14685
14878
  }
14686
14879
  const received = isLiteralType(target) || target.kind === "union" ? source : widenLiteral(source);
14687
- const message = `${subject} expects "${typeToString(target)}" but received "${typeToString(received)}".`;
14880
+ const message = `${subject} expects "${typeToString(printed)}" but received "${typeToString(received)}".`;
14688
14881
  const hint = `${nilHint(source, target, this.mode)}${missingKeyHint(source, target, (name) => this.nominalShape(name))}`;
14689
14882
  this.report("check-type-mismatch", `${message}${hint}`, position2);
14690
14883
  }
14691
14884
  declareProject(project) {
14692
14885
  for (const declaration of project.globals) {
14693
14886
  this.projectEnvironments.set(declaration.name, declaration.environment);
14694
- if (isAvailableIn(declaration.environment, this.environment)) {
14887
+ if (isVisibleIn(declaration.environment, this.environment)) {
14695
14888
  const symbol = { name: declaration.name, type: descriptorToType(declaration.type), isLocal: false, position: PROJECT_POSITION };
14696
14889
  this.binder.declareGlobal(symbol);
14697
14890
  }
@@ -14712,7 +14905,14 @@ var CheckContext = class {
14712
14905
  this.declarations.declareEnum(info);
14713
14906
  this.binder.declareGlobal({ name: info.name, type: createNamed(info.name), isLocal: false, position: info.position });
14714
14907
  }
14908
+ for (const info of ambient.aliases) {
14909
+ this.ambientAliases.add(info.name);
14910
+ this.binder.declareAlias(info.name, info.type, info.typeParameters);
14911
+ this.declarations.declareAlias(info);
14912
+ }
14715
14913
  for (const info of ambient.globals) {
14914
+ this.ambientGlobals.add(info.name);
14915
+ this.declarations.declareGlobal(info);
14716
14916
  this.binder.declareGlobal({ name: info.name, type: info.type, isLocal: false, position: info.position });
14717
14917
  }
14718
14918
  for (const info of ambient.events) {
@@ -14732,18 +14932,20 @@ var CheckContext = class {
14732
14932
  return createMap(this.resolveAnnotation(key), this.resolveAnnotation(value));
14733
14933
  }
14734
14934
  resolveClassAnnotation(name, typeArguments, position2) {
14735
- const declared = this.declarations.lookupClass(name)?.typeParameters ?? [];
14935
+ const declaredInterface = this.declarations.lookupInterface(name);
14936
+ const owner = declaredInterface === null ? "Class" : "Interface";
14937
+ const declared = declaredInterface?.typeParameters ?? this.declarations.lookupClass(name)?.typeParameters ?? [];
14736
14938
  const resolved2 = typeArguments.map((argument) => this.resolveAnnotation(argument));
14737
14939
  if (declared.length === 0) {
14738
14940
  return createNamed(name);
14739
14941
  }
14740
14942
  if (resolved2.length !== declared.length) {
14741
14943
  const expected = declared.length === 1 ? "1 type argument" : `${declared.length} type arguments`;
14742
- this.report("check-generic-arity", `Class "${name}" expects ${expected} but received ${resolved2.length}.`, position2);
14944
+ this.report("check-generic-arity", `${owner} "${name}" expects ${expected} but received ${resolved2.length}.`, position2);
14743
14945
  }
14744
14946
  const specialized = createNamed(name, declared.map((unused, index) => resolved2[index] ?? ANY_TYPE));
14745
14947
  if (namedNesting(specialized) > MAX_GENERIC_DEPTH) {
14746
- const message = `Class "${name}" is nested more than ${MAX_GENERIC_DEPTH} levels deep here. Name the inner type with a "type" alias.`;
14948
+ const message = `${owner} "${name}" is nested more than ${MAX_GENERIC_DEPTH} levels deep here. Name the inner type with a "type" alias.`;
14747
14949
  this.report("check-generic-depth", message, position2);
14748
14950
  return createNamed(name);
14749
14951
  }
@@ -14789,6 +14991,7 @@ var ALLOWED = /* @__PURE__ */ new Set([
14789
14991
  var LABELS = {
14790
14992
  "local-statement": 'A "local" declaration',
14791
14993
  "assignment-statement": "An assignment",
14994
+ "global-statement": "A global declaration",
14792
14995
  "call-statement": "A call",
14793
14996
  "return-statement": 'A "return"',
14794
14997
  "break-statement": 'A "break"',
@@ -14848,6 +15051,7 @@ function statementExpressions(statement) {
14848
15051
  switch (statement.kind) {
14849
15052
  case "local-statement":
14850
15053
  case "return-statement":
15054
+ case "global-statement":
14851
15055
  return statement.values;
14852
15056
  case "assignment-statement":
14853
15057
  return [...statement.targets, ...statement.values];
@@ -14995,18 +15199,28 @@ function checkContract(context, info, contract, member) {
14995
15199
  const message = `${subject} expects "${typeToString(member.type)}" from interface "${contract}" but is "${typeToString(actual.type)}".`;
14996
15200
  context.report("check-unimplemented-interface", message, actual.position);
14997
15201
  }
15202
+ function contractName(name, typeArguments) {
15203
+ return typeArguments.length === 0 ? name : `${name}<${typeArguments.map(typeToString).join(", ")}>`;
15204
+ }
14998
15205
  function checkInterfaces(context, info) {
14999
- for (const name of info.interfaces) {
15206
+ info.interfaces.forEach((name, index) => {
15000
15207
  const contract = context.declarations.lookupInterface(name);
15001
15208
  if (contract === null) {
15002
15209
  context.noteExternalReference(name, info.position);
15003
15210
  context.report("check-unknown-interface", `Class "${info.name}" implements "${name}", which is not defined.`, info.position);
15004
- continue;
15211
+ return;
15005
15212
  }
15213
+ const typeArguments = info.interfaceArguments[index] ?? [];
15214
+ if (contract.typeParameters.length > 0 && typeArguments.length !== contract.typeParameters.length) {
15215
+ const expected = contract.typeParameters.length === 1 ? "1 type argument" : `${contract.typeParameters.length} type arguments`;
15216
+ context.report("check-generic-arity", `Interface "${name}" expects ${expected} but received ${typeArguments.length}.`, info.position);
15217
+ }
15218
+ const substitutions = parameterSubstitutions(contract.typeParameters, typeArguments);
15219
+ const label2 = contractName(name, typeArguments);
15006
15220
  for (const member of context.declarations.collectInterfaceContract(contract.name)) {
15007
- checkContract(context, info, name, member);
15221
+ checkContract(context, info, label2, substitutions.size === 0 ? member : { ...member, type: substituteType(member.type, substitutions) });
15008
15222
  }
15009
- }
15223
+ });
15010
15224
  }
15011
15225
  function checkOverrides(context, info, statement) {
15012
15226
  for (const member of statement.members) {
@@ -15615,6 +15829,7 @@ function statementExpressions2(statement) {
15615
15829
  switch (statement.kind) {
15616
15830
  case "local-statement":
15617
15831
  case "return-statement":
15832
+ case "global-statement":
15618
15833
  return statement.values;
15619
15834
  case "assignment-statement":
15620
15835
  return [...statement.targets, ...statement.values];
@@ -15672,6 +15887,9 @@ function addTargets(statement, paths) {
15672
15887
  if (statement.kind === "local-statement") {
15673
15888
  statement.declarations.forEach((declaration) => paths.add(declaration.name));
15674
15889
  }
15890
+ if (statement.kind === "global-statement") {
15891
+ paths.add(statement.declaration.name);
15892
+ }
15675
15893
  }
15676
15894
  function collectPaths(body, paths) {
15677
15895
  for (const statement of body) {
@@ -19083,21 +19301,42 @@ function eventHandler(name, environment) {
19083
19301
  return MTA_EVENT_SIGNATURES[environment][name] ?? null;
19084
19302
  }
19085
19303
 
19086
- // ../compiler/src/checker/environment-checks.ts
19087
- var EVENT_FUNCTIONS = /* @__PURE__ */ new Set(["addEventHandler", "removeEventHandler", "triggerEvent"]);
19304
+ // ../compiler/src/checker/environment-message.ts
19088
19305
  function scopeLabel(environment) {
19089
19306
  return environment === "shared" ? "shared" : `${environment}-only`;
19090
19307
  }
19308
+ function unavailableTail(environment) {
19309
+ return `is not available in a "${environment}" file.`;
19310
+ }
19311
+ function unusableTail(environment) {
19312
+ return `cannot be used in a "${environment}" file.`;
19313
+ }
19314
+ function reportEnvironment(context, code, message, position2) {
19315
+ if (context.environment === "shared") {
19316
+ return;
19317
+ }
19318
+ context.report(code, message, position2);
19319
+ }
19320
+
19321
+ // ../compiler/src/checker/environment-checks.ts
19322
+ var EVENT_FUNCTIONS = /* @__PURE__ */ new Set(["addEventHandler", "removeEventHandler", "triggerEvent"]);
19323
+ function reportProject(context, name, declared, position2) {
19324
+ const message = `"${name}" is declared by the project, is ${scopeLabel(declared)}, and ${unavailableTail(context.environment)}`;
19325
+ reportEnvironment(context, "check-environment-api", message, position2);
19326
+ }
19327
+ function reportApi(context, name, declared, position2) {
19328
+ const message = `API "${name}" is ${scopeLabel(declared)} and ${unavailableTail(context.environment)}`;
19329
+ reportEnvironment(context, "check-environment-api", message, position2);
19330
+ }
19091
19331
  function checkGlobalReference(context, name, position2) {
19092
19332
  if (name === "self") {
19093
- const message2 = 'A "self" is only bound inside a class method or a "function Name:method()"; here it reads a global that is "nil".';
19094
- context.report("check-invalid-self", message2, position2);
19333
+ const message = 'A "self" is only bound inside a class method or a "function Name:method()"; here it reads a global that is "nil".';
19334
+ context.report("check-invalid-self", message, position2);
19095
19335
  return;
19096
19336
  }
19097
19337
  const project = context.projectEnvironmentOf(name);
19098
19338
  if (project !== null) {
19099
- const message2 = `"${name}" is declared by the project, is ${scopeLabel(project)}, and is not available in a "${context.environment}" file.`;
19100
- context.report("check-environment-api", message2, position2);
19339
+ reportProject(context, name, project, position2);
19101
19340
  return;
19102
19341
  }
19103
19342
  const declared = declarationEnvironment(name);
@@ -19105,8 +19344,26 @@ function checkGlobalReference(context, name, position2) {
19105
19344
  context.noteExternalReference(name, position2);
19106
19345
  return;
19107
19346
  }
19108
- const message = `API "${name}" is ${scopeLabel(declared)} and is not available in a "${context.environment}" file.`;
19109
- context.report("check-environment-api", message, position2);
19347
+ reportApi(context, name, declared, position2);
19348
+ }
19349
+ function checkSharedReference(context, name, position2) {
19350
+ if (context.environment !== "shared" || context.moduleGlobals.has(name)) {
19351
+ return;
19352
+ }
19353
+ const project = context.projectEnvironmentOf(name);
19354
+ if (project !== null) {
19355
+ if (project !== "shared") {
19356
+ reportProject(context, name, project, position2);
19357
+ }
19358
+ return;
19359
+ }
19360
+ if (!context.binder.isBuiltinReference(name)) {
19361
+ return;
19362
+ }
19363
+ const declared = declarationEnvironment(name);
19364
+ if (declared !== null && declared !== "shared") {
19365
+ reportApi(context, name, declared, position2);
19366
+ }
19110
19367
  }
19111
19368
  function eventName(expression) {
19112
19369
  const argument = expression.args[0];
@@ -19125,8 +19382,8 @@ function checkEventUsage(context, expression) {
19125
19382
  if (name === null || custom?.environment === "shared" || custom?.environment === context.environment || declared === null || declared === context.environment) {
19126
19383
  return;
19127
19384
  }
19128
- const message = `Event "${name}" is ${scopeLabel(declared)} and cannot be used in a "${context.environment}" file.`;
19129
- context.report("check-environment-event", message, expression.position);
19385
+ const message = `Event "${name}" is ${scopeLabel(declared)} and ${unusableTail(context.environment)}`;
19386
+ reportEnvironment(context, "check-environment-event", message, expression.position);
19130
19387
  }
19131
19388
 
19132
19389
  // ../mta-types/src/event-call-semantics.ts
@@ -19307,6 +19564,136 @@ function withoutSelfParameter(signature2) {
19307
19564
  return createFunction(signature2.parameters.slice(1), signature2.returnType, minimum, signature2.isVariadic, names.slice(1), signature2.variadicType);
19308
19565
  }
19309
19566
 
19567
+ // ../compiler/src/checker/record-completion.ts
19568
+ function recordShapeOf(context, type) {
19569
+ if (type.kind === "record") {
19570
+ return type;
19571
+ }
19572
+ if (type.kind !== "named") {
19573
+ return null;
19574
+ }
19575
+ const shape = context.assignability().resolveNominal?.(type.name) ?? null;
19576
+ return shape === null ? null : { kind: "record", name: type.name, origin: null, members: shape.members };
19577
+ }
19578
+ function pendingKeys(context, source, target) {
19579
+ if (source.kind !== "record" || source.isLiteral !== true) {
19580
+ return null;
19581
+ }
19582
+ const shape = recordShapeOf(context, target);
19583
+ if (shape === null) {
19584
+ return null;
19585
+ }
19586
+ const missing2 = /* @__PURE__ */ new Map();
19587
+ const options = context.assignability();
19588
+ for (const [name, member] of shape.members) {
19589
+ const written = source.members.get(name);
19590
+ if (written === void 0) {
19591
+ if (member.kind !== "optional") {
19592
+ missing2.set(name, member);
19593
+ }
19594
+ continue;
19595
+ }
19596
+ if (!isAssignable(written, member, options)) {
19597
+ return null;
19598
+ }
19599
+ }
19600
+ for (const name of source.members.keys()) {
19601
+ if (!shape.members.has(name)) {
19602
+ return null;
19603
+ }
19604
+ }
19605
+ return missing2.size === 0 ? null : missing2;
19606
+ }
19607
+ function deferRecordCompletion(context, target, source, declared, subject, position2) {
19608
+ const path = pathOf2(target);
19609
+ if (path === null) {
19610
+ return false;
19611
+ }
19612
+ const missing2 = pendingKeys(context, source, declared);
19613
+ if (missing2 === null) {
19614
+ return false;
19615
+ }
19616
+ context.openRecordObligation({
19617
+ path,
19618
+ subject,
19619
+ source,
19620
+ target: declared,
19621
+ missing: missing2,
19622
+ discharged: 0,
19623
+ depth: context.blockDepth,
19624
+ line: position2.line,
19625
+ position: position2
19626
+ });
19627
+ return true;
19628
+ }
19629
+ function reportIncompleteRecord(context, obligation, position2) {
19630
+ if (obligation.discharged === 0) {
19631
+ context.closeRecordObligation(obligation.path);
19632
+ context.expectAssignable(obligation.source, obligation.target, obligation.position, obligation.subject);
19633
+ return;
19634
+ }
19635
+ const keys = [...obligation.missing.keys()].map((key) => `"${key}"`);
19636
+ const label2 = keys.length === 1 ? `Key ${keys[0]} is` : `Keys ${keys.join(", ")} are`;
19637
+ const built = `The table built on line ${obligation.line} is used before it is complete.`;
19638
+ const message = `${obligation.subject} expects "${typeToString(obligation.target)}". ${label2} never assigned. ${built}`;
19639
+ context.report("check-incomplete-record", message, position2);
19640
+ context.closeRecordObligation(obligation.path);
19641
+ }
19642
+ function dischargeRecordKey(context, target, valueType, position2) {
19643
+ if (target.kind !== "member-expression") {
19644
+ return false;
19645
+ }
19646
+ const path = pathOf2(target.object);
19647
+ const obligation = path === null ? null : context.recordObligation(path);
19648
+ if (obligation === null || obligation.depth !== context.blockDepth) {
19649
+ return false;
19650
+ }
19651
+ const expected = obligation.missing.get(target.property);
19652
+ if (expected === void 0) {
19653
+ return false;
19654
+ }
19655
+ context.expectAssignable(valueType, expected, position2, `Key "${target.property}"`);
19656
+ obligation.missing.delete(target.property);
19657
+ obligation.discharged += 1;
19658
+ if (obligation.missing.size === 0) {
19659
+ context.closeRecordObligation(obligation.path);
19660
+ }
19661
+ return true;
19662
+ }
19663
+ function extendInferredRecord(context, target, valueType) {
19664
+ if (target.kind !== "member-expression" || target.object.kind !== "identifier") {
19665
+ return false;
19666
+ }
19667
+ const symbol = context.binder.lookup(target.object.name);
19668
+ if (symbol === null || symbol.type.kind !== "record" || symbol.type.isInferred !== true || symbol.type.members.has(target.property)) {
19669
+ return false;
19670
+ }
19671
+ const members = new Map(symbol.type.members);
19672
+ members.set(target.property, widenInferred(valueType));
19673
+ const extended = createObjectType(members);
19674
+ symbol.type = extended.kind === "record" ? { ...extended, isInferred: true } : extended;
19675
+ return true;
19676
+ }
19677
+ function escapesRecordObligation(context, expression) {
19678
+ const path = pathOf2(expression);
19679
+ const obligation = path === null ? null : context.recordObligation(path);
19680
+ if (obligation !== null) {
19681
+ reportIncompleteRecord(context, obligation, expression.position);
19682
+ }
19683
+ }
19684
+ function settleRecordObligations(context, opened) {
19685
+ for (const path of context.recordObligationPaths()) {
19686
+ if (opened.has(path)) {
19687
+ continue;
19688
+ }
19689
+ const obligation = context.recordObligation(path);
19690
+ if (obligation === null) {
19691
+ continue;
19692
+ }
19693
+ reportIncompleteRecord(context, obligation, obligation.position);
19694
+ }
19695
+ }
19696
+
19310
19697
  // ../compiler/src/checker/resource-exports.ts
19311
19698
  var RESOURCE_LOOKUP = "getResourceFromName";
19312
19699
  var EXPORTS_TABLE = "exports";
@@ -19516,9 +19903,6 @@ function isElementType(name) {
19516
19903
 
19517
19904
  // ../compiler/src/checker/oop-members.ts
19518
19905
  var OOP_HINT = 'Set "compiler = { oop = true }" in .luam.manifest to enable the MTA OOP API.';
19519
- function scopeLabel2(environment) {
19520
- return environment === "shared" ? "shared" : `${environment}-only`;
19521
- }
19522
19906
  function isMtaElementName(declarations, className) {
19523
19907
  if (!isElementType(className)) {
19524
19908
  return false;
@@ -19536,6 +19920,14 @@ function reportDisabled(context, className, name, position2) {
19536
19920
  const subject = `"${className}.${name}" is part of the MTA OOP API, which this project does not enable.`;
19537
19921
  context.report("check-oop-disabled", `${subject} Call "${member.procedural}" instead. ${OOP_HINT}`, position2);
19538
19922
  }
19923
+ function acceptsMember(context, className, name, member, position2) {
19924
+ if (member.environment === void 0 || isAvailableIn(member.environment, context.environment)) {
19925
+ return true;
19926
+ }
19927
+ const subject = `"${className}.${name}" wraps "${member.procedural}", which is ${scopeLabel(member.environment)}`;
19928
+ reportEnvironment(context, "check-environment-api", `${subject} and ${unavailableTail(context.environment)}`, position2);
19929
+ return context.environment === "shared";
19930
+ }
19539
19931
  function isMtaClassReference(context, className) {
19540
19932
  return isMtaClass(className) && context.binder.lookup(className) === null && context.declarations.lookupClass(className) === null && context.declarations.lookupInterface(className) === null;
19541
19933
  }
@@ -19551,12 +19943,7 @@ function resolveMtaStaticMember(context, className, name, position2) {
19551
19943
  reportDisabled(context, className, name, position2);
19552
19944
  return null;
19553
19945
  }
19554
- if (member.environment !== void 0 && !isAvailableIn(member.environment, context.environment)) {
19555
- const subject = `"${className}.${name}" wraps "${member.procedural}", which is ${scopeLabel2(member.environment)}`;
19556
- context.report("check-environment-api", `${subject} and is not available in a "${context.environment}" file.`, position2);
19557
- return null;
19558
- }
19559
- return member;
19946
+ return acceptsMember(context, className, name, member, position2) ? member : null;
19560
19947
  }
19561
19948
  function resolveMtaConstructor(context, className, position2) {
19562
19949
  const constructor = mtaConstructor(className);
@@ -19571,9 +19958,11 @@ function resolveMtaConstructor(context, className, position2) {
19571
19958
  return null;
19572
19959
  }
19573
19960
  if (!isAvailableIn(constructor.environment, context.environment)) {
19574
- const subject = `"${className}(...)" is ${scopeLabel2(constructor.environment)}`;
19575
- context.report("check-environment-api", `${subject} and is not available in a "${context.environment}" file.`, position2);
19576
- return null;
19961
+ const subject = `"${className}(...)" is ${scopeLabel(constructor.environment)}`;
19962
+ reportEnvironment(context, "check-environment-api", `${subject} and ${unavailableTail(context.environment)}`, position2);
19963
+ if (context.environment !== "shared") {
19964
+ return null;
19965
+ }
19577
19966
  }
19578
19967
  return constructor.type;
19579
19968
  }
@@ -19593,12 +19982,7 @@ function resolveMtaMember(context, className, name, position2) {
19593
19982
  reportUnknown(context, className, name, position2);
19594
19983
  return null;
19595
19984
  }
19596
- if (member.environment !== void 0 && !isAvailableIn(member.environment, context.environment)) {
19597
- const subject = `"${className}.${name}" wraps "${member.procedural}", which is ${scopeLabel2(member.environment)}`;
19598
- context.report("check-environment-api", `${subject} and is not available in a "${context.environment}" file.`, position2);
19599
- return null;
19600
- }
19601
- return member;
19985
+ return acceptsMember(context, className, name, member, position2) ? member : null;
19602
19986
  }
19603
19987
 
19604
19988
  // ../compiler/src/checker/members.ts
@@ -19724,7 +20108,9 @@ function resolveNamedMember(context, receiver, expression) {
19724
20108
  if (contract === null) {
19725
20109
  return null;
19726
20110
  }
19727
- const members = new Map(context.declarations.collectMembers(name).map((member2) => [member2.name, member2]));
20111
+ const substitutions = parameterSubstitutions(contract.typeParameters, receiver.typeArguments ?? []);
20112
+ const collected = context.declarations.collectMembers(name).map((entry) => substitutions.size === 0 ? entry : { ...entry, type: substituteType(entry.type, substitutions) });
20113
+ const members = new Map(collected.map((entry) => [entry.name, entry]));
19728
20114
  return checkInterfaceMember(context, name, members, expression);
19729
20115
  }
19730
20116
  function reportUnknownMethod(context, receiver, callee, method, position2) {
@@ -19892,6 +20278,39 @@ function checkUnary(context, operator, operand, expression) {
19892
20278
  return context.record(expression, NUMBER_TYPE);
19893
20279
  }
19894
20280
 
20281
+ // ../compiler/src/checker/shadowing.ts
20282
+ var HELPER_TARGETS = new Map(
20283
+ NATIVE_EXTENSIONS.map((extension) => [
20284
+ extension.target,
20285
+ extension.style === "call" ? `${extension.receiver}.${extension.property}(...)` : `${extension.receiver}.${extension.property}`
20286
+ ])
20287
+ );
20288
+ var SUPPRESSION = 'Rename the declaration, or record the new signature in a ".d.luam" file so later calls are checked against it.';
20289
+ function reportShadowedGlobal(context, name, position2) {
20290
+ if (context.isDeclarationFile || !context.binder.isBuiltinReference(name)) {
20291
+ return;
20292
+ }
20293
+ const message = `"${name}" is an API this environment declares. Later calls are still checked against the declared signature, not this one. ${SUPPRESSION}`;
20294
+ context.warn("check-shadowed-api", message, position2);
20295
+ }
20296
+ function reportShadowedHelper(context, library, member, position2) {
20297
+ if (context.isDeclarationFile || !isLibrary(library) || findLibraryMember(library, member) === null) {
20298
+ return;
20299
+ }
20300
+ const target = `${library}.${member}`;
20301
+ const rewrite = HELPER_TARGETS.get(target);
20302
+ const lowered = rewrite === void 0 ? "" : ` The extension "${rewrite}" lowers to it.`;
20303
+ const message = `"${target}" is part of the standard library this build models.${lowered} Later calls are still checked against the declared signature, not this one. ${SUPPRESSION}`;
20304
+ context.warn("check-shadowed-helper", message, position2);
20305
+ }
20306
+ function reportImplicitGlobal(context, name, position2) {
20307
+ if (context.isDeclarationFile || !context.noImplicitGlobals) {
20308
+ return;
20309
+ }
20310
+ const message = `"${name}" is not declared anywhere this file can reach, so this assignment creates a global. Declare it with "local", annotate it as a global, or describe it in a ".d.luam" file.`;
20311
+ context.warn("check-implicit-global", message, position2);
20312
+ }
20313
+
19895
20314
  // ../compiler/src/checker/union-members.ts
19896
20315
  function isShapeOption(context, option) {
19897
20316
  if (option.kind === "record") {
@@ -20113,8 +20532,12 @@ function negatedFacts(context, condition) {
20113
20532
  // ../compiler/src/checker/control-flow.ts
20114
20533
  var ITERATOR_FUNCTIONS = /* @__PURE__ */ new Set(["pairs", "ipairs"]);
20115
20534
  function checkBlock(context, body) {
20535
+ const opened = new Set(context.recordObligationPaths());
20116
20536
  context.binder.pushScope();
20537
+ context.blockDepth += 1;
20117
20538
  checkStatements(context, body);
20539
+ context.blockDepth -= 1;
20540
+ settleRecordObligations(context, opened);
20118
20541
  const declared = context.binder.currentScopeNames();
20119
20542
  context.binder.popScope();
20120
20543
  for (const name of declared) {
@@ -20309,7 +20732,9 @@ function minimumArguments(context, parameters) {
20309
20732
  function buildFunctionType(context, parameters, returnAnnotation, contextual = null) {
20310
20733
  const isVariadic = parameters.some((parameter) => parameter.isVararg);
20311
20734
  const named2 = parameters.filter((parameter) => !parameter.isVararg);
20312
- const types = named2.map((parameter, index) => parameter.annotation === null ? contextual?.parameters[index] ?? ANY_TYPE : context.resolveAnnotation(parameter.annotation));
20735
+ const types = named2.map(
20736
+ (parameter, index) => parameter.annotation === null ? contextual?.parameters[index] ?? ANY_TYPE : context.resolveValueAnnotation(parameter.annotation, "a parameter")
20737
+ );
20313
20738
  const names = named2.map((parameter) => parameter.name);
20314
20739
  return createFunction(types, context.resolveAnnotation(returnAnnotation), minimumArguments(context, parameters), isVariadic, names);
20315
20740
  }
@@ -20354,7 +20779,11 @@ function checkFunctionBody(context, parameters, returnAnnotation, body, signatur
20354
20779
  parameterIndex += 1;
20355
20780
  }
20356
20781
  }
20782
+ const opened = new Set(context.recordObligationPaths());
20783
+ context.blockDepth += 1;
20357
20784
  checkStatements(context, body);
20785
+ context.blockDepth -= 1;
20786
+ settleRecordObligations(context, opened);
20358
20787
  const declared = context.currentReturnType();
20359
20788
  if (declared !== null && context.flowState.reachable && !toleratesFallthrough(declared)) {
20360
20789
  reportMissingReturn(context, declared, position2);
@@ -20383,9 +20812,11 @@ function checkLocal(context, statement) {
20383
20812
  context.binder.declare({ name: declaration.name, type: inferred, isLocal: true, position: declaration.position, origin: "local" });
20384
20813
  return;
20385
20814
  }
20386
- const declared = context.resolveAnnotation(declaration.annotation);
20387
- if (value !== void 0) {
20388
- context.expectAssignable(valueType, declared, value.position, `Variable "${declaration.name}"`);
20815
+ const declared = context.resolveValueAnnotation(declaration.annotation, "a local");
20816
+ const subject = `Variable "${declaration.name}"`;
20817
+ const deferred = value !== void 0 && deferRecordCompletion(context, { kind: "identifier", name: declaration.name, position: declaration.position }, valueType, declared, subject, value.position);
20818
+ if (value !== void 0 && !deferred) {
20819
+ context.expectAssignable(valueType, declared, value.position, subject);
20389
20820
  }
20390
20821
  context.binder.declare({ name: declaration.name, type: declared, isLocal: true, position: declaration.position, origin: "local" });
20391
20822
  if (value !== void 0) {
@@ -20404,6 +20835,13 @@ function checkCompoundOperand(context, operator, type, position2) {
20404
20835
  context.report("check-invalid-operand", `Operator "${operator}" cannot be applied to "${typeToString(type)}".`, position2);
20405
20836
  }
20406
20837
  }
20838
+ var DELETABLE_CONTAINERS = /* @__PURE__ */ new Set(["map", "array", "table"]);
20839
+ function deletesContainerKey(context, target, valueType) {
20840
+ if (valueType.kind !== "nil" || target.kind !== "index-expression" && target.kind !== "member-expression") {
20841
+ return false;
20842
+ }
20843
+ return DELETABLE_CONTAINERS.has(context.typeOf(target.object).kind);
20844
+ }
20407
20845
  function recordAssignedFact(context, path, valueType, declared) {
20408
20846
  if (path === null || valueType.kind === "any") {
20409
20847
  return;
@@ -20420,9 +20858,12 @@ function checkAssignment(context, statement) {
20420
20858
  if (assigned !== null) {
20421
20859
  context.forgetNarrowing(assigned);
20422
20860
  }
20423
- const targetType = checkExpression(context, target);
20424
20861
  const valueType = valueTypes[index] ?? NIL_TYPE;
20425
20862
  const value = valueAt(statement.values, valueTypes, index);
20863
+ if (value !== void 0 && extendInferredRecord(context, target, valueType)) {
20864
+ return;
20865
+ }
20866
+ const targetType = checkExpression(context, target);
20426
20867
  if (target.kind === "member-expression") {
20427
20868
  const object = context.typeOf(target.object);
20428
20869
  const frame = context.currentClassMethod();
@@ -20441,16 +20882,54 @@ function checkAssignment(context, statement) {
20441
20882
  return;
20442
20883
  }
20443
20884
  if (target.kind === "identifier" && context.binder.lookup(target.name) === null) {
20885
+ reportImplicitGlobal(context, target.name, target.position);
20444
20886
  context.declareModuleGlobal({ name: target.name, type: valueType, isLocal: false, position: target.position });
20445
20887
  return;
20446
20888
  }
20889
+ if (!context.insideFunction()) {
20890
+ reportShadowedAssignment(context, target);
20891
+ }
20447
20892
  const declared = target.kind === "identifier" ? context.binder.lookup(target.name)?.type ?? targetType : targetType;
20893
+ if (deletesContainerKey(context, target, valueType)) {
20894
+ return;
20895
+ }
20896
+ if (value !== void 0 && dischargeRecordKey(context, target, valueType, value.position)) {
20897
+ return;
20898
+ }
20899
+ const path = pathOf2(target);
20900
+ if (path !== null) {
20901
+ context.closeRecordObligation(path);
20902
+ }
20903
+ if (value !== void 0 && deferRecordCompletion(context, target, valueType, declared, "Assignment", value.position)) {
20904
+ return;
20905
+ }
20448
20906
  if (value !== void 0) {
20449
20907
  context.expectAssignable(valueType, declared, value.position, "Assignment");
20450
20908
  }
20451
20909
  recordAssignedFact(context, pathOf2(target), valueType, declared);
20452
20910
  });
20453
20911
  }
20912
+ function rootIdentifier(expression) {
20913
+ if (expression.kind === "identifier") {
20914
+ return expression.name;
20915
+ }
20916
+ return expression.kind === "member-expression" || expression.kind === "index-expression" ? rootIdentifier(expression.object) : null;
20917
+ }
20918
+ function reportShadowedAssignment(context, target) {
20919
+ if (target.kind !== "identifier") {
20920
+ const root = rootIdentifier(target);
20921
+ if (root !== null && context.binder.lookup(root) === null) {
20922
+ reportImplicitGlobal(context, root, target.position);
20923
+ }
20924
+ }
20925
+ if (target.kind === "identifier") {
20926
+ reportShadowedGlobal(context, target.name, target.position);
20927
+ return;
20928
+ }
20929
+ if (target.kind === "member-expression" && target.object.kind === "identifier") {
20930
+ reportShadowedHelper(context, target.object.name, target.property, target.position);
20931
+ }
20932
+ }
20454
20933
  function checkFunctionDeclaration(context, statement) {
20455
20934
  const built = buildFunctionType(context, statement.parameters, statement.returnAnnotation);
20456
20935
  const type = applyTypeParameters(context, built, statement.typeParameters, statement.typeConstraints);
@@ -20459,9 +20938,13 @@ function checkFunctionDeclaration(context, statement) {
20459
20938
  if (statement.isLocal) {
20460
20939
  context.binder.declare({ ...symbol, origin: "local" });
20461
20940
  } else {
20941
+ reportShadowedGlobal(context, statement.name.name, statement.position);
20462
20942
  context.declareModuleGlobal(symbol);
20463
20943
  }
20464
20944
  }
20945
+ if (!statement.isLocal && statement.name.kind !== "identifier") {
20946
+ reportShadowedAssignment(context, statement.name);
20947
+ }
20465
20948
  context.record(statement.name, type);
20466
20949
  checkFunctionBody(context, statement.parameters, statement.returnAnnotation, statement.body, type, statement.isMethod ? ANY_TYPE : null, statement.position);
20467
20950
  }
@@ -20514,7 +20997,34 @@ function checkDeclareStatement(context, statement) {
20514
20997
  }
20515
20998
  const type = context.resolveAnnotation(statement.annotation);
20516
20999
  context.declareModuleGlobal({ name: statement.name, type, isLocal: false, position: statement.position });
20517
- context.declarations.declareGlobal({ name: statement.name, type, position: statement.position });
21000
+ context.declarations.declareGlobal({ name: statement.name, type, isDeclared: true, position: statement.position });
21001
+ }
21002
+ function checkGlobalStatement(context, statement) {
21003
+ const declaration = statement.declaration;
21004
+ const declared = declaration.annotation === null ? ANY_TYPE : context.resolveValueAnnotation(declaration.annotation, "a global");
21005
+ const valueTypes = checkValueList(context, statement.values);
21006
+ const value = valueAt(statement.values, valueTypes, 0);
21007
+ if (context.insideFunction()) {
21008
+ const message = `A type on global "${declaration.name}" is only valid at the top level of a file. Assign it without the annotation here.`;
21009
+ context.report("check-global-annotation-scope", message, statement.position);
21010
+ return;
21011
+ }
21012
+ const existing = context.declarations.lookupGlobal(declaration.name);
21013
+ const contract = existing !== null && existing.isDeclared === true ? existing.type : declared;
21014
+ if (existing !== null && existing.isDeclared !== true) {
21015
+ context.report("check-duplicate-global", `Global "${declaration.name}" is already declared with a type.`, statement.position);
21016
+ }
21017
+ if (value !== void 0) {
21018
+ context.expectAssignable(valueTypes[0] ?? NIL_TYPE, contract, value.position, `Global "${declaration.name}"`);
21019
+ }
21020
+ context.forgetNarrowing(declaration.name);
21021
+ context.declareModuleGlobal({ name: declaration.name, type: contract, isLocal: false, position: declaration.position });
21022
+ if (existing === null) {
21023
+ context.declarations.declareGlobal({ name: declaration.name, type: contract, position: declaration.position });
21024
+ }
21025
+ if (value !== void 0) {
21026
+ recordAssignedFact(context, declaration.name, valueTypes[0] ?? NIL_TYPE, contract);
21027
+ }
20518
21028
  }
20519
21029
  function checkStatement(context, statement) {
20520
21030
  switch (statement.kind) {
@@ -20522,6 +21032,8 @@ function checkStatement(context, statement) {
20522
21032
  return checkLocal(context, statement);
20523
21033
  case "assignment-statement":
20524
21034
  return checkAssignment(context, statement);
21035
+ case "global-statement":
21036
+ return checkGlobalStatement(context, statement);
20525
21037
  case "call-statement":
20526
21038
  checkExpression(context, statement.expression);
20527
21039
  return;
@@ -20548,11 +21060,15 @@ function checkStatement(context, statement) {
20548
21060
  case "generic-for-statement":
20549
21061
  return checkGenericFor(context, statement);
20550
21062
  case "type-alias-statement": {
20551
- const resolved2 = context.resolveAnnotation(statement.annotation);
21063
+ const resolved2 = context.resolveValueAnnotation(statement.annotation, "a type alias");
20552
21064
  const shape = statement.annotation.kind === "type-object" || statement.annotation.kind === "type-intersection";
20553
21065
  const named2 = shape ? renameRecord(resolved2, statement.name) : resolved2;
21066
+ if (context.isAmbientAlias(statement.name)) {
21067
+ context.report("check-duplicate-type", `Type "${statement.name}" is already defined.`, statement.position);
21068
+ }
20554
21069
  context.noteTypeParameters(statement.typeParameters);
20555
21070
  context.binder.declareAlias(statement.name, named2, statement.typeParameters);
21071
+ context.declarations.declareAlias({ name: statement.name, typeParameters: statement.typeParameters, type: named2, position: statement.position });
20556
21072
  return;
20557
21073
  }
20558
21074
  case "declare-statement":
@@ -20590,6 +21106,19 @@ function templateLiteralText(segments) {
20590
21106
  return segments.map((segment) => segment.kind === "text" ? segment.value : `\${${segment.value}}`).join("");
20591
21107
  }
20592
21108
 
21109
+ // ../compiler/src/checker/value-distribution.ts
21110
+ function distributeValueTypes(types) {
21111
+ const distributed = [];
21112
+ types.forEach((type, index) => {
21113
+ if (index === types.length - 1) {
21114
+ distributed.push(...valuesOf(type));
21115
+ return;
21116
+ }
21117
+ distributed.push(firstValueOf(type));
21118
+ });
21119
+ return distributed;
21120
+ }
21121
+
20593
21122
  // ../compiler/src/checker/expressions.ts
20594
21123
  var NAME_PATH_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
20595
21124
  var EXTENSION_RESULTS = {
@@ -20633,7 +21162,8 @@ function checkMember(context, expression) {
20633
21162
  checkExpression(context, expression.object);
20634
21163
  return context.record(expression, library);
20635
21164
  }
20636
- const objectType = checkExpression(context, expression.object);
21165
+ const received = checkExpression(context, expression.object);
21166
+ const objectType = received.kind === "optional" ? received.element : received;
20637
21167
  if (objectType.kind === "record") {
20638
21168
  return context.record(expression, resolveRecordMember(context, objectType, expression));
20639
21169
  }
@@ -20656,26 +21186,44 @@ function checkMember(context, expression) {
20656
21186
  }
20657
21187
  return context.record(expression, extension === null ? ANY_TYPE : EXTENSION_RESULTS[extension.result]);
20658
21188
  }
21189
+ function spreadsUnknownArity(args, argumentTypes) {
21190
+ if (args.length === 0 || args.length !== argumentTypes.length) {
21191
+ return false;
21192
+ }
21193
+ return args[args.length - 1]?.kind === "call-expression" && argumentTypes[argumentTypes.length - 1]?.kind === "any";
21194
+ }
21195
+ function resolveKeyedMember(context, objectType, expression) {
21196
+ if (expression.index.kind !== "string-literal" || objectType.kind !== "record" && objectType.kind !== "named") {
21197
+ return null;
21198
+ }
21199
+ const access = { kind: "member-expression", object: expression.object, property: expression.index.value, position: expression.index.position };
21200
+ return objectType.kind === "record" ? resolveRecordMember(context, objectType, access) : resolveNamedMember(context, objectType, access);
21201
+ }
20659
21202
  function countArguments2(total) {
20660
21203
  return total === 1 ? "1 argument" : `${total} arguments`;
20661
21204
  }
20662
21205
  function checkSignature(context, args, declared, position2, owner = "This call", typeArguments = []) {
20663
21206
  const argumentTypes = checkValueList(context, args, declared.parameters);
20664
21207
  const signature2 = specializeCall(context, owner, declared, argumentTypes, typeArguments, position2);
20665
- if (argumentTypes.length < signature2.minimumArguments) {
21208
+ const spreads = spreadsUnknownArity(args, argumentTypes);
21209
+ const fixed = spreads ? argumentTypes.length - 1 : argumentTypes.length;
21210
+ if (!spreads && argumentTypes.length < signature2.minimumArguments) {
20666
21211
  const message = `This call expects at least ${countArguments2(signature2.minimumArguments)} but received ${argumentTypes.length}.`;
20667
21212
  context.report("check-argument-count", message, position2);
20668
21213
  }
20669
- if (!signature2.isVariadic && argumentTypes.length > signature2.parameters.length) {
21214
+ if (!signature2.isVariadic && fixed > signature2.parameters.length) {
20670
21215
  const message = `This call expects at most ${countArguments2(signature2.parameters.length)} but received ${argumentTypes.length}.`;
20671
21216
  context.report("check-argument-count", message, position2);
20672
21217
  }
20673
21218
  signature2.parameters.forEach((parameter, index) => {
20674
21219
  const argumentType = argumentTypes[index];
20675
21220
  const argument = args[index];
20676
- if (argumentType !== void 0 && argument !== void 0) {
20677
- context.expectAssignable(argumentType, parameter, argument.position, `Argument ${index + 1}`);
21221
+ if (argumentType === void 0 || argument === void 0) {
21222
+ return;
20678
21223
  }
21224
+ const optional = index >= signature2.minimumArguments && parameter.kind !== "optional";
21225
+ const expected = optional ? createOptional(parameter) : parameter;
21226
+ context.expectAssignable(argumentType, expected, argument.position, `Argument ${index + 1}`, parameter);
20679
21227
  });
20680
21228
  if (signature2.isVariadic && signature2.variadicType !== void 0) {
20681
21229
  for (let index = signature2.parameters.length; index < argumentTypes.length; index += 1) {
@@ -20688,6 +21236,20 @@ function checkSignature(context, args, declared, position2, owner = "This call",
20688
21236
  }
20689
21237
  return signature2.returnType;
20690
21238
  }
21239
+ function reportClassReceiver(context, name, method, position2) {
21240
+ if (context.declarations.lookupStaticMember(name, method) !== null) {
21241
+ const message2 = `Call the static member "${method}" as "${name}.${method}(...)". A class value has no "self" to pass.`;
21242
+ context.report("check-static-receiver", message2, position2);
21243
+ return;
21244
+ }
21245
+ if (context.declarations.lookupMember(name, method) === null) {
21246
+ const message2 = `Call the static member "${method}" as "${name}.${method}(...)". A class value has no "self" to pass.`;
21247
+ context.report("check-static-receiver", message2, position2);
21248
+ return;
21249
+ }
21250
+ const message = `The receiver here is the class "${name}" itself, and "${method}" is one of its instance members. Create an instance \u2014 "local value = new ${name}()" \u2014 and call "value:${method}(...)", or read the member from a value of that class. Where the class and its single instance share one name, give the instance its own name.`;
21251
+ context.report("check-class-receiver", message, position2);
21252
+ }
20691
21253
  function callOwner(expression) {
20692
21254
  const name = expression.method ?? (expression.callee.kind === "identifier" ? expression.callee.name : null);
20693
21255
  return name === null ? "This call" : `Function "${name}"`;
@@ -20759,9 +21321,7 @@ function checkCall(context, expression) {
20759
21321
  return context.record(expression, checkSignature(context, expression.args, constructor, expression.position));
20760
21322
  }
20761
21323
  if (expression.method !== null && expression.callee.kind === "identifier" && isUserClassReference(context, expression.callee.name)) {
20762
- const name = expression.callee.name;
20763
- const message = `Call the static member "${expression.method}" as "${name}.${expression.method}(...)". A class value has no "self" to pass.`;
20764
- context.report("check-static-receiver", message, expression.position);
21324
+ reportClassReceiver(context, expression.callee.name, expression.method, expression.position);
20765
21325
  checkValueList(context, expression.args);
20766
21326
  return context.record(expression, ANY_TYPE);
20767
21327
  }
@@ -20840,6 +21400,8 @@ function checkMultiValueExpression(context, expression, expected = null) {
20840
21400
  const symbol = context.binder.lookup(expression.name);
20841
21401
  if (symbol === null) {
20842
21402
  checkGlobalReference(context, expression.name, expression.position);
21403
+ } else {
21404
+ checkSharedReference(context, expression.name, expression.position);
20843
21405
  }
20844
21406
  if (symbol === null || symbol.isLocal !== true) {
20845
21407
  context.noteGlobalReference(expression.name);
@@ -20855,6 +21417,10 @@ function checkMultiValueExpression(context, expression, expected = null) {
20855
21417
  context.expectAssignable(indexType, objectType.key, expression.index.position, "Key");
20856
21418
  return context.record(expression, objectType.value);
20857
21419
  }
21420
+ const keyed = resolveKeyedMember(context, objectType, expression);
21421
+ if (keyed !== null) {
21422
+ return context.record(expression, keyed);
21423
+ }
20858
21424
  return context.record(expression, objectType.kind === "array" ? objectType.element : ANY_TYPE);
20859
21425
  }
20860
21426
  case "call-expression":
@@ -20884,16 +21450,12 @@ function checkExpression(context, expression, expected = null) {
20884
21450
  return firstValueOf(checkMultiValueExpression(context, expression, expected));
20885
21451
  }
20886
21452
  function checkValueList(context, values, expected = []) {
20887
- const types = [];
20888
- values.forEach((value, index) => {
21453
+ const types = values.map((value, index) => {
20889
21454
  const type = checkMultiValueExpression(context, value, expected[index] ?? null);
20890
- if (index === values.length - 1) {
20891
- types.push(...valuesOf(type));
20892
- return;
20893
- }
20894
- types.push(firstValueOf(type));
21455
+ escapesRecordObligation(context, value);
21456
+ return type;
20895
21457
  });
20896
- return types;
21458
+ return distributeValueTypes(types);
20897
21459
  }
20898
21460
 
20899
21461
  // ../compiler/src/checker/classes.ts
@@ -20902,7 +21464,7 @@ function fieldType(context, member) {
20902
21464
  if (member.annotation === null) {
20903
21465
  return valueType === null ? ANY_TYPE : widenInferred(valueType);
20904
21466
  }
20905
- const declared = context.resolveAnnotation(member.annotation);
21467
+ const declared = context.resolveValueAnnotation(member.annotation, "a field");
20906
21468
  if (valueType !== null && member.value !== null) {
20907
21469
  context.expectAssignable(valueType, declared, member.value.position, `Field "${member.name}"`);
20908
21470
  }
@@ -20996,6 +21558,7 @@ function declareBuilder(context, info, statement) {
20996
21558
  superClass: null,
20997
21559
  superArguments: [],
20998
21560
  interfaces: [],
21561
+ interfaceArguments: [],
20999
21562
  members,
21000
21563
  statics: /* @__PURE__ */ new Map(),
21001
21564
  position: statement.position
@@ -21023,8 +21586,9 @@ function checkMethodBody(context, info, member) {
21023
21586
  context.popClassMethod();
21024
21587
  }
21025
21588
  function assignSuperClass(context, info, statement) {
21026
- info.superClass = resolveSuperClass(context, statement);
21027
- info.hasUnresolvedParent = statement.superClass !== null && info.superClass === null;
21589
+ const resolved2 = resolveSuperClass(context, statement);
21590
+ info.superClass = resolved2 ?? statement.superClass;
21591
+ info.hasUnresolvedParent = statement.superClass !== null && resolved2 === null;
21028
21592
  }
21029
21593
  function resolveSuperClass(context, statement) {
21030
21594
  if (statement.superClass === null) {
@@ -21066,6 +21630,7 @@ function declareClassInfo(context, statement) {
21066
21630
  superClass: null,
21067
21631
  superArguments: [],
21068
21632
  interfaces: statement.interfaces,
21633
+ interfaceArguments: statement.interfaceArguments.map((args) => args.map((argument) => context.resolveAnnotation(argument))),
21069
21634
  members: /* @__PURE__ */ new Map(),
21070
21635
  statics: /* @__PURE__ */ new Map(),
21071
21636
  position: statement.position
@@ -21104,6 +21669,7 @@ function checkInterfaceDeclaration(context, statement) {
21104
21669
  context.report("check-duplicate-interface", `Interface "${statement.name}" is already defined.`, statement.position);
21105
21670
  return;
21106
21671
  }
21672
+ context.noteTypeParameters(statement.typeParameters);
21107
21673
  const members = /* @__PURE__ */ new Map();
21108
21674
  for (const name of new Set(statement.superInterfaces)) {
21109
21675
  const parent = context.declarations.lookupInterface(name);
@@ -21120,7 +21686,7 @@ function checkInterfaceDeclaration(context, statement) {
21120
21686
  context.report("check-duplicate-interface-parent", `Interface "${statement.name}" extends the same interface more than once.`, statement.position);
21121
21687
  }
21122
21688
  for (const member of statement.members) {
21123
- const type = member.kind === "interface-field" ? context.resolveAnnotation(member.annotation) : buildFunctionType(context, member.parameters, member.returnAnnotation);
21689
+ const type = member.kind === "interface-field" ? context.resolveValueAnnotation(member.annotation, "a member") : buildFunctionType(context, member.parameters, member.returnAnnotation);
21124
21690
  const info = { name: member.name, type, isMethod: member.kind === "interface-method", position: member.position };
21125
21691
  if (members.has(member.name)) {
21126
21692
  context.report("check-duplicate-interface-member", `Interface "${statement.name}" declares member "${member.name}" more than once.`, member.position);
@@ -21141,6 +21707,8 @@ function checkInterfaceDeclaration(context, statement) {
21141
21707
  }
21142
21708
  context.declarations.declareInterface({
21143
21709
  name: statement.name,
21710
+ typeParameters: statement.typeParameters,
21711
+ typeConstraints: statement.typeConstraints.map((constraint) => constraint === null ? null : context.resolveAnnotation(constraint)),
21144
21712
  superInterfaces: statement.superInterfaces,
21145
21713
  members,
21146
21714
  position: statement.position
@@ -21263,12 +21831,14 @@ function check(program2, mode, environment = DEFAULT_ENVIRONMENT, options = {})
21263
21831
  const ambient = options.ambient ?? EMPTY_AMBIENT;
21264
21832
  const project = options.project ?? EMPTY_PROJECT_DECLARATIONS;
21265
21833
  const context = new CheckContext(mode, environment, ambient, project, options.oop === true);
21834
+ context.noImplicitGlobals = options.noImplicitGlobals === true;
21266
21835
  context.isDeclarationFile = options.isDeclarationFile === true;
21267
21836
  context.contracts = options.contracts ?? [];
21268
21837
  const structure = context.isDeclarationFile ? checkDeclarationFile(program2.body) : checkJumps(program2.body);
21269
21838
  const directives = collectDirectives(program2.body, { isDeclarationFile: context.isDeclarationFile });
21270
21839
  predeclareModule(context, program2.body);
21271
21840
  checkStatements(context, program2.body);
21841
+ settleRecordObligations(context, /* @__PURE__ */ new Set());
21272
21842
  reportUnknownTypes(context);
21273
21843
  reportUnusedSymbols(context, options.noUnusedLocals === true, options.noUnusedParameters === true);
21274
21844
  const external = externalReferences(context);
@@ -21641,13 +22211,17 @@ function emitExpression(state, expression, limit = 0) {
21641
22211
  }
21642
22212
 
21643
22213
  // ../compiler/src/emitter/classes.ts
22214
+ var BARE_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
22215
+ function emitMemberKey(name) {
22216
+ return BARE_KEY.test(name) ? name : `[${emitString(name)}]`;
22217
+ }
21644
22218
  function emitMethod(state, className, member) {
21645
22219
  const self = { name: "self", annotation: null, isVararg: false, position: member.position };
21646
22220
  if (member.generated !== void 0) {
21647
22221
  return emitGeneratedMethod(state, className, member);
21648
22222
  }
21649
22223
  const parameters = member.isStatic ? member.parameters : [self, ...member.parameters];
21650
- return withSymbol(state, `${className}${member.isStatic ? "." : ":"}${member.name}`, () => emitFunctionBody(state, parameters, member.body, `${member.name} = function`));
22224
+ return withSymbol(state, `${className}${member.isStatic ? "." : ":"}${member.name}`, () => emitFunctionBody(state, parameters, member.body, `${emitMemberKey(member.name)} = function`));
21651
22225
  }
21652
22226
  function emitGeneratedMethod(state, className, member) {
21653
22227
  const fields = member.generated?.fields ?? [];
@@ -21733,7 +22307,7 @@ function emitMembers(state, statement) {
21733
22307
  continue;
21734
22308
  }
21735
22309
  const value = member.value === null ? "nil" : emitExpression(state, member.value);
21736
- entries3.push(indentLine(state, `${markSource(state, member.position.line, statement.name)}${member.name} = ${value}`));
22310
+ entries3.push(indentLine(state, `${markSource(state, member.position.line, statement.name)}${emitMemberKey(member.name)} = ${value}`));
21737
22311
  }
21738
22312
  for (const generated of state.generatedMembers.get(statement) ?? []) {
21739
22313
  entries3.push(indentLine(state, `${markSource(state, generated.position.line, `${statement.name}:${generated.name}`)}${emitMethod(state, statement.name, generated)}`));
@@ -21926,6 +22500,8 @@ function emitStatement(state, statement) {
21926
22500
  return emitLocal(state, statement);
21927
22501
  case "assignment-statement":
21928
22502
  return emitAssignment(state, statement);
22503
+ case "global-statement":
22504
+ return `${statement.declaration.name} = ${statement.values.map((value) => emitExpression(state, value)).join(", ")}`;
21929
22505
  case "call-statement":
21930
22506
  return emitExpression(state, statement.expression);
21931
22507
  case "function-declaration":
@@ -22133,6 +22709,9 @@ function nestedBlocks(statement) {
22133
22709
  case "assignment-statement":
22134
22710
  fromExpressions([...statement.targets, ...statement.values], blocks);
22135
22711
  break;
22712
+ case "global-statement":
22713
+ fromExpressions(statement.values, blocks);
22714
+ break;
22136
22715
  case "call-statement":
22137
22716
  fromExpression(statement.expression, blocks);
22138
22717
  break;
@@ -22824,6 +23403,7 @@ function compile(source, options = {}) {
22824
23403
  isDeclarationFile,
22825
23404
  oop: settings.oop,
22826
23405
  noUnusedLocals: settings.noUnusedLocals,
23406
+ noImplicitGlobals: settings.noImplicitGlobals,
22827
23407
  noUnusedParameters: settings.noUnusedParameters
22828
23408
  });
22829
23409
  const diagnostics = sortDiagnostics([...parsed.diagnostics, ...resolved2.diagnostics, ...checked.diagnostics]);
@@ -23067,11 +23647,12 @@ function createDependencyGraph(nodes) {
23067
23647
 
23068
23648
  // ../compiler/src/project/ambient-scope.ts
23069
23649
  function optionsKey(options) {
23070
- return `${options.strict ? "strict" : ""}|${options.oop ? "oop" : ""}|${options.noUnusedLocals ? "nul" : ""}|${options.noUnusedParameters ? "nup" : ""}`;
23650
+ const flags = [options.strict ? "strict" : "", options.oop ? "oop" : "", options.noUnusedLocals ? "nul" : "", options.noUnusedParameters ? "nup" : ""];
23651
+ return `${flags.join("|")}|${options.noImplicitGlobals ? "nig" : ""}`;
23071
23652
  }
23072
23653
  function declaresNothing(declarations) {
23073
- const { classes, interfaces, enums, globals, events } = declarations;
23074
- return classes.length === 0 && interfaces.length === 0 && enums.length === 0 && globals.length === 0 && events.length === 0;
23654
+ const { classes, interfaces, enums, aliases, globals, events } = declarations;
23655
+ return classes.length === 0 && interfaces.length === 0 && enums.length === 0 && aliases.length === 0 && globals.length === 0 && events.length === 0;
23075
23656
  }
23076
23657
  function sharedEnums(collected) {
23077
23658
  const declared = new Set(collected.flatMap((entry) => entry.declarations.enums.map((enumeration) => enumeration.name)));