@tilua/parser 3.0.1 → 4.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.
package/dist/index.js CHANGED
@@ -596,6 +596,8 @@ var UNUSED_EXPECT_ERROR = "Unused '@tilua-expect-error' directive";
596
596
 
597
597
  // src/ast/builders.ts
598
598
  var NO_DOTS = "tilua has no bare '...': take a rest parameter, '...args: T[]', or 'scriptArgs' for the script's own";
599
+ var NO_MEMBER_NAME = "Expected a name after '.'";
600
+ var NO_TYPEOF_CALL = "'typeof' is an operator, not a function: write 'typeof v'";
599
601
  var ACCESSIBILITIES = /* @__PURE__ */ new Set(["public", "private", "protected"]);
600
602
  var CLASS_MODIFIERS = /* @__PURE__ */ new Set([...ACCESSIBILITIES, "static", "abstract", "override", "readonly"]);
601
603
  function bindThis(func, at) {
@@ -603,7 +605,7 @@ function bindThis(func, at) {
603
605
  func.isMethod = true;
604
606
  }
605
607
  function bindThisParam(params, at) {
606
- params.unshift({ type: "FunctionParameter", name: "this", ...spanFrom(at, at) });
608
+ params.unshift({ type: "FunctionParameter", name: "this", implicit: true, ...spanFrom(at, at) });
607
609
  }
608
610
  var ParseError = class extends Error {
609
611
  constructor(message, line, column) {
@@ -1660,7 +1662,7 @@ var Parser = class {
1660
1662
  };
1661
1663
  }
1662
1664
  if (isMethod) {
1663
- func.params.unshift({ type: "FunctionParameter", name: "self", ...spanFrom(target, target) });
1665
+ func.params.unshift({ type: "FunctionParameter", name: "self", implicit: true, ...spanFrom(target, target) });
1664
1666
  func.isMethod = true;
1665
1667
  }
1666
1668
  return {
@@ -1854,47 +1856,7 @@ var Parser = class {
1854
1856
  const isStatic = modifiers.has("static");
1855
1857
  if (this.checkKeyword("function")) {
1856
1858
  this.advance();
1857
- const memberName = this.parseIdentifier();
1858
- if (modifiers.has("abstract")) {
1859
- const head = this.parseFunctionHead();
1860
- const func = {
1861
- type: "FunctionBody",
1862
- generics: head.generics,
1863
- params: head.params,
1864
- hasVarargs: head.hasVarargs,
1865
- returnType: head.returnType,
1866
- predicate: head.predicate,
1867
- body: { type: "Block", statements: [], ...spanFrom(head.start, this.previous()) },
1868
- ...spanFrom(head.start, this.previous())
1869
- };
1870
- if (!isStatic) bindThis(func, start);
1871
- if (!head.returnType) this.problem("An abstract method has no body to infer from: write its return type");
1872
- if (this.checkPunctuator("{")) this.problem("An abstract method has no body");
1873
- return { type: "ClassMethod", name: memberName, isStatic, func, isAbstract: true, ...spanFrom(start, this.previous()) };
1874
- }
1875
- const signatures = [];
1876
- let written = memberName;
1877
- while (true) {
1878
- const head = this.parseFunctionHead();
1879
- if (this.isClassOverloadContinuation(memberName.name, isStatic)) {
1880
- if (!isStatic) bindThisParam(head.params, start);
1881
- signatures.push({ ...this.headToSignature(head), name: written });
1882
- if (isStatic) this.advance();
1883
- this.expectKeyword("function");
1884
- written = this.parseIdentifier();
1885
- continue;
1886
- }
1887
- const func = this.headToBody(head, start);
1888
- if (!isStatic) bindThis(func, start);
1889
- return {
1890
- type: "ClassMethod",
1891
- name: memberName,
1892
- isStatic,
1893
- func,
1894
- signatures: signatures.length ? signatures : void 0,
1895
- ...spanFrom(start, this.previous())
1896
- };
1897
- }
1859
+ return this.parseClassMethod(start, modifiers);
1898
1860
  }
1899
1861
  if (!isStatic && this.checkIdentifierValue("constructor") && this.punctuatorAt(1, "(")) {
1900
1862
  this.advance();
@@ -1904,6 +1866,9 @@ var Parser = class {
1904
1866
  bindThis(func, start);
1905
1867
  return { type: "ClassConstructor", func, ...spanFrom(start, this.previous()) };
1906
1868
  }
1869
+ if (this.startsMethodShorthand(0)) {
1870
+ return this.parseClassMethod(start, modifiers);
1871
+ }
1907
1872
  if ((this.checkIdentifierValue("get") || this.checkIdentifierValue("set")) && this.peek(1).type === "Identifier" && this.punctuatorAt(2, "(")) {
1908
1873
  const kind = this.advance().value;
1909
1874
  const memberName = this.parseIdentifier();
@@ -1933,25 +1898,90 @@ var Parser = class {
1933
1898
  return { type: "ClassField", name: memberName, isStatic, typeAnnotation, init, ...spanFrom(start, this.previous()) };
1934
1899
  }
1935
1900
  if (this.recover) {
1936
- this.softError("Expected a class member: a field, 'function', 'constructor', 'get' or 'set'");
1901
+ this.softError("Expected a class member: a field, a method, 'constructor', 'get' or 'set'");
1937
1902
  this.advance();
1938
1903
  return void 0;
1939
1904
  }
1940
- this.error("Expected a class member: a field, 'function', 'constructor', 'get' or 'set'");
1905
+ this.error("Expected a class member: a field, a method, 'constructor', 'get' or 'set'");
1906
+ }
1907
+ /** A name at `offset` opening a method without `function`: `name(` or
1908
+ * `name<T>(`. A field's name is followed by `:` or `=` instead. */
1909
+ startsMethodShorthand(offset) {
1910
+ if (this.peek(offset).type !== "Identifier") return false;
1911
+ const next = this.peek(offset + 1);
1912
+ return (next.type === "Punctuator" || next.type === "Operator") && (next.value === "(" || next.value === "<");
1913
+ }
1914
+ /** A method, from its name on: `function` (if written) and the modifiers
1915
+ * are behind. */
1916
+ parseClassMethod(start, modifiers) {
1917
+ const isStatic = modifiers.has("static");
1918
+ const memberName = this.parseIdentifier();
1919
+ if (modifiers.has("abstract")) {
1920
+ const head = this.parseFunctionHead();
1921
+ const func = {
1922
+ type: "FunctionBody",
1923
+ generics: head.generics,
1924
+ params: head.params,
1925
+ hasVarargs: head.hasVarargs,
1926
+ returnType: head.returnType,
1927
+ predicate: head.predicate,
1928
+ body: { type: "Block", statements: [], ...spanFrom(head.start, this.previous()) },
1929
+ ...spanFrom(head.start, this.previous())
1930
+ };
1931
+ if (!isStatic) bindThis(func, start);
1932
+ if (!head.returnType) this.problem("An abstract method has no body to infer from: write its return type");
1933
+ if (this.checkPunctuator("{")) this.problem("An abstract method has no body");
1934
+ return { type: "ClassMethod", name: memberName, isStatic, func, isAbstract: true, ...spanFrom(start, this.previous()) };
1935
+ }
1936
+ const signatures = [];
1937
+ let written = memberName;
1938
+ while (true) {
1939
+ const head = this.parseFunctionHead();
1940
+ const skip = this.classOverloadContinuation(memberName.name, isStatic);
1941
+ if (skip !== void 0) {
1942
+ if (!isStatic) bindThisParam(head.params, start);
1943
+ signatures.push({ ...this.headToSignature(head), name: written });
1944
+ for (let i = 0; i < skip; i++) this.advance();
1945
+ written = this.parseIdentifier();
1946
+ continue;
1947
+ }
1948
+ const func = this.headToBody(head, start);
1949
+ if (!isStatic) bindThis(func, start);
1950
+ return {
1951
+ type: "ClassMethod",
1952
+ name: memberName,
1953
+ isStatic,
1954
+ func,
1955
+ signatures: signatures.length ? signatures : void 0,
1956
+ ...spanFrom(start, this.previous())
1957
+ };
1958
+ }
1941
1959
  }
1942
- /** Give a class method its `this`: a real first parameter, the way
1943
- * `function T:m()` gets a real `self`. Every later pass — scopes, types,
1944
- * arity, lowering — then sees an ordinary parameter and needs to know
1945
- * nothing about classes. */
1946
1960
  /** After a bodyless head inside a class body, does another declaration of
1947
- * the same member follow? Then the head was an overload signature. */
1948
- isClassOverloadContinuation(name, isStatic) {
1949
- const offset = isStatic ? 1 : 0;
1950
- if (isStatic && !this.checkIdentifierValue("static")) return false;
1961
+ * the same member follow? Then the head was an overload signature, and
1962
+ * this is how many tokens stand before the next one's name: a `;` that
1963
+ * ends the head, as TypeScript writes it, the modifiers, and `function`
1964
+ * when it is written. `undefined` when no overload follows. Each head may
1965
+ * be written either way, with `function` or without. */
1966
+ classOverloadContinuation(name, isStatic) {
1967
+ let offset = 0;
1968
+ if (this.punctuatorAt(0, ";")) offset++;
1969
+ let sawStatic = false;
1970
+ while (true) {
1971
+ const t = this.peek(offset);
1972
+ const word = t.type === "Identifier" ? t.value : void 0;
1973
+ if (word === void 0 || !CLASS_MODIFIERS.has(word) || this.startsMethodShorthand(offset)) break;
1974
+ if (word === "static") sawStatic = true;
1975
+ offset++;
1976
+ }
1977
+ if (sawStatic !== isStatic) return void 0;
1951
1978
  const keyword = this.peek(offset);
1952
- if (!(keyword.type === "Keyword" && keyword.value === "function")) return false;
1953
- const named = this.peek(offset + 1);
1954
- return named.type === "Identifier" && named.value === name;
1979
+ const hasKeyword = keyword.type === "Keyword" && keyword.value === "function";
1980
+ if (hasKeyword) offset++;
1981
+ const named = this.peek(offset);
1982
+ if (!(named.type === "Identifier" && named.value === name)) return void 0;
1983
+ if (!hasKeyword && !this.startsMethodShorthand(offset)) return void 0;
1984
+ return offset;
1955
1985
  }
1956
1986
  operatorAt(ahead, value) {
1957
1987
  const token = this.peek(ahead);
@@ -2105,6 +2135,7 @@ var Parser = class {
2105
2135
  * `:` Identifier followed by one of Lua's call forms. */
2106
2136
  startsMethodCall(offset = 0) {
2107
2137
  if (this.peek(offset + 1).type !== "Identifier") return false;
2138
+ if (this.startsOwnLine(this.peek(offset), this.peek(offset + 1))) return false;
2108
2139
  const after = this.peek(offset + 2);
2109
2140
  if (after.type === "Punctuator") {
2110
2141
  const v = String(after.value);
@@ -2130,6 +2161,23 @@ var Parser = class {
2130
2161
  const isString = t.type === "InterpolatedString" || t.type === "Literal" && t.kind === "string";
2131
2162
  return isString && this.punctuatorAt(1, ":") && this.startsMethodCall(1);
2132
2163
  }
2164
+ /** Is there no member name at `offset`, after the `.` or `:` before it? */
2165
+ namelessAfter(offset) {
2166
+ const name = this.peek(offset);
2167
+ if (name.type !== "Identifier") return true;
2168
+ const before = offset === 0 ? this.previous() : this.peek(offset - 1);
2169
+ return before !== void 0 && this.startsOwnLine(before, name);
2170
+ }
2171
+ /** Recovery only. `obj.` may go on to its name on a later line, as in
2172
+ * JavaScript — but while `obj.` is still being typed, the line below is
2173
+ * whatever statement was already there, and reading it as the name turns
2174
+ * one unfinished line into errors about the next. So, as a missing `}`
2175
+ * is placed, the indentation says which: a line that carries the access
2176
+ * on is indented deeper than the line it carries on from. */
2177
+ startsOwnLine(before, name) {
2178
+ if (!this.recover || name.line.start <= before.line.end) return false;
2179
+ return this.indentOf(name) <= this.indentOf(before);
2180
+ }
2133
2181
  /** Does the next token start right where the current one ends? */
2134
2182
  touchesNext() {
2135
2183
  const current = this.current();
@@ -2171,6 +2219,7 @@ var Parser = class {
2171
2219
  const t = this.current();
2172
2220
  if (t.type === "Keyword" && t.value === "not") return "not";
2173
2221
  if (t.type === "Operator" && (t.value === "-" || t.value === "#")) return t.value;
2222
+ if (t.type === "Identifier" && t.value === "typeof") return "typeof";
2174
2223
  return null;
2175
2224
  }
2176
2225
  /** Runs `fn` inside a bracket, where a `:` can only ever be a method
@@ -2270,6 +2319,10 @@ var Parser = class {
2270
2319
  const op = this.isUnaryOperator();
2271
2320
  if (op) {
2272
2321
  const opTok = this.advance();
2322
+ const next = this.current();
2323
+ if (op === "typeof" && this.checkPunctuator("(") && next.line.start === opTok.line.end && next.column.start === opTok.column.end) {
2324
+ this.problemAt(next, NO_TYPEOF_CALL);
2325
+ }
2273
2326
  const argument = this.parseExpression(UNARY_PRECEDENCE);
2274
2327
  return {
2275
2328
  type: "UnaryExpression",
@@ -2439,6 +2492,13 @@ var Parser = class {
2439
2492
  if (this.checkPunctuator("?") && this.touchesNext()) {
2440
2493
  const next = this.peek(1);
2441
2494
  const punct = next.type === "Punctuator" ? String(next.value) : void 0;
2495
+ if (punct === "." && this.recover && this.namelessAfter(2) && !this.punctuatorAt(2, "[") && !this.punctuatorAt(2, "(")) {
2496
+ this.advance();
2497
+ this.advance();
2498
+ this.problemAt(this.previous(), NO_MEMBER_NAME);
2499
+ base = { type: "ErrorExpression", expression: base, ...spanFrom(base, this.previous()) };
2500
+ break;
2501
+ }
2442
2502
  if (punct === "." && this.peek(2).type === "Identifier") {
2443
2503
  this.advance();
2444
2504
  this.advance();
@@ -2493,9 +2553,9 @@ var Parser = class {
2493
2553
  }
2494
2554
  }
2495
2555
  if (this.matchPunctuator(".")) {
2496
- if (this.recover && !this.checkType("Identifier")) {
2497
- this.softError("Expected identifier");
2498
- base = { type: "ErrorExpression", ...spanFrom(base, this.previous()) };
2556
+ if (this.namelessAfter(0)) {
2557
+ this.problemAt(this.previous(), NO_MEMBER_NAME);
2558
+ base = { type: "ErrorExpression", expression: base, ...spanFrom(base, this.previous()) };
2499
2559
  break;
2500
2560
  }
2501
2561
  const prop = this.parseIdentifier();
@@ -2508,6 +2568,12 @@ var Parser = class {
2508
2568
  base = { type: "IndexExpression", object: base, index, ...spanFrom(base, this.previous()) };
2509
2569
  continue;
2510
2570
  }
2571
+ if (this.recover && !this.ternaryColons && this.checkPunctuator(":") && this.peek(1).line.start > this.current().line.end && !this.startsMethodCall()) {
2572
+ this.advance();
2573
+ this.problemAt(this.previous(), "Expected a name after ':'");
2574
+ base = { type: "ErrorExpression", expression: base, ...spanFrom(base, this.previous()) };
2575
+ break;
2576
+ }
2511
2577
  if (this.checkPunctuator(":") && this.startsMethodCall() && this.takeMethodColon()) {
2512
2578
  this.advance();
2513
2579
  const method = this.parseIdentifier();
@@ -3351,6 +3417,7 @@ var Parser = class {
3351
3417
  return { type: "TupleTypeNode", elements, ...rest ? { rest } : {}, ...spanFrom(t, this.previous()) };
3352
3418
  }
3353
3419
  if (t.type === "Identifier" && t.value === "typeof" && this.peek(1).type === "Punctuator" && this.peek(1).value === "(") {
3420
+ this.problemAt(this.peek(1), NO_TYPEOF_CALL);
3354
3421
  this.advance();
3355
3422
  this.advance();
3356
3423
  const expression = this.parseExpression();
@@ -3731,7 +3798,7 @@ var BinaryOperators = [
3731
3798
  "and",
3732
3799
  "or"
3733
3800
  ];
3734
- var UnaryOperators = ["-", "not", "#"];
3801
+ var UnaryOperators = ["-", "not", "#", "typeof"];
3735
3802
 
3736
3803
  // src/ast/analyzeScopes.ts
3737
3804
  function getBinding(analysis, id) {
@@ -4358,7 +4425,9 @@ var Analyzer = class {
4358
4425
  case "BooleanLiteral":
4359
4426
  case "NumberLiteral":
4360
4427
  case "StringLiteral":
4428
+ return;
4361
4429
  case "ErrorExpression":
4430
+ if (expr.expression) this.visitExpression(expr.expression, scope);
4362
4431
  return;
4363
4432
  case "InterpolatedStringExpression":
4364
4433
  for (const part of expr.parts) {
@@ -4571,11 +4640,34 @@ declare metatable<T> T[]: {
4571
4640
  -- \`nil\` when it is missing; an indexer's keys follow the named ones. A
4572
4641
  -- member of the table's own with one of these names is what a call reaches,
4573
4642
  -- and a class instance, which has a metatable of its own, has none of them.
4643
+ --
4644
+ -- The rest are JavaScript's \`Object\` functions, written as methods of the
4645
+ -- table they take first:
4646
+ --
4647
+ -- point:hasOwn("x") -- Object.hasOwn(point, "x")
4648
+ -- point:assign({ z: 3 }) -- Object.assign(point, { z: 3 })
4649
+ -- point:freeze() -- Object.freeze(point)
4650
+ --
4651
+ -- \`hasOwn\` asks the table itself, not what its metatable's \`__index\`
4652
+ -- lends it. \`assign\` copies each source's members onto the table, later ones
4653
+ -- over earlier ones, skipping a \`nil\` source, and answers the table. \`freeze\`
4654
+ -- is Luau's \`table.freeze\`; under Lua 5.1, which cannot freeze a table, the
4655
+ -- table is answered as it is and \`isFrozen\` is false.
4574
4656
  declare metatable<T extends {}> T: {
4575
4657
  __index: {
4576
4658
  keys: (self: T) => ObjectKeys<T>,
4577
4659
  values: (self: T) => ObjectValues<T>,
4578
4660
  entries: (self: T) => ObjectEntries<T>,
4661
+
4662
+ hasOwn: (self: T, key: unknown) => boolean,
4663
+ hasOwnProperty: (self: T, key: unknown) => boolean,
4664
+ assign: (<U>(self: T, source: U) => T & U)
4665
+ & (<U, V>(self: T, a: U, b: V) => T & U & V)
4666
+ & (<U, V, W>(self: T, a: U, b: V, c: W) => T & U & V & W)
4667
+ -- Generic like the rest: a plain signature would be tried first.
4668
+ & (<S extends {}>(self: T, ...sources: S[]) => T),
4669
+ freeze: (self: T) => Readonly<T>,
4670
+ isFrozen: (self: T) => boolean,
4579
4671
  },
4580
4672
  }
4581
4673
  `;
@@ -5641,6 +5733,13 @@ function mergeEnv(a, b, base) {
5641
5733
  }
5642
5734
  return out;
5643
5735
  }
5736
+ function joinNarrowings(into, a, aFrom, b, bFrom) {
5737
+ for (const [key, left] of a) {
5738
+ const right = b.get(key);
5739
+ if (right === void 0 || left === aFrom.get(key) || right === bFrom.get(key)) continue;
5740
+ into.set(key, union([left, right]));
5741
+ }
5742
+ }
5644
5743
  function mergeIndexer(a, b) {
5645
5744
  return a ? { key: union([a.key, b.key]), value: union([a.value, b.value]) } : b;
5646
5745
  }
@@ -5920,6 +6019,10 @@ function unwrapParens(e) {
5920
6019
  while (e.type === "ParenthesizedExpression") e = e.expression;
5921
6020
  return e;
5922
6021
  }
6022
+ function memberLabel(label, key) {
6023
+ if (label === void 0 || key === void 0) return void 0;
6024
+ return typeof key === "number" ? `${label}[${key}]` : `${label}.${key}`;
6025
+ }
5923
6026
  function expressionLabel(e, depth = 0) {
5924
6027
  if (depth > 6) return void 0;
5925
6028
  const args = (list) => {
@@ -5991,6 +6094,7 @@ var TypeAnalyzer = class {
5991
6094
  metatables = [];
5992
6095
  metatableCache = /* @__PURE__ */ new WeakMap();
5993
6096
  methodSources = /* @__PURE__ */ new Map();
6097
+ typeofTables = /* @__PURE__ */ new Map();
5994
6098
  /** Public: each alias resolved once (generic aliases keep their params as
5995
6099
  * `typeParam` nodes in the body). */
5996
6100
  aliases = new LazyMap();
@@ -6069,7 +6173,7 @@ var TypeAnalyzer = class {
6069
6173
  const given = this.options.globalTypes?.[name];
6070
6174
  if (given) this.bindingType.set(id, given);
6071
6175
  else if (this.libGlobalTypes.has(name)) this.bindingType.defer(id, () => this.libGlobalTypes.get(name) ?? anyType);
6072
- else if (LANGUAGE_GLOBALS.includes(name)) this.bindingType.set(id, arrayOf(unknownType));
6176
+ else if (LANGUAGE_GLOBALS.includes(name)) this.bindingType.set(id, arrayOf(declaredUnknownType));
6073
6177
  else this.bindingType.set(id, anyType);
6074
6178
  }
6075
6179
  setAliasExpander((t) => this.expand(t));
@@ -6092,6 +6196,7 @@ var TypeAnalyzer = class {
6092
6196
  aliases: this.resolveDeferredAliases(),
6093
6197
  loops: this.loops,
6094
6198
  methodSources: this.methodSources,
6199
+ typeofTables: this.typeofTables,
6095
6200
  metatableMembers: (type) => this.metatableMembers(type),
6096
6201
  diagnostics: this.diagnostics
6097
6202
  };
@@ -6942,7 +7047,12 @@ var TypeAnalyzer = class {
6942
7047
  this.aliases.defer(name, () => this.classType(cls));
6943
7048
  continue;
6944
7049
  }
6945
- if (this.dependsOnTypeQuery(name)) continue;
7050
+ if (this.dependsOnTypeQuery(name)) {
7051
+ if (def.runtimeClass) {
7052
+ this.aliases.defer(name, () => this.withTypeParams(def.params, () => this.resolveDef(def)));
7053
+ }
7054
+ continue;
7055
+ }
6946
7056
  this.withTypeParams(def.params, () => {
6947
7057
  this.aliases.set(name, this.resolveDef(def));
6948
7058
  });
@@ -6950,14 +7060,16 @@ var TypeAnalyzer = class {
6950
7060
  }
6951
7061
  typeQueryDependents = /* @__PURE__ */ new Map();
6952
7062
  /** Does alias `name` contain a `typeof`, itself or through an alias it
6953
- * names? */
7063
+ * names? Its parameters count: `class C<K extends Config["key"]>`
7064
+ * resolves the bound along with the class. */
6954
7065
  dependsOnTypeQuery(name, visiting = /* @__PURE__ */ new Set()) {
6955
7066
  const known = this.typeQueryDependents.get(name);
6956
7067
  if (known !== void 0) return known;
6957
7068
  const def = this.aliasDefs.get(name);
6958
7069
  if (!def || def.class || visiting.has(name)) return false;
6959
7070
  visiting.add(name);
6960
- const result = containsTypeQuery(def.node) || referencedTypeNames(def.node).some((other) => other !== name && this.dependsOnTypeQuery(other, visiting));
7071
+ const nodes = [def.node, ...def.params.flatMap((p) => [p.constraint, p.default])];
7072
+ const result = nodes.some(containsTypeQuery) || referencedTypeNames(nodes).some((other) => other !== name && this.dependsOnTypeQuery(other, visiting));
6961
7073
  visiting.delete(name);
6962
7074
  this.typeQueryDependents.set(name, result);
6963
7075
  return result;
@@ -7682,7 +7794,7 @@ var TypeAnalyzer = class {
7682
7794
  }
7683
7795
  }
7684
7796
  const mode = this.initIsAsConst(source) ? "asconst" : !isFreshLiteralExpr(source) ? "keep" : stmt.kind === "const" ? "const" : "widen";
7685
- this.bindPattern(target, inferred, env, mode);
7797
+ this.bindPattern(target, inferred, env, mode, source && expressionLabel(source));
7686
7798
  if (stmt.kind === "const") {
7687
7799
  this.correlateDestructuring(target, inferred, env);
7688
7800
  this.correlateIndexed(target, source, env);
@@ -7775,9 +7887,10 @@ var TypeAnalyzer = class {
7775
7887
  } else if (target.type === "MemberExpression" || target.type === "IndexExpression") {
7776
7888
  this.infer(target, env);
7777
7889
  this.checkReadonlyAssign(target, env);
7890
+ this.checkMemberAssign(stmt, target, vt, env);
7778
7891
  this.assignToRef(target, isFreshLiteralExpr(source) ? widen(vt) : vt, env);
7779
7892
  } else if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
7780
- this.reassignPattern(target, vt, env);
7893
+ this.reassignPattern(target, vt, env, expressionLabel(source));
7781
7894
  }
7782
7895
  return;
7783
7896
  }
@@ -9143,7 +9256,8 @@ var TypeAnalyzer = class {
9143
9256
  }
9144
9257
  /** Like `bindPattern`, but the leaves are *existing* bindings (a `{a} = t`
9145
9258
  * assignment). Updates their flow type; keeps a declared annotation. */
9146
- reassignPattern(target, valueType, env) {
9259
+ reassignPattern(target, valueType, env, label) {
9260
+ if (target.type === "ObjectPattern" || target.type === "ArrayPattern") valueType = this.destructured(target, valueType, label);
9147
9261
  switch (target.type) {
9148
9262
  case "IdentifierPattern": {
9149
9263
  const id = this.scopes.bindingOf.get(target);
@@ -9168,21 +9282,43 @@ var TypeAnalyzer = class {
9168
9282
  for (const p of target.properties) {
9169
9283
  const key = !p.computed && p.key.type === "Identifier" ? p.key.name : !p.computed && p.key.type === "StringLiteral" ? p.key.value : void 0;
9170
9284
  const pt = key !== void 0 ? this.propertyType(valueType, key) : unknownType;
9171
- this.reassignPattern(p.value, this.withDefault(pt, p.default, env), env);
9285
+ this.reassignPattern(p.value, this.withDefault(pt, p.default, env), env, memberLabel(label, key));
9172
9286
  }
9173
9287
  if (target.rest) this.reassignPattern(target.rest, this.withoutKeys(valueType, target.properties), env);
9174
9288
  return;
9175
9289
  }
9176
9290
  case "ArrayPattern": {
9177
9291
  target.elements.forEach((el, i) => {
9178
- if (el) this.reassignPattern(el.value, this.withDefault(this.elementType(valueType, i), el.default, env), env);
9292
+ if (el) this.reassignPattern(el.value, this.withDefault(this.elementType(valueType, i), el.default, env), env, memberLabel(label, i + 1));
9179
9293
  });
9180
9294
  if (target.rest) this.reassignPattern(target.rest, this.restAfter(valueType, target.elements.length), env);
9181
9295
  return;
9182
9296
  }
9183
9297
  }
9184
9298
  }
9185
- bindPattern(target, valueType, env, mode) {
9299
+ destructuredReported = /* @__PURE__ */ new WeakSet();
9300
+ /** What a pattern reads from. `const { a } = v` is `v.a`, and fails where
9301
+ * that would: on a `v` that may be nil, and on one that is `unknown`.
9302
+ * `label` is the value as it would be written — `args[1]` for the inner
9303
+ * pattern of `const [{ a }] = args`. */
9304
+ destructured(pattern, raw, label) {
9305
+ let type = raw;
9306
+ const report = (message) => {
9307
+ if (!this.emitDiagnostics || this.destructuredReported.has(pattern)) return;
9308
+ this.destructuredReported.add(pattern);
9309
+ this.diagnostics.push({ node: pattern, message: `${label === void 0 ? "Object" : `'${label}'`} ${message}` });
9310
+ };
9311
+ if (this.includesNil(type)) {
9312
+ const t = this.expand(type);
9313
+ report(t.kind === "primitive" ? "is nil" : "is possibly nil. Check it first, or give it a default");
9314
+ type = withoutNil(t);
9315
+ }
9316
+ const read = this.expand(type);
9317
+ if (read.kind === "unknown" && read.declared) report("is of type 'unknown'");
9318
+ return type;
9319
+ }
9320
+ bindPattern(target, valueType, env, mode, label) {
9321
+ if (target.type === "ObjectPattern" || target.type === "ArrayPattern") valueType = this.destructured(target, valueType, label);
9186
9322
  switch (target.type) {
9187
9323
  case "IdentifierPattern": {
9188
9324
  const id = this.bindingIdByName(target.name, target);
@@ -9203,7 +9339,7 @@ var TypeAnalyzer = class {
9203
9339
  for (const p of target.properties) {
9204
9340
  const key = !p.computed && p.key.type === "Identifier" ? p.key.name : !p.computed && p.key.type === "StringLiteral" ? p.key.value : void 0;
9205
9341
  const propType = key !== void 0 ? this.propertyType(valueType, key) : unknownType;
9206
- this.bindPattern(p.value, this.withDefault(propType, p.default, env), env, mode);
9342
+ this.bindPattern(p.value, this.withDefault(propType, p.default, env), env, mode, memberLabel(label, key));
9207
9343
  }
9208
9344
  if (target.rest) this.bindPattern(target.rest, this.withoutKeys(valueType, target.properties), env, mode);
9209
9345
  return;
@@ -9211,7 +9347,7 @@ var TypeAnalyzer = class {
9211
9347
  case "ArrayPattern": {
9212
9348
  target.elements.forEach((el, i) => {
9213
9349
  if (!el) return;
9214
- this.bindPattern(el.value, this.withDefault(this.elementType(valueType, i), el.default, env), env, mode);
9350
+ this.bindPattern(el.value, this.withDefault(this.elementType(valueType, i), el.default, env), env, mode, memberLabel(label, i + 1));
9215
9351
  });
9216
9352
  if (target.rest) this.bindPattern(target.rest, this.restAfter(valueType, target.elements.length), env, mode);
9217
9353
  return;
@@ -9776,6 +9912,7 @@ var TypeAnalyzer = class {
9776
9912
  }
9777
9913
  // Broken syntax is reported by the parser; nothing more to say.
9778
9914
  case "ErrorExpression":
9915
+ if (expr.expression) this.infer(expr.expression, env);
9779
9916
  return anyType;
9780
9917
  case "Identifier": {
9781
9918
  const id = this.bindingIdOf(expr);
@@ -9839,6 +9976,14 @@ var TypeAnalyzer = class {
9839
9976
  this.checkOperands(expr, "#", arg);
9840
9977
  return numberType;
9841
9978
  }
9979
+ case "typeof": {
9980
+ const names = this.typeofNamesOf(arg);
9981
+ const tables = names && [...names].filter((name) => name === "array" || name === "object");
9982
+ if (tables?.length === 1) this.typeofTables.set(expr, tables[0]);
9983
+ else this.typeofTables.delete(expr);
9984
+ const answers = names?.size ? [...names] : [...this.typeofTypes().keys()];
9985
+ return union(answers.map((name) => literal(name)));
9986
+ }
9842
9987
  }
9843
9988
  return arg;
9844
9989
  }
@@ -9881,7 +10026,9 @@ var TypeAnalyzer = class {
9881
10026
  return booleanType;
9882
10027
  case "==":
9883
10028
  case "~=":
9884
- this.checkOverlap(expr, op, l, r);
10029
+ if (!this.checkTypeofName(expr, expr.left, expr.right) && !this.checkTypeofName(expr, expr.right, expr.left)) {
10030
+ this.checkOverlap(expr, op, l, r);
10031
+ }
9885
10032
  return booleanType;
9886
10033
  case "+":
9887
10034
  case "-":
@@ -10082,6 +10229,7 @@ var TypeAnalyzer = class {
10082
10229
  const fromMetatable = this.receiverMetatableMember(objType, expr.method.name);
10083
10230
  if (fromMetatable) this.methodSources.set(expr, fromMetatable.source);
10084
10231
  const method = fromMetatable?.type ?? this.propertyType(objType, expr.method.name);
10232
+ this.typeOf.set(expr.method, method);
10085
10233
  const united = this.unionSignatures(method);
10086
10234
  const fns = united ?? this.overloadsOf(method);
10087
10235
  const explicit = this.explicitTypeArguments(expr, fns);
@@ -10222,6 +10370,43 @@ var TypeAnalyzer = class {
10222
10370
  message: `Cannot assign to '${name}' because it is a read-only property`
10223
10371
  });
10224
10372
  }
10373
+ /** `t.x = v` against what `x` was declared as. Only a property the
10374
+ * table's type names is checked: a key it does not name is the Lua
10375
+ * table growing, which `checkMissingMember` is not about. */
10376
+ checkMemberAssign(stmt, target, value, env) {
10377
+ if (!this.emitDiagnostics) return;
10378
+ let name;
10379
+ if (target.type === "MemberExpression") {
10380
+ name = target.property.name;
10381
+ } else {
10382
+ const index = this.expand(this.infer(target.index, env));
10383
+ if (index.kind !== "literal" || typeof index.value !== "string") return;
10384
+ name = index.value;
10385
+ }
10386
+ if (target.object.type === "Identifier") {
10387
+ const id = this.bindingIdOf(target.object);
10388
+ if (id !== void 0 && this.scopes.bindings.get(id)?.declaredBy === "namespace") return;
10389
+ }
10390
+ const declared = this.declaredProperty(this.expand(this.infer(target.object, env)), name);
10391
+ if (!declared || declared.kind === "any" || declared.kind === "unknown") return;
10392
+ if (this.fitsAnnotation(stmt.value, declared, value, env)) return;
10393
+ this.diagnostics.push({
10394
+ node: stmt,
10395
+ message: `Type '${briefType(value)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(value, declared)
10396
+ });
10397
+ }
10398
+ /** The type a property was declared with, where the type names it. */
10399
+ declaredProperty(t, name) {
10400
+ if (t.kind === "object") {
10401
+ const p = t.properties.get(name);
10402
+ return p && (p.optional ? optional(p.type) : p.type);
10403
+ }
10404
+ if (t.kind === "intersection") {
10405
+ const parts = t.types.flatMap((m) => this.declaredProperty(this.expand(m), name) ?? []);
10406
+ return parts.length ? intersection(parts) : void 0;
10407
+ }
10408
+ return void 0;
10409
+ }
10225
10410
  isReadonlyProperty(t, name) {
10226
10411
  if (t.kind === "object") return t.properties.get(name)?.readonly === true;
10227
10412
  if (t.kind === "intersection" || t.kind === "union") {
@@ -10617,13 +10802,23 @@ var TypeAnalyzer = class {
10617
10802
  if (cond.type === "BinaryExpression") {
10618
10803
  const { operator: op, left, right } = cond;
10619
10804
  if (op === "and") {
10620
- this.applyNarrowing(left, env, t, forkEnv(env));
10621
- this.applyNarrowing(right, t, t, forkEnv(t));
10805
+ const before = forkEnv(env);
10806
+ const leftFailed = forkEnv(env);
10807
+ this.applyNarrowing(left, env, t, leftFailed);
10808
+ const between = forkEnv(t);
10809
+ const rightFailed = forkEnv(t);
10810
+ this.applyNarrowing(right, t, t, rightFailed);
10811
+ joinNarrowings(f, leftFailed, before, rightFailed, between);
10622
10812
  return;
10623
10813
  }
10624
10814
  if (op === "or") {
10625
- this.applyNarrowing(left, env, forkEnv(env), f);
10626
- this.applyNarrowing(right, f, forkEnv(f), f);
10815
+ const before = forkEnv(env);
10816
+ const leftHeld = forkEnv(env);
10817
+ this.applyNarrowing(left, env, leftHeld, f);
10818
+ const between = forkEnv(f);
10819
+ const rightHeld = forkEnv(f);
10820
+ this.applyNarrowing(right, f, rightHeld, f);
10821
+ joinNarrowings(t, leftHeld, before, rightHeld, between);
10627
10822
  return;
10628
10823
  }
10629
10824
  if (op === "==" || op === "~=") {
@@ -10642,11 +10837,13 @@ var TypeAnalyzer = class {
10642
10837
  this.narrowOptionalLinks(cond, env, t);
10643
10838
  }
10644
10839
  /** `a == b` / `a ~= b`. Handles, in order: a declaration-driven
10645
- * `typeof(x) == "..."` test, a literal/`nil` comparison against a
10840
+ * `typeof x == "..."` test, a literal/`nil` comparison against a
10646
10841
  * reference, and a reference-to-reference comparison. */
10647
10842
  narrowByComparison(left, right, eq, env, t, f) {
10648
10843
  const yes = eq ? t : f;
10649
10844
  const no = eq ? f : t;
10845
+ if (this.narrowByTypeof(left, right, yes, no, env)) return;
10846
+ if (this.narrowByTypeof(right, left, yes, no, env)) return;
10650
10847
  if (this.narrowByCallResult(left, right, yes, no, env)) return;
10651
10848
  if (this.narrowByCallResult(right, left, yes, no, env)) return;
10652
10849
  const litOf = (e) => {
@@ -10673,18 +10870,149 @@ var TypeAnalyzer = class {
10673
10870
  this.narrowRef(right, env, yes, no, (cur) => ({ yes: narrowTo(cur, lt), no: cur }));
10674
10871
  }
10675
10872
  }
10676
- /** Declaration-driven `typeof(x) == "number"`.
10677
- *
10678
- * `typeof` is not special-cased: it is declared in the definitions as an
10679
- * overload set whose members return *string-literal* types
10680
- * (`(value: number) -> "number"`, ...). So for `f(x) == "lit"` we keep the
10681
- * parameter types of the overloads that can return `"lit"`, and that union
10682
- * is what `x` narrows to. Any user function declared the same way gets the
10683
- * same treatment for free.
10873
+ // --------------------------------------------------------
10874
+ // typeof
10875
+ // --------------------------------------------------------
10876
+ //
10877
+ // `typeof v` answers what Luau's `typeof` does, except for a table: that
10878
+ // is an `"array"` or an `"object"`. The names of a runtime's own values —
10879
+ // Roblox's `"Instance"`, `"Vector3"` — are a library's to give: it
10880
+ // declares `type TypeofNames = { Vector3: Vector3, Instance: Instance }`,
10881
+ // each property the name `typeof` answers for a value of that type. A
10882
+ // value several of them fit is the first one's.
10883
+ typeofTypesCache;
10884
+ /** Every name `typeof` answers, and what a value it answered that for
10885
+ * is — `unknown` where nothing more is known. */
10886
+ typeofTypes() {
10887
+ if (this.typeofTypesCache) return this.typeofTypesCache;
10888
+ const types = /* @__PURE__ */ new Map([
10889
+ ["nil", nilType],
10890
+ ["boolean", booleanType],
10891
+ ["number", numberType],
10892
+ ["string", stringType],
10893
+ ["function", fn([], declaredUnknownType, declaredUnknownType)],
10894
+ ["array", arrayOf(declaredUnknownType)],
10895
+ ["object", objectType([], { key: declaredUnknownType, value: declaredUnknownType })],
10896
+ ["thread", threadType],
10897
+ ["buffer", bufferType],
10898
+ ["userdata", unknownType]
10899
+ ]);
10900
+ for (const [name, type] of this.typeofLibraryNames()) if (!types.has(name)) types.set(name, type);
10901
+ return this.typeofTypesCache = types;
10902
+ }
10903
+ typeofLibraryCache;
10904
+ typeofLibraryNames() {
10905
+ if (this.typeofLibraryCache) return this.typeofLibraryCache;
10906
+ const declared = this.aliases.get("TypeofNames");
10907
+ const names = declared && this.expand(declared);
10908
+ return this.typeofLibraryCache = names?.kind === "object" ? [...names.properties].map(([name, property]) => [name, property.type]) : [];
10909
+ }
10910
+ /** The names `typeof` can answer for a value of type `raw`, or
10911
+ * `undefined` where the type does not say. */
10912
+ typeofNamesOf(raw, depth = 0) {
10913
+ if (depth > 8) return void 0;
10914
+ const t = this.expand(raw);
10915
+ switch (t.kind) {
10916
+ case "never":
10917
+ return /* @__PURE__ */ new Set();
10918
+ case "primitive":
10919
+ return /* @__PURE__ */ new Set([t.name]);
10920
+ case "literal":
10921
+ return /* @__PURE__ */ new Set([t.base]);
10922
+ case "templateLiteral":
10923
+ return /* @__PURE__ */ new Set(["string"]);
10924
+ case "array":
10925
+ case "tuple":
10926
+ return /* @__PURE__ */ new Set(["array"]);
10927
+ case "function":
10928
+ return /* @__PURE__ */ new Set(["function"]);
10929
+ case "object": {
10930
+ const named = t.class && this.typeofLibraryNames().find(([, type]) => isAssignable(t, type));
10931
+ return /* @__PURE__ */ new Set([named ? named[0] : "object"]);
10932
+ }
10933
+ case "union": {
10934
+ const all = /* @__PURE__ */ new Set();
10935
+ for (const member of t.types) {
10936
+ const names = this.typeofNamesOf(member, depth + 1);
10937
+ if (!names) return void 0;
10938
+ for (const name of names) all.add(name);
10939
+ }
10940
+ return all;
10941
+ }
10942
+ case "intersection": {
10943
+ const known = t.types.map((part) => this.typeofNamesOf(part, depth + 1)).filter((names) => names !== void 0);
10944
+ if (!known.length) return void 0;
10945
+ const common = [...known[0]].filter((name) => known.every((names) => names.has(name)));
10946
+ return new Set(common.length ? common : known[0]);
10947
+ }
10948
+ case "typeParam":
10949
+ return t.constraint ? this.typeofNamesOf(t.constraint, depth + 1) : void 0;
10950
+ case "difference":
10951
+ return this.typeofNamesOf(t.base, depth + 1);
10952
+ default:
10953
+ return void 0;
10954
+ }
10955
+ }
10956
+ /** `typeof v == "table"`: a name `typeof` never answers. Returns true if
10957
+ * the comparison needs no further checking. */
10958
+ checkTypeofName(node, typeofSide, nameSide) {
10959
+ const test = unwrapParens(typeofSide);
10960
+ if (test.type !== "UnaryExpression" || test.operator !== "typeof") return false;
10961
+ const name = this.asLiteral(unwrapParens(nameSide));
10962
+ if (typeof name !== "string") return false;
10963
+ if (name === "nil") return true;
10964
+ if (this.typeofTypes().has(name)) return false;
10965
+ if (this.emitDiagnostics) {
10966
+ this.diagnostics.push({
10967
+ node,
10968
+ message: name === "table" ? `'typeof' never answers "table": a table is an "array" or an "object"` : `'typeof' never answers "${name}"`
10969
+ });
10970
+ }
10971
+ return true;
10972
+ }
10973
+ /** `typeof x == "number"`: `x` is what answers that name where the test
10974
+ * holds, and what does not where it fails. Returns true if `testSide` is
10975
+ * a `typeof` compared with a name. */
10976
+ narrowByTypeof(testSide, nameSide, yes, no, env) {
10977
+ const test = unwrapParens(testSide);
10978
+ if (test.type !== "UnaryExpression" || test.operator !== "typeof") return false;
10979
+ const name = this.asLiteral(unwrapParens(nameSide));
10980
+ if (typeof name !== "string") return false;
10981
+ const answered = this.typeofTypes().get(name);
10982
+ if (!answered) return true;
10983
+ const opaque = answered.kind !== "unknown";
10984
+ const subtracts = opaque && name !== "array" && name !== "object";
10985
+ const members = (cur) => {
10986
+ const t = this.expand(cur);
10987
+ return t.kind === "union" ? t.types.flatMap(members) : [t];
10988
+ };
10989
+ if (this.refKeyOf(test.argument) !== void 0) {
10990
+ this.narrowRef(test.argument, env, yes, no, (cur) => {
10991
+ const kept = [];
10992
+ const dropped = [];
10993
+ for (const member of members(cur)) {
10994
+ const names = this.typeofNamesOf(member);
10995
+ if (!names) {
10996
+ kept.push(opaque ? narrowTo(member, answered) : member);
10997
+ dropped.push(subtracts ? narrowExclude(member, answered) : member);
10998
+ continue;
10999
+ }
11000
+ if (names.has(name)) kept.push(member);
11001
+ if (!(names.size === 1 && names.has(name))) dropped.push(member);
11002
+ }
11003
+ return { yes: union(kept), no: union(dropped) };
11004
+ });
11005
+ }
11006
+ this.narrowOptionalLinks(test.argument, env, name === "nil" ? no : yes);
11007
+ return true;
11008
+ }
11009
+ /** Declaration-driven `kind(x) == "number"`.
10684
11010
  *
10685
- * The analyzer knows nothing about the names `type` and `typeof`: with no
10686
- * definitions loaded there are no literal-returning overloads and this
10687
- * narrows nothing. Returns true if it handled the comparison. */
11011
+ * A function declared as an overload set whose members return
11012
+ * *string-literal* types (`(value: number) => "number"`, ...) says by its
11013
+ * answer what it was given. So for `f(x) == "lit"` we keep the parameter
11014
+ * types of the overloads that can return `"lit"`, and that union is what
11015
+ * `x` narrows to. Returns true if it handled the comparison. */
10688
11016
  narrowByCallResult(callSide, litSide, yes, no, env) {
10689
11017
  if (callSide.type !== "CallExpression" || callSide.arguments.length !== 1) return false;
10690
11018
  const name = this.asLiteral(litSide);
@@ -11761,7 +12089,53 @@ function aliasTargets(config, specifier) {
11761
12089
 
11762
12090
  // src/project/sourcemap.ts
11763
12091
  import { dirname as dirname4, extname, resolve as resolve4 } from "path";
12092
+
12093
+ // src/luau.ts
12094
+ var LUAU_KEYWORDS = /* @__PURE__ */ new Set([
12095
+ "and",
12096
+ "break",
12097
+ "do",
12098
+ "else",
12099
+ "elseif",
12100
+ "end",
12101
+ "false",
12102
+ "for",
12103
+ "function",
12104
+ "if",
12105
+ "in",
12106
+ "local",
12107
+ "nil",
12108
+ "not",
12109
+ "or",
12110
+ "repeat",
12111
+ "return",
12112
+ "then",
12113
+ "true",
12114
+ "until",
12115
+ "while"
12116
+ ]);
11764
12117
  var IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
12118
+ function isIdentifier(name) {
12119
+ return IDENTIFIER.test(name);
12120
+ }
12121
+ function isLuauName(name) {
12122
+ return IDENTIFIER.test(name) && !LUAU_KEYWORDS.has(name);
12123
+ }
12124
+ var NAMED_ESCAPES = {
12125
+ "\\": "\\\\",
12126
+ '"': '\\"',
12127
+ "\n": "\\n",
12128
+ "\r": "\\r",
12129
+ " ": "\\t"
12130
+ };
12131
+ function escapeLuauString(text) {
12132
+ return text.replace(/[\\"\x00-\x1f\x7f]/g, (c) => NAMED_ESCAPES[c] ?? `\\${c.charCodeAt(0).toString().padStart(3, "0")}`);
12133
+ }
12134
+ function luauString(text) {
12135
+ return `"${escapeLuauString(text)}"`;
12136
+ }
12137
+
12138
+ // src/project/sourcemap.ts
11765
12139
  var INSTANCE_MEMBERS = /* @__PURE__ */ new Set(["Name", "ClassName", "Parent", "Archivable"]);
11766
12140
  var SCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([".tilua", ".luau", ".lua"]);
11767
12141
  function sourceMapTypes(text, path, options) {
@@ -11788,7 +12162,7 @@ function sourceMapTypes(text, path, options) {
11788
12162
  const alias = aliasFor(segments);
11789
12163
  aliasOfNode.set(node, alias);
11790
12164
  for (const filePath of node.filePaths ?? []) aliasOfFile.set(fileKey(resolve4(directory, filePath)), alias);
11791
- const className = IDENTIFIER.test(node.className) && options.classes.has(node.className) ? node.className : "Instance";
12165
+ const className = isIdentifier(node.className) && options.classes.has(node.className) ? node.className : "Instance";
11792
12166
  const taken = options.membersOf?.(className) ?? INSTANCE_MEMBERS;
11793
12167
  const members = [];
11794
12168
  if (parent) members.push(`Parent: ${parent}`);
@@ -11797,7 +12171,7 @@ function sourceMapTypes(text, path, options) {
11797
12171
  if (!isNode(child)) continue;
11798
12172
  const childAlias = visit(child, [...segments, child.name], alias);
11799
12173
  const service = child.name === child.className && options.classes.has(child.className);
11800
- if (!IDENTIFIER.test(child.name) || taken.has(child.name) && !service || named.has(child.name)) continue;
12174
+ if (!isIdentifier(child.name) || taken.has(child.name) && !service || named.has(child.name)) continue;
11801
12175
  named.add(child.name);
11802
12176
  members.push(`${child.name}: ${childAlias}`);
11803
12177
  }
@@ -11839,6 +12213,34 @@ function fileKey(path) {
11839
12213
  return process.platform === "win32" ? normalized.toLowerCase() : normalized;
11840
12214
  }
11841
12215
 
12216
+ // src/loweringHelpers.ts
12217
+ function withoutNil2(type) {
12218
+ if (!type) return [];
12219
+ const members = type.kind === "union" ? type.types : [type];
12220
+ return members.filter((m) => !(m.kind === "primitive" && m.name === "nil"));
12221
+ }
12222
+ function isFunctionType(type) {
12223
+ const members = withoutNil2(type);
12224
+ return members.length > 0 && members.every((m) => m.kind === "function" || m.kind === "intersection" && m.types.every(isFunctionType));
12225
+ }
12226
+ function everyObject(type, test) {
12227
+ const members = withoutNil2(type);
12228
+ return members.length > 0 && members.every((m) => m.kind === "object" && test(m));
12229
+ }
12230
+ function isInstanceOf(type, className) {
12231
+ return everyObject(type, (m) => m.class !== void 0 && m.class.ancestors.includes(className));
12232
+ }
12233
+ function hasMembers(type, names) {
12234
+ return everyObject(type, (m) => names.every((name) => m.properties.has(name)));
12235
+ }
12236
+ function returnsTuple(receiver, method) {
12237
+ return everyObject(receiver, (m) => {
12238
+ const found = m.properties.get(method)?.type;
12239
+ const signatures = !found ? [] : found.kind === "intersection" ? found.types : [found];
12240
+ return signatures.length > 0 && signatures.every((f) => f.kind === "function" && f.returns.kind === "tuple");
12241
+ });
12242
+ }
12243
+
11842
12244
  // src/index.ts
11843
12245
  var tilua = {
11844
12246
  tokenize,
@@ -11858,6 +12260,7 @@ export {
11858
12260
  CONFIG_FILE_NAMES,
11859
12261
  Keywords,
11860
12262
  LANGUAGE_GLOBALS,
12263
+ LUAU_KEYWORDS,
11861
12264
  LexError,
11862
12265
  Operators,
11863
12266
  PRELUDE_SOURCE,
@@ -11879,20 +12282,27 @@ export {
11879
12282
  difference,
11880
12283
  directivesOf,
11881
12284
  equalTypes,
12285
+ escapeLuauString,
11882
12286
  falsyType,
11883
12287
  findConfig,
11884
12288
  fn,
11885
12289
  formatType,
11886
12290
  getBinding,
12291
+ hasMembers,
11887
12292
  intersection,
11888
12293
  isAssignable,
11889
12294
  isClassType,
12295
+ isFunctionType,
11890
12296
  isGlobal,
12297
+ isIdentifier,
12298
+ isInstanceOf,
12299
+ isLuauName,
11891
12300
  isPossiblyFalsy,
11892
12301
  isPossiblyTruthy,
11893
12302
  isUnassignedGlobal,
11894
12303
  literal,
11895
12304
  loadConfig,
12305
+ luauString,
11896
12306
  matchInfer,
11897
12307
  moduleCandidates,
11898
12308
  moduleExports,
@@ -11915,6 +12325,7 @@ export {
11915
12325
  readDirectives,
11916
12326
  resolveModulePath,
11917
12327
  resolveTypeLibraries,
12328
+ returnsTuple,
11918
12329
  setAliasExpander,
11919
12330
  setDeferredBound,
11920
12331
  sourceMapTypes,
@@ -11933,5 +12344,6 @@ export {
11933
12344
  unknownType,
11934
12345
  widen,
11935
12346
  withAliasName,
11936
- withoutAliasName
12347
+ withoutAliasName,
12348
+ withoutNil2 as withoutNil
11937
12349
  };