cluaupp 1.5.2 → 2.0.0-beta.1

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 (195) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/README.md +27 -40
  3. package/api/generated/bench-latest.json +43 -21
  4. package/api/generated/lsp-index.json +1 -1
  5. package/api/manifests/roblox-target.manifest.json +5 -5
  6. package/api/normalized/roblox-target.profile.json +8729 -267
  7. package/api/roblox/luau-builtins.d.luau +185 -0
  8. package/api/roblox/surface.json +1 -0
  9. package/api/roblox-api.lock.json +11 -2
  10. package/bin/cluaupp.js +38 -0
  11. package/editors/vscode/syntaxes/clpp.tmLanguage.json +211 -0
  12. package/examples/README.md +9 -9
  13. package/examples/game/cluaupp.config.json +2 -2
  14. package/examples/game/default.project.json +4 -15
  15. package/examples/game/out/ReplicatedStorage/Shared/GameInfo.luau +8 -0
  16. package/examples/game/out/ReplicatedStorage/Shared/GameInfo.luau.map.json +13 -0
  17. package/examples/game/out/ServerScriptService/Leaderstats.server.luau +26 -0
  18. package/examples/game/out/ServerScriptService/Leaderstats.server.luau.map.json +13 -0
  19. package/examples/game/out/ServerScriptService/Main.server.luau +15 -0
  20. package/examples/game/out/ServerScriptService/Main.server.luau.map.json +13 -0
  21. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Main.client.luau +10 -0
  22. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Main.client.luau.map.json +13 -0
  23. package/examples/game/src/ReplicatedStorage/Shared/GameInfo.clp +1 -0
  24. package/examples/game/src/ServerScriptService/Leaderstats.server.clp +18 -0
  25. package/examples/game/src/ServerScriptService/Main.server.clp +9 -0
  26. package/examples/game/src/StarterPlayer/StarterPlayerScripts/Main.client.clp +4 -0
  27. package/generated/api/build-profile.js +1 -1
  28. package/generated/api/loader.js +2 -2
  29. package/generated/api/surface/build.js +328 -0
  30. package/generated/api/surface/luau-defs.js +595 -0
  31. package/generated/api/surface/model.js +8 -0
  32. package/generated/clpp/contract.js +6 -3
  33. package/generated/clpp/handshake.js +36 -0
  34. package/generated/clpp/modules.js +20 -9
  35. package/generated/clpp/runner.js +62 -31
  36. package/generated/compile.js +18 -1
  37. package/generated/compiler/ast.js +3 -0
  38. package/generated/compiler/checker/calls.js +999 -0
  39. package/generated/compiler/checker/checker.js +1215 -0
  40. package/generated/compiler/checker/exprs.js +791 -0
  41. package/generated/compiler/checker/flow.js +168 -0
  42. package/generated/compiler/checker/members.js +763 -0
  43. package/generated/compiler/checker/statements.js +792 -0
  44. package/generated/compiler/checker/symbols.js +90 -0
  45. package/generated/compiler/checker/typeops.js +348 -0
  46. package/generated/compiler/checker/yields.js +115 -0
  47. package/generated/compiler/diagnostics.js +138 -0
  48. package/generated/compiler/emit/emitter.js +2356 -0
  49. package/generated/compiler/emit/luau-types.js +137 -0
  50. package/generated/compiler/emit/names.js +130 -0
  51. package/generated/compiler/emit/runtime.js +432 -0
  52. package/generated/compiler/lexer.js +403 -0
  53. package/generated/compiler/parser.js +1881 -0
  54. package/generated/compiler/project.js +332 -0
  55. package/generated/compiler/roblox/api.js +388 -0
  56. package/generated/compiler/roblox/context.js +100 -0
  57. package/generated/compiler/source.js +59 -0
  58. package/generated/compiler/stdlib.js +232 -0
  59. package/generated/compiler/types.js +250 -0
  60. package/generated/target/build-cache.js +4 -4
  61. package/generated/target/gen-rojo-tree.js +15 -0
  62. package/generated/target/source-map.js +61 -8
  63. package/generated/target/studio-bridge.js +1 -0
  64. package/generated/utils/project.js +83 -25
  65. package/native/bin/.gitkeep +1 -0
  66. package/package.json +17 -6
  67. package/plugins/CluauppNav/Plugin.luau +28 -4
  68. package/rust/cluaupp/Cargo.lock +795 -0
  69. package/rust/cluaupp/Cargo.toml +17 -0
  70. package/rust/cluaupp/src/main.rs +717 -0
  71. package/scripts/postinstall-native.js +56 -0
  72. package/skills/README.md +3 -3
  73. package/skills/clpp-hive-flare/SKILL.md +30 -26
  74. package/skills/clpp-language/SKILL.md +67 -74
  75. package/skills/clpp-language/compiler.md +24 -36
  76. package/skills/clpp-style/SKILL.md +31 -44
  77. package/src/api/build-profile.ts +1 -1
  78. package/src/api/loader.ts +2 -2
  79. package/src/api/surface/build.ts +374 -0
  80. package/src/api/surface/luau-defs.ts +635 -0
  81. package/src/api/surface/model.ts +96 -0
  82. package/src/clpp/contract.ts +20 -3
  83. package/src/clpp/handshake.ts +25 -0
  84. package/src/clpp/modules.ts +17 -10
  85. package/src/clpp/runner.ts +52 -34
  86. package/src/compile.ts +20 -2
  87. package/src/compiler/ast.ts +336 -0
  88. package/src/compiler/checker/calls.ts +1025 -0
  89. package/src/compiler/checker/checker.ts +1352 -0
  90. package/src/compiler/checker/exprs.ts +805 -0
  91. package/src/compiler/checker/flow.ts +174 -0
  92. package/src/compiler/checker/members.ts +793 -0
  93. package/src/compiler/checker/statements.ts +802 -0
  94. package/src/compiler/checker/symbols.ts +219 -0
  95. package/src/compiler/checker/typeops.ts +355 -0
  96. package/src/compiler/checker/yields.ts +120 -0
  97. package/src/compiler/diagnostics.ts +163 -0
  98. package/src/compiler/emit/emitter.ts +2486 -0
  99. package/src/compiler/emit/luau-types.ts +143 -0
  100. package/src/compiler/emit/names.ts +130 -0
  101. package/src/compiler/emit/runtime.ts +438 -0
  102. package/src/compiler/lexer.ts +444 -0
  103. package/src/compiler/parser.ts +1943 -0
  104. package/src/compiler/project.ts +354 -0
  105. package/src/compiler/roblox/api.ts +408 -0
  106. package/src/compiler/roblox/context.ts +108 -0
  107. package/src/compiler/source.ts +67 -0
  108. package/src/compiler/stdlib.ts +256 -0
  109. package/src/compiler/types.ts +313 -0
  110. package/src/target/build-cache.ts +5 -8
  111. package/src/target/gen-rojo-tree.ts +15 -0
  112. package/src/target/source-map.ts +65 -27
  113. package/src/target/studio-bridge.ts +3 -0
  114. package/src/types.ts +3 -0
  115. package/src/utils/project.ts +79 -31
  116. package/templates/README.md +7 -0
  117. package/templates/{game → game-modern}/cluaupp.config.json +2 -2
  118. package/templates/{game → game-modern}/package.json +1 -2
  119. package/templates/game-modern/rokit.toml +2 -0
  120. package/templates/game-modern/src/ReplicatedStorage/Shared/GameInfo.clp +1 -0
  121. package/templates/game-modern/src/ServerScriptService/Handlers/Guard.server.clp +4 -0
  122. package/templates/game-modern/src/ServerScriptService/Main.server.clp +8 -0
  123. package/templates/game-modern/src/StarterPlayer/StarterPlayerScripts/Main.client.clp +4 -0
  124. package/examples/game/out/ReplicatedStorage/Shared/Constants/Datas/TemplateData.luau +0 -29
  125. package/examples/game/out/ReplicatedStorage/Shared/Constants/Datas/TemplateData.luau.map.json +0 -13
  126. package/examples/game/out/ReplicatedStorage/Shared/Net/Net.luau +0 -83
  127. package/examples/game/out/ReplicatedStorage/Shared/Net/Net.luau.map.json +0 -13
  128. package/examples/game/out/ServerScriptService/Boot/DataBoot.server.luau +0 -18
  129. package/examples/game/out/ServerScriptService/Boot/DataBoot.server.luau.map.json +0 -13
  130. package/examples/game/out/ServerScriptService/Configuration/ServerConfig.luau +0 -14
  131. package/examples/game/out/ServerScriptService/Configuration/ServerConfig.luau.map.json +0 -13
  132. package/examples/game/out/ServerScriptService/Handlers/Guard.server.luau +0 -15
  133. package/examples/game/out/ServerScriptService/Handlers/Guard.server.luau.map.json +0 -13
  134. package/examples/game/out/ServerScriptService/Handlers/PlayerHandler.server.luau +0 -50
  135. package/examples/game/out/ServerScriptService/Handlers/PlayerHandler.server.luau.map.json +0 -13
  136. package/examples/game/out/StarterPlayer/StarterCharacterScripts/Character/CharacterReady.client.luau +0 -12
  137. package/examples/game/out/StarterPlayer/StarterCharacterScripts/Character/CharacterReady.client.luau.map.json +0 -13
  138. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Controllers/DataController.client.luau +0 -16
  139. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Controllers/DataController.client.luau.map.json +0 -13
  140. package/examples/game/src/Client/Controllers/DataController.client.clpp +0 -7
  141. package/examples/game/src/Client/Main.client.clpp +0 -4
  142. package/examples/game/src/Declarations/Net.flare +0 -5
  143. package/examples/game/src/Include/GameTypes.clh +0 -40
  144. package/examples/game/src/Modules/Core/Guard.server.clpp +0 -7
  145. package/examples/game/src/Modules/Core/PlayerHandler.server.clpp +0 -23
  146. package/examples/game/src/Server/Boot/DataBoot.server.clpp +0 -13
  147. package/examples/game/src/Server/Main.server.clpp +0 -4
  148. package/templates/game/.clangd +0 -25
  149. package/templates/game/.vscode/extensions.json +0 -8
  150. package/templates/game/.vscode/settings.json +0 -17
  151. package/templates/game/Packages/.gitkeep +0 -0
  152. package/templates/game/compile_flags.txt +0 -6
  153. package/templates/game/default.project.json +0 -41
  154. package/templates/game/include/clpp/datatypes.clh +0 -575
  155. package/templates/game/include/clpp/generated/enums.clh +0 -4920
  156. package/templates/game/include/clpp/generated/instances.clh +0 -87182
  157. package/templates/game/include/clpp/libs/arrayindexer.clh +0 -12
  158. package/templates/game/include/clpp/libs/axiom.clh +0 -154
  159. package/templates/game/include/clpp/libs/bloom.clh +0 -58
  160. package/templates/game/include/clpp/libs/coil.clh +0 -37
  161. package/templates/game/include/clpp/libs/crest.clh +0 -22
  162. package/templates/game/include/clpp/libs/echo.clh +0 -52
  163. package/templates/game/include/clpp/libs/ember.clh +0 -17
  164. package/templates/game/include/clpp/libs/flare.clh +0 -65
  165. package/templates/game/include/clpp/libs/gleam.clh +0 -111
  166. package/templates/game/include/clpp/libs/guide.clh +0 -42
  167. package/templates/game/include/clpp/libs/helm.clh +0 -31
  168. package/templates/game/include/clpp/libs/hive.clh +0 -20
  169. package/templates/game/include/clpp/libs/keep.clh +0 -158
  170. package/templates/game/include/clpp/libs/lens.clh +0 -24
  171. package/templates/game/include/clpp/libs/mint.clh +0 -28
  172. package/templates/game/include/clpp/libs/net.clh +0 -31
  173. package/templates/game/include/clpp/libs/occlude.clh +0 -15
  174. package/templates/game/include/clpp/libs/pin.clh +0 -28
  175. package/templates/game/include/clpp/libs/promise.clh +0 -24
  176. package/templates/game/include/clpp/libs/roster.clh +0 -123
  177. package/templates/game/include/clpp/libs/shift.clh +0 -22
  178. package/templates/game/include/clpp/libs/spark.clh +0 -28
  179. package/templates/game/include/clpp/libs/stage.clh +0 -23
  180. package/templates/game/include/clpp/libs/sweep.clh +0 -23
  181. package/templates/game/include/clpp/libs/trace.clh +0 -18
  182. package/templates/game/include/clpp/libs/ward.clh +0 -19
  183. package/templates/game/include/clpp/libs.clh +0 -31
  184. package/templates/game/include/clpp/roblox.clh +0 -41
  185. package/templates/game/rokit.toml +0 -5
  186. package/templates/game/src/Client/Controllers/DataController.client.clpp +0 -7
  187. package/templates/game/src/Client/Main.client.clpp +0 -4
  188. package/templates/game/src/Declarations/Net.flare +0 -5
  189. package/templates/game/src/Include/GameTypes.clh +0 -40
  190. package/templates/game/src/Modules/Core/Guard.server.clpp +0 -7
  191. package/templates/game/src/Modules/Core/PlayerHandler.server.clpp +0 -24
  192. package/templates/game/src/Server/Boot/DataBoot.server.clpp +0 -13
  193. package/templates/game/src/Server/Main.server.clpp +0 -4
  194. package/templates/game/stylua.toml +0 -9
  195. package/templates/game/wally.toml +0 -12
@@ -0,0 +1,999 @@
1
+ "use strict";
2
+ /**
3
+ * Calls: user functions (with call-site inference for unannotated parameters), methods, CL++
4
+ * builtins, struct/Instance/datatype construction, enum cases, Option/Result, `GetService`,
5
+ * generic class arguments (`WaitForChild<Model>("Map")`) and overload resolution on the Roblox API.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.nonNull = void 0;
9
+ exports.checkCall = checkCall;
10
+ exports.checkNew = checkNew;
11
+ exports.checkArgs = checkArgs;
12
+ exports.joinTypes = joinTypes;
13
+ const diagnostics_js_1 = require("../diagnostics.js");
14
+ const types_js_1 = require("../types.js");
15
+ Object.defineProperty(exports, "nonNull", { enumerable: true, get: function () { return types_js_1.nonNull; } });
16
+ const checker_js_1 = require("./checker.js");
17
+ const exprs_js_1 = require("./exprs.js");
18
+ const members_js_1 = require("./members.js");
19
+ const statements_js_1 = require("./statements.js");
20
+ const typeops_js_1 = require("./typeops.js");
21
+ /** Methods whose class-name string argument can be written as a type argument. */
22
+ const CLASS_ARG_METHODS = new Set([
23
+ "FindFirstChildOfClass", "FindFirstChildWhichIsA", "FindFirstAncestorOfClass", "FindFirstAncestorWhichIsA",
24
+ "IsA", "GetService", "FindService",
25
+ ]);
26
+ /** Methods whose result can be narrowed with a type argument (a checked downcast). */
27
+ /** Axiom functions that return an int when every argument is an int (`Axiom.Clamp(hp - 5, 0, 100)`). */
28
+ const INT_PRESERVING = new Set(["Clamp", "Min", "Max", "Abs", "Sign", "Floor", "Ceil", "Round", "Trunc", "Snap", "Wrap", "Pow", "Gcd", "Lcm", "Factorial", "Sum", "Approach"]);
29
+ const CAST_RESULT_METHODS = new Set(["WaitForChild", "FindFirstChild", "FindFirstDescendant", "FindFirstAncestor", "Clone"]);
30
+ function setCall(e, call, args, variadicArgs) {
31
+ e.res = { kind: "call", call, args, ...(variadicArgs && variadicArgs.length ? { variadicArgs } : {}) };
32
+ }
33
+ function checkCall(c, e, expected) {
34
+ const callee = e.callee;
35
+ if (e.legacyConnect) {
36
+ c.warn("CL1204", callee.span, "`event~>Connect(fn)` is Cluaupp 1.x syntax", ["write `event.Connect(fn)` or the statement `event ~> fn;`"]);
37
+ }
38
+ if (callee.kind === "Name" && !c.scope.lookup(callee.name)) {
39
+ const special = checkSpecialName(c, e, callee, expected);
40
+ if (special) {
41
+ return special;
42
+ }
43
+ }
44
+ if (callee.kind === "Name") {
45
+ const sym = c.scope.lookup(callee.name);
46
+ if (sym && sym.kind === "struct" && sym.typeId) {
47
+ sym.used = true;
48
+ callee.res = { kind: "struct", name: sym.name, sym };
49
+ return constructStruct(c, e, sym.typeId, callee.typeArgs, e.args, expected);
50
+ }
51
+ if (sym && sym.kind === "signal") {
52
+ return fireSignal(c, e, sym);
53
+ }
54
+ if (sym && sym.kind === "variant") {
55
+ sym.used = true;
56
+ callee.res = { kind: "builtin", name: "variant" };
57
+ const fn = sym.type;
58
+ const r = checkArgs(c, e, fn, callee.span, null);
59
+ setCall(e, { form: "variant" }, r.args);
60
+ return fn.ret;
61
+ }
62
+ }
63
+ if (callee.kind === "SelfField") {
64
+ const t = (0, exprs_js_1.checkExpr)(c, callee);
65
+ if (callee.res?.kind === "method") {
66
+ return finishCall(c, e, (0, types_js_1.prune)(t), callee.span, { form: "call" }, expected);
67
+ }
68
+ return finishCall(c, e, (0, types_js_1.prune)(t), callee.span, { form: "call" }, expected);
69
+ }
70
+ if (callee.kind === "Member") {
71
+ return checkMemberCall(c, e, callee, expected);
72
+ }
73
+ const t = (0, types_js_1.prune)((0, exprs_js_1.checkExpr)(c, callee));
74
+ if (t.k === "namespace" && t.ns.kind === "struct") {
75
+ return constructStruct(c, e, t.ns.id, null, e.args, expected);
76
+ }
77
+ if (t.k === "namespace" && t.ns.kind === "datatype") {
78
+ return constructDatatype(c, e, t.ns.name, e.args);
79
+ }
80
+ return finishCall(c, e, t, callee.span, { form: "call" }, expected);
81
+ }
82
+ // ---------- names with special meaning ----------
83
+ function checkSpecialName(c, e, callee, expected) {
84
+ const name = callee.name;
85
+ switch (name) {
86
+ case "len":
87
+ case "list":
88
+ case "array":
89
+ case "dictionary":
90
+ case "push":
91
+ case "pop":
92
+ case "insert":
93
+ case "remove":
94
+ case "find":
95
+ case "sort":
96
+ case "args":
97
+ case "mutex":
98
+ case "lock":
99
+ case "unlock":
100
+ case "fetch_add":
101
+ case "atomic_load":
102
+ case "actor":
103
+ case "Some":
104
+ case "Ok":
105
+ case "Err":
106
+ case "int":
107
+ case "float":
108
+ case "bool":
109
+ case "string":
110
+ case "GetService":
111
+ callee.res = { kind: "builtin", name };
112
+ callee.ty = types_js_1.T.any;
113
+ return checkBuiltin(c, e, name, callee.typeArgs, expected);
114
+ case "type":
115
+ c.requireRoblox(callee.span, "type");
116
+ callee.res = { kind: "global", luau: "type" };
117
+ callee.ty = types_js_1.T.any;
118
+ checkArgsLoose(c, e);
119
+ setCall(e, { form: "call" }, e.args.map((a) => a.value));
120
+ return types_js_1.T.string;
121
+ default:
122
+ break;
123
+ }
124
+ if (name === "Vector2" || name === "Vector3" || (c.api.hasDatatype(name) && !c.api.hasClass(name))) {
125
+ callee.res = { kind: "global", luau: name };
126
+ return constructDatatype(c, e, name, e.args);
127
+ }
128
+ if (name === "Vector4") {
129
+ c.error("RBX5002", callee.span, "Vector4 has no Roblox equivalent", ["use Color3 for colors or a struct"]);
130
+ checkArgsLoose(c, e);
131
+ return types_js_1.T.unknown;
132
+ }
133
+ if (c.api.hasClass(name) && !c.api.isService(name)) {
134
+ c.requireRoblox(callee.span, name);
135
+ callee.res = { kind: "global", luau: name };
136
+ return constructInstance(c, e, name, e.args, callee.span);
137
+ }
138
+ return null;
139
+ }
140
+ // ---------- builtins ----------
141
+ function argValue(e, i) {
142
+ return e.args[i]?.value ?? null;
143
+ }
144
+ function arity(c, e, name, min, max = min) {
145
+ if (e.args.some((a) => a.name !== null) && name !== "dictionary") {
146
+ c.error("CL3101", e.span, `${name}(…) does not take named arguments`);
147
+ }
148
+ if (e.args.length < min || e.args.length > max) {
149
+ c.error("CL3102", e.span, `${name} takes ${min === max ? min : `${min} to ${max}`} argument${max === 1 ? "" : "s"}, got ${e.args.length}`);
150
+ return false;
151
+ }
152
+ return true;
153
+ }
154
+ function checkArgsLoose(c, e) {
155
+ return e.args.map((a) => (0, exprs_js_1.checkExpr)(c, a.value));
156
+ }
157
+ function listOf(c, x, what) {
158
+ if (!x) {
159
+ return null;
160
+ }
161
+ const t = (0, types_js_1.prune)((0, exprs_js_1.checkExpr)(c, x));
162
+ if (t.k === "list") {
163
+ return t;
164
+ }
165
+ if (!(0, types_js_1.isDynamic)(t)) {
166
+ c.error("CL3103", x.span, `${what} needs a list, got ${(0, types_js_1.typeToString)(t)}`);
167
+ }
168
+ return null;
169
+ }
170
+ function requireMutableList(c, x, what) {
171
+ if (!x) {
172
+ return;
173
+ }
174
+ const root = (0, exprs_js_1.rootSymbol)(x);
175
+ if (root && (root.kind === "var" || root.kind === "param") && !root.mutable) {
176
+ c.diags.error("CL2040", x.span, `${what} changes '${root.name}', which is declared with let`, ["declare it with `let mut`"], {
177
+ span: root.span,
178
+ text: `mut ${root.name}`,
179
+ title: `Make '${root.name}' mutable (let mut)`,
180
+ });
181
+ }
182
+ if (root) {
183
+ root.mutatedFields = true;
184
+ }
185
+ }
186
+ function elementSlot(list, value) {
187
+ const elem = (0, types_js_1.prune)(list.elem);
188
+ if (elem.k === "infer" && !elem.resolved) {
189
+ elem.resolved = (0, checker_js_1.widenLiteral)(value);
190
+ return elem.resolved;
191
+ }
192
+ return list.elem;
193
+ }
194
+ function checkBuiltin(c, e, name, typeArgs, expected) {
195
+ const builtin = (elemType) => setCall(e, { form: "builtin", name, ...(elemType ? { elemType } : {}) }, e.args.map((a) => a.value));
196
+ switch (name) {
197
+ case "len": {
198
+ arity(c, e, "len", 1);
199
+ const x = argValue(e, 0);
200
+ const t = x ? (0, types_js_1.prune)((0, exprs_js_1.checkExpr)(c, x)) : types_js_1.T.unknown;
201
+ if (!(t.k === "list" || t.k === "string" || t.k === "lit" || t.k === "dict" || t.k === "buffer" || t.k === "table" || (0, types_js_1.isDynamic)(t))) {
202
+ c.error("CL3104", x?.span ?? e.span, `len(…) needs a list, text or dictionary, got ${(0, types_js_1.typeToString)(t)}`, t.k === "instance" ? ["for children use len(x.GetChildren())"] : undefined);
203
+ }
204
+ builtin(t);
205
+ return types_js_1.T.int;
206
+ }
207
+ case "list": {
208
+ const ex = expected ? (0, types_js_1.prune)(expected) : null;
209
+ if (e.args.length === 0) {
210
+ builtin();
211
+ return ex && ex.k === "list" ? ex : types_js_1.T.list({ k: "infer", resolved: null });
212
+ }
213
+ const want = ex && ex.k === "list" ? ex.elem : undefined;
214
+ let elem = types_js_1.T.never;
215
+ for (const a of e.args) {
216
+ if (a.name) {
217
+ c.error("CL3101", a.nameSpan ?? a.value.span, "list(…) takes values, not named arguments", ["use dictionary(Key: value, …) for named entries"]);
218
+ }
219
+ const t = (0, exprs_js_1.checkExpr)(c, a.value, want);
220
+ c.markCopy(a.value);
221
+ elem = (0, typeops_js_1.join)(c, elem, (0, checker_js_1.widenLiteral)(t));
222
+ }
223
+ builtin();
224
+ if (want) {
225
+ for (const a of e.args) {
226
+ c.expectAssignable(want, a.value.ty ?? types_js_1.T.unknown, a.value.span, "list element");
227
+ }
228
+ return ex;
229
+ }
230
+ return types_js_1.T.list((0, types_js_1.prune)(elem).k === "never" ? types_js_1.T.any : elem);
231
+ }
232
+ case "array": {
233
+ if (!typeArgs || typeArgs.length !== 1) {
234
+ c.error("CL3105", e.span, "array needs its element type: array<int>(1, 2, 3)");
235
+ checkArgsLoose(c, e);
236
+ builtin();
237
+ return types_js_1.T.list(types_js_1.T.unknown);
238
+ }
239
+ const elem = c.resolveType(typeArgs[0]);
240
+ for (const a of e.args) {
241
+ const t = (0, exprs_js_1.checkExpr)(c, a.value, elem);
242
+ c.expectAssignable(elem, t, a.value.span, "array element");
243
+ c.markCopy(a.value);
244
+ }
245
+ builtin(elem);
246
+ return types_js_1.T.list(elem);
247
+ }
248
+ case "dictionary": {
249
+ if (e.args.length && e.args.every((a) => a.name !== null)) {
250
+ // dictionary(Transparency: 1, Color: c) → { Transparency = 1, Color = c }
251
+ const fields = {};
252
+ for (const a of e.args) {
253
+ fields[a.name] = (0, checker_js_1.widenLiteral)((0, exprs_js_1.checkExpr)(c, a.value));
254
+ c.markCopy(a.value);
255
+ }
256
+ setCall(e, { form: "table", keys: e.args.map((a) => a.name) }, e.args.map((a) => a.value));
257
+ return { k: "table", fields };
258
+ }
259
+ let key = types_js_1.T.any;
260
+ let value = types_js_1.T.any;
261
+ if (typeArgs && typeArgs.length === 2) {
262
+ key = c.resolveType(typeArgs[0]);
263
+ value = c.resolveType(typeArgs[1]);
264
+ }
265
+ else if (typeArgs) {
266
+ c.error("CL3105", e.span, "dictionary takes two type arguments: dictionary<string, int>(…)");
267
+ }
268
+ else if (expected && (0, types_js_1.prune)(expected).k === "dict") {
269
+ const ex = (0, types_js_1.prune)(expected);
270
+ key = ex.key;
271
+ value = ex.value;
272
+ }
273
+ else if (e.args.length === 0) {
274
+ key = { k: "infer", resolved: null };
275
+ value = { k: "infer", resolved: null };
276
+ }
277
+ if (e.args.length % 2 !== 0) {
278
+ c.error("CL3106", e.span, "dictionary(…) takes key/value pairs: dictionary<string, int>(\"a\", 1, \"b\", 2)");
279
+ }
280
+ let inferredKey = types_js_1.T.never;
281
+ let inferredValue = types_js_1.T.never;
282
+ e.args.forEach((a, i) => {
283
+ const isKey = i % 2 === 0;
284
+ const want = isKey ? key : value;
285
+ const t = (0, exprs_js_1.checkExpr)(c, a.value, (0, types_js_1.isDynamic)(want) ? undefined : want);
286
+ c.markCopy(a.value);
287
+ if (!(0, types_js_1.isDynamic)(want)) {
288
+ c.expectAssignable(want, t, a.value.span, isKey ? "dictionary key" : "dictionary value");
289
+ }
290
+ else if (isKey) {
291
+ inferredKey = (0, typeops_js_1.join)(c, inferredKey, (0, checker_js_1.widenLiteral)(t));
292
+ }
293
+ else {
294
+ inferredValue = (0, typeops_js_1.join)(c, inferredValue, (0, checker_js_1.widenLiteral)(t));
295
+ }
296
+ });
297
+ builtin();
298
+ if (!typeArgs && e.args.length && (0, types_js_1.isDynamic)(key)) {
299
+ return types_js_1.T.dict((0, types_js_1.prune)(inferredKey).k === "never" ? types_js_1.T.any : inferredKey, (0, types_js_1.prune)(inferredValue).k === "never" ? types_js_1.T.any : inferredValue);
300
+ }
301
+ return types_js_1.T.dict(key, value);
302
+ }
303
+ case "push": {
304
+ arity(c, e, "push", 2);
305
+ const list = listOf(c, argValue(e, 0), "push");
306
+ requireMutableList(c, argValue(e, 0), "push");
307
+ const v = argValue(e, 1);
308
+ if (v) {
309
+ const want = list ? elementSlot(list, (0, types_js_1.prune)((0, exprs_js_1.checkExpr)(c, v, list.typed && !(0, types_js_1.isDynamic)(list.elem) ? list.elem : undefined))) : types_js_1.T.any;
310
+ if (list && list.typed) {
311
+ c.expectAssignable(want, v.ty ?? types_js_1.T.unknown, v.span, "push");
312
+ }
313
+ c.markCopy(v);
314
+ }
315
+ builtin();
316
+ return types_js_1.T.void;
317
+ }
318
+ case "pop": {
319
+ arity(c, e, "pop", 1);
320
+ const list = listOf(c, argValue(e, 0), "pop");
321
+ requireMutableList(c, argValue(e, 0), "pop");
322
+ builtin();
323
+ return list ? list.elem : types_js_1.T.any;
324
+ }
325
+ case "insert": {
326
+ arity(c, e, "insert", 3);
327
+ const list = listOf(c, argValue(e, 0), "insert");
328
+ requireMutableList(c, argValue(e, 0), "insert");
329
+ const i = argValue(e, 1);
330
+ if (i) {
331
+ const it = (0, exprs_js_1.checkExpr)(c, i);
332
+ if (!(0, types_js_1.isIntLike)(it) && !(0, types_js_1.isDynamic)(it)) {
333
+ c.error("CL3060", i.span, `insert position must be int, got ${(0, types_js_1.typeToString)(it)}`);
334
+ }
335
+ }
336
+ const v = argValue(e, 2);
337
+ if (v) {
338
+ const vt = (0, exprs_js_1.checkExpr)(c, v, list?.elem);
339
+ if (list) {
340
+ c.expectAssignable(elementSlot(list, vt), vt, v.span, "insert");
341
+ }
342
+ c.markCopy(v);
343
+ }
344
+ builtin();
345
+ return types_js_1.T.void;
346
+ }
347
+ case "remove": {
348
+ arity(c, e, "remove", 2);
349
+ const target = argValue(e, 0);
350
+ const t = target ? (0, types_js_1.prune)((0, exprs_js_1.checkExpr)(c, target)) : types_js_1.T.unknown;
351
+ requireMutableList(c, target, "remove");
352
+ const k = argValue(e, 1);
353
+ const kt = k ? (0, exprs_js_1.checkExpr)(c, k) : types_js_1.T.unknown;
354
+ if (t.k === "list") {
355
+ if (!(0, types_js_1.isIntLike)(kt) && !(0, types_js_1.isDynamic)(kt)) {
356
+ c.error("CL3060", k.span, `remove position must be int, got ${(0, types_js_1.typeToString)(kt)}`);
357
+ }
358
+ builtin(types_js_1.T.list(types_js_1.T.any));
359
+ return t.elem;
360
+ }
361
+ if (t.k === "dict") {
362
+ if ((0, typeops_js_1.assignable)(c, t.key, kt) === "no") {
363
+ c.error("CL3063", k.span, `dictionary key must be ${(0, types_js_1.typeToString)(t.key)}`);
364
+ }
365
+ builtin(types_js_1.T.dict(types_js_1.T.any, types_js_1.T.any));
366
+ return t.value;
367
+ }
368
+ if (!(0, types_js_1.isDynamic)(t)) {
369
+ c.error("CL3103", target?.span ?? e.span, `remove needs a list or dictionary, got ${(0, types_js_1.typeToString)(t)}`);
370
+ }
371
+ builtin();
372
+ return types_js_1.T.any;
373
+ }
374
+ case "find": {
375
+ arity(c, e, "find", 2);
376
+ const list = listOf(c, argValue(e, 0), "find");
377
+ const v = argValue(e, 1);
378
+ if (v) {
379
+ const vt = (0, exprs_js_1.checkExpr)(c, v, list?.elem);
380
+ if (list && (0, typeops_js_1.assignable)(c, list.elem, vt) === "no" && (0, typeops_js_1.assignable)(c, vt, list.elem) === "no") {
381
+ c.error("CL3107", v.span, `find looks for ${(0, types_js_1.typeToString)(list.elem)}, got ${(0, types_js_1.typeToString)(vt)}`);
382
+ }
383
+ }
384
+ builtin(list?.elem);
385
+ return types_js_1.T.int;
386
+ }
387
+ case "sort": {
388
+ arity(c, e, "sort", 1);
389
+ const list = listOf(c, argValue(e, 0), "sort");
390
+ if (list && !(0, types_js_1.isNumeric)(list.elem) && !(0, types_js_1.isStringLike)(list.elem) && !(0, types_js_1.isDynamic)(list.elem)) {
391
+ c.error("CL3108", e.span, `sort orders numbers or text, not ${(0, types_js_1.typeToString)(list.elem)}`);
392
+ }
393
+ builtin();
394
+ return list ?? types_js_1.T.list(types_js_1.T.any);
395
+ }
396
+ case "args":
397
+ c.error("RBX5100", e.span, "args() has no meaning on Roblox: scripts get no command line");
398
+ builtin();
399
+ return types_js_1.T.list(types_js_1.T.string);
400
+ case "actor":
401
+ c.error("RBX5100", e.span, "actor(...) is the clpp VM sandbox; on Roblox use an Actor instance with parallel Luau");
402
+ checkArgsLoose(c, e);
403
+ builtin();
404
+ return types_js_1.T.any;
405
+ case "mutex":
406
+ arity(c, e, "mutex", 0);
407
+ builtin();
408
+ return types_js_1.T.datatype("__Mutex");
409
+ case "lock":
410
+ case "unlock": {
411
+ arity(c, e, name, 1);
412
+ checkArgsLoose(c, e);
413
+ if (name === "lock") {
414
+ c.noteYield();
415
+ }
416
+ builtin();
417
+ return types_js_1.T.void;
418
+ }
419
+ case "fetch_add":
420
+ case "atomic_load": {
421
+ arity(c, e, name, name === "fetch_add" ? 2 : 1);
422
+ const a = argValue(e, 0);
423
+ if (a) {
424
+ (0, exprs_js_1.checkExpr)(c, a);
425
+ const sym = a.kind === "Name" ? c.scope.lookup(a.name) : undefined;
426
+ if (!sym || !sym.atomic) {
427
+ c.error("CL3109", a.span, `${name} needs a variable declared with \`atomic\``);
428
+ }
429
+ }
430
+ if (name === "fetch_add" && argValue(e, 1)) {
431
+ (0, exprs_js_1.checkExpr)(c, argValue(e, 1));
432
+ }
433
+ builtin();
434
+ return types_js_1.T.int;
435
+ }
436
+ case "Some": {
437
+ arity(c, e, "Some", 1);
438
+ const ex = expected ? (0, types_js_1.prune)(expected) : null;
439
+ const inner = ex && ex.k === "option" ? ex.inner : undefined;
440
+ const t = argValue(e, 0) ? (0, exprs_js_1.checkExpr)(c, argValue(e, 0), inner) : types_js_1.T.unknown;
441
+ if ((0, types_js_1.prune)(t).k === "option" || (0, types_js_1.prune)(t).k === "null") {
442
+ c.error("CL3110", e.span, "Some(…) of a value that may already be null cannot be told apart from None on Roblox");
443
+ }
444
+ if (argValue(e, 0)) {
445
+ c.markCopy(argValue(e, 0));
446
+ }
447
+ setCall(e, { form: "some" }, e.args.map((a) => a.value));
448
+ return types_js_1.T.option(inner ?? (0, checker_js_1.widenLiteral)(t));
449
+ }
450
+ case "Ok":
451
+ case "Err": {
452
+ arity(c, e, name, 1);
453
+ const ex = expected ? (0, types_js_1.prune)(expected) : null;
454
+ const res = ex && ex.k === "result" ? ex : null;
455
+ const want = res ? (name === "Ok" ? res.ok : res.err) : undefined;
456
+ const t = argValue(e, 0) ? (0, checker_js_1.widenLiteral)((0, exprs_js_1.checkExpr)(c, argValue(e, 0), want)) : types_js_1.T.unknown;
457
+ if (argValue(e, 0)) {
458
+ c.markCopy(argValue(e, 0));
459
+ }
460
+ setCall(e, { form: name === "Ok" ? "ok" : "err" }, e.args.map((a) => a.value));
461
+ return name === "Ok" ? { k: "result", ok: res?.ok ?? t, err: res?.err ?? types_js_1.T.any } : { k: "result", ok: res?.ok ?? types_js_1.T.any, err: res?.err ?? t };
462
+ }
463
+ case "int":
464
+ case "float":
465
+ case "bool":
466
+ case "string": {
467
+ arity(c, e, name, 1);
468
+ const x = argValue(e, 0);
469
+ const t = x ? (0, types_js_1.prune)((0, exprs_js_1.checkExpr)(c, x)) : types_js_1.T.unknown;
470
+ if ((name === "int" || name === "float") && !(0, types_js_1.isNumeric)(t) && !(0, types_js_1.isStringLike)(t) && !(0, types_js_1.isDynamic)(t) && t.k !== "enum" && t.k !== "rbxEnumItem") {
471
+ c.error("CL3111", x?.span ?? e.span, `cannot convert ${(0, types_js_1.typeToString)(t)} to ${name}`);
472
+ }
473
+ if ((name === "int" || name === "float") && (0, types_js_1.isStringLike)(t)) {
474
+ c.warn("CL3112", x.span, `${name}(text) is null-free on Roblox only if the text is a number`, ["prefer Text.toNumber(text), which reports bad input"]);
475
+ }
476
+ setCall(e, { form: "convert", to: name }, [x]);
477
+ return name === "int" ? types_js_1.T.int : name === "float" ? types_js_1.T.float : name === "bool" ? types_js_1.T.bool : types_js_1.T.string;
478
+ }
479
+ case "GetService": {
480
+ let service = null;
481
+ if (typeArgs && typeArgs.length === 1 && typeArgs[0].kind === "Named") {
482
+ service = typeArgs[0].name;
483
+ }
484
+ else if (e.args.length === 1 && e.args[0].value.kind === "String") {
485
+ service = e.args[0].value.value;
486
+ }
487
+ if (!service) {
488
+ c.error("RBX5050", e.span, "GetService needs the service name: GetService<Players>() or GetService(\"Players\")");
489
+ return types_js_1.T.unknown;
490
+ }
491
+ return serviceCall(c, e, service, e.span);
492
+ }
493
+ default:
494
+ return types_js_1.T.unknown;
495
+ }
496
+ }
497
+ function serviceCall(c, e, service, span) {
498
+ if (!c.api.isService(service)) {
499
+ const s = (0, diagnostics_js_1.suggest)(service, c.api.services);
500
+ c.error("RBX5051", span, `'${service}' is not a Roblox service`, s ? [`did you mean ${s}?`] : undefined);
501
+ return types_js_1.T.unknown;
502
+ }
503
+ c.useService(service, span);
504
+ setCall(e, { form: "service", service }, []);
505
+ const dm = c.input.dataModel?.children.get(service);
506
+ return { k: "instance", cls: service, ...(dm ? { dm } : {}) };
507
+ }
508
+ // ---------- member calls ----------
509
+ function checkMemberCall(c, e, callee, expected) {
510
+ const t = (0, types_js_1.prune)((0, members_js_1.checkMember)(c, callee, undefined, { forCall: true }));
511
+ const res = callee.res;
512
+ if (!res) {
513
+ return finishCall(c, e, t, callee.span, { form: "call" }, expected);
514
+ }
515
+ switch (res.kind) {
516
+ case "enumCase":
517
+ if (res.hasPayload) {
518
+ const fn = t;
519
+ const r = checkArgs(c, e, fn, callee.span, null);
520
+ setCall(e, { form: "enumCase", enumId: res.enumName, index: res.index }, r.args);
521
+ return fn.ret;
522
+ }
523
+ c.error("CL3113", e.span, `this enum case carries no value; write it without parentheses`);
524
+ return t;
525
+ case "moduleMember":
526
+ if (res.memberKind === "struct" && t.k === "namespace" && t.ns.kind === "struct") {
527
+ return constructStruct(c, e, t.ns.id, callee.typeArgs, e.args, expected);
528
+ }
529
+ break;
530
+ case "builtin":
531
+ if (res.name === "OnChange") {
532
+ const fn = t;
533
+ const r = checkArgs(c, e, fn, callee.span, null);
534
+ const sym = c.scope.lookup(callee.object.name);
535
+ (sym.handlers ||= []).push(...r.args.filter(Boolean));
536
+ setCall(e, { form: "observe", sym }, r.args);
537
+ return types_js_1.T.void;
538
+ }
539
+ break;
540
+ case "stdlib": {
541
+ const fn = t;
542
+ const r = checkArgs(c, e, fn, callee.span, null);
543
+ setCall(e, { form: "std", module: res.module, fn: res.member }, r.args);
544
+ if (res.module === "axiom" && INT_PRESERVING.has(res.member) && r.args.length && r.args.every((a) => a && (0, types_js_1.isIntLike)(a.ty ?? types_js_1.T.any))) {
545
+ return types_js_1.T.int;
546
+ }
547
+ return fn.ret;
548
+ }
549
+ case "library":
550
+ if (res.stringMethod) {
551
+ const r = checkArgs(c, e, t, callee.span, null);
552
+ setCall(e, { form: "stringMethod", name: callee.name }, r.args, r.rest);
553
+ return (0, types_js_1.prune)(t.ret);
554
+ }
555
+ break;
556
+ case "datatypeStatic":
557
+ if (res.datatype === "Instance" && res.member === "new") {
558
+ return instanceNewCall(c, e, callee);
559
+ }
560
+ break;
561
+ case "rbxMethod":
562
+ if (callee.name === "GetService" && (callee.typeArgs || e.args[0]?.value.kind === "String")) {
563
+ const name = callee.typeArgs?.[0]?.kind === "Named" ? callee.typeArgs[0].name : e.args[0]?.value.kind === "String" ? e.args[0].value.value : null;
564
+ if (name) {
565
+ return serviceCall(c, e, name, e.span);
566
+ }
567
+ }
568
+ if (callee.typeArgs && callee.typeArgs.length) {
569
+ return genericRobloxCall(c, e, callee, t, expected);
570
+ }
571
+ if (CLASS_ARG_METHODS.has(callee.name) && e.args[0]?.value.kind === "String") {
572
+ const cls = e.args[0].value.value;
573
+ if (!c.api.hasClass(cls)) {
574
+ const s = (0, diagnostics_js_1.suggest)(cls, Object.keys(c.api.classes));
575
+ c.error("RBX5052", e.args[0].value.span, `'${cls}' is not a Roblox class`, s ? [`did you mean "${s}"?`] : undefined);
576
+ }
577
+ const r = checkArgs(c, e, t, callee.span, null);
578
+ setCall(e, { form: "call" }, r.args, r.rest);
579
+ const ret = (0, types_js_1.prune)(t.ret);
580
+ if (c.api.hasClass(cls) && callee.name !== "IsA") {
581
+ return ret.k === "option" ? types_js_1.T.option(types_js_1.T.instance(cls)) : types_js_1.T.instance(cls);
582
+ }
583
+ return ret;
584
+ }
585
+ break;
586
+ case "datatypeMember":
587
+ if (res.datatype === "RBXScriptSignal" && (callee.name === "Connect" || callee.name === "Once" || callee.name === "ConnectParallel")) {
588
+ const signal = (0, types_js_1.prune)(callee.object.ty ?? types_js_1.T.any);
589
+ const r = checkArgs(c, e, t, callee.span, null);
590
+ if (signal.k === "rbxSignal" && r.args[0]) {
591
+ handlerCheck(c, r.args[0], signal.args);
592
+ }
593
+ setCall(e, { form: "call" }, r.args);
594
+ return types_js_1.T.datatype("RBXScriptConnection");
595
+ }
596
+ break;
597
+ default:
598
+ break;
599
+ }
600
+ if (t.k === "namespace" && t.ns.kind === "struct") {
601
+ return constructStruct(c, e, t.ns.id, callee.typeArgs, e.args, expected);
602
+ }
603
+ if (t.k === "namespace" && t.ns.kind === "datatype") {
604
+ return constructDatatype(c, e, t.ns.name, e.args);
605
+ }
606
+ return finishCall(c, e, t, callee.span, { form: "call" }, expected, callee.typeArgs);
607
+ }
608
+ function handlerCheck(c, handler, args) {
609
+ (0, statements_js_1.checkHandler)(c, handler.ty ?? types_js_1.T.any, args, handler.span);
610
+ }
611
+ /** `x.WaitForChild<Model>("Map")`, `x.FindFirstChildOfClass<Humanoid>()`, `x.IsA<BasePart>()`. */
612
+ function genericRobloxCall(c, e, callee, fn, expected) {
613
+ const node = callee.typeArgs[0];
614
+ const target = c.resolveType(node);
615
+ const p = (0, types_js_1.prune)(target);
616
+ if (p.k !== "instance") {
617
+ c.error("RBX5053", node.span, `${callee.name}<…> needs a Roblox class, got ${(0, types_js_1.typeToString)(target)}`);
618
+ const r = checkArgs(c, e, fn, callee.span, null);
619
+ setCall(e, { form: "call" }, r.args, r.rest);
620
+ return fn.ret;
621
+ }
622
+ if (CLASS_ARG_METHODS.has(callee.name)) {
623
+ const shifted = { ...fn, params: fn.params.slice(1) };
624
+ const r = checkArgs(c, e, shifted, callee.span, null);
625
+ setCall(e, { form: "classArg", cls: p.cls, cast: callee.name !== "IsA" }, r.args, r.rest);
626
+ const ret = (0, types_js_1.prune)(fn.ret);
627
+ if (callee.name === "IsA") {
628
+ return types_js_1.T.bool;
629
+ }
630
+ return ret.k === "option" ? types_js_1.T.option(p) : p;
631
+ }
632
+ if (CAST_RESULT_METHODS.has(callee.name)) {
633
+ const r = checkArgs(c, e, fn, callee.span, null);
634
+ setCall(e, { form: "cast", to: p }, r.args, r.rest);
635
+ const ret = (0, types_js_1.prune)(fn.ret);
636
+ return ret.k === "option" ? types_js_1.T.option(p) : p;
637
+ }
638
+ c.error("RBX5054", node.span, `${callee.name} does not take a type argument`, [
639
+ `type arguments work on ${[...CLASS_ARG_METHODS, ...CAST_RESULT_METHODS].join(", ")}`,
640
+ ]);
641
+ void expected;
642
+ return finishCall(c, e, fn, callee.span, { form: "call" }, undefined);
643
+ }
644
+ function instanceNewCall(c, e, callee) {
645
+ const first = e.args[0]?.value;
646
+ checkArgsLoose(c, e);
647
+ setCall(e, { form: "call" }, e.args.map((a) => a.value));
648
+ if (!first || first.kind !== "String") {
649
+ if (first && !(0, types_js_1.isStringLike)(first.ty ?? types_js_1.T.any) && !(0, types_js_1.isDynamic)(first.ty ?? types_js_1.T.any)) {
650
+ c.error("RBX5055", first.span, "Instance.new needs a class name");
651
+ }
652
+ return types_js_1.T.instance("Instance");
653
+ }
654
+ const cls = first.value;
655
+ const problem = c.api.creationError(cls);
656
+ if (problem) {
657
+ const s = c.api.hasClass(cls) ? null : (0, diagnostics_js_1.suggest)(cls, Object.keys(c.api.classes));
658
+ c.error("RBX5056", first.span, problem, s ? [`did you mean "${s}"?`] : undefined);
659
+ return types_js_1.T.unknown;
660
+ }
661
+ if (e.args.length > 1) {
662
+ c.warn("PERF8001", e.args[1].value.span, "Instance.new with a parent sets Parent before the other properties (slow replication)", [
663
+ `create it first and set Parent last, or write ${cls}(Name: …, Parent: …)`,
664
+ ]);
665
+ }
666
+ if (c.api.classes[cls].deprecated) {
667
+ c.warn("RBX5010", first.span, `${cls} is deprecated`);
668
+ }
669
+ void callee;
670
+ return types_js_1.T.instance(cls);
671
+ }
672
+ // ---------- construction ----------
673
+ function checkNew(c, e, expected) {
674
+ const target = e.target;
675
+ const asCall = { kind: "Call", callee: target, args: e.args, span: e.span };
676
+ let t;
677
+ if (target.kind === "Name" && !c.scope.lookup(target.name) && c.api.hasClass(target.name)) {
678
+ c.requireRoblox(target.span, target.name);
679
+ target.res = { kind: "global", luau: target.name };
680
+ t = constructInstance(c, asCall, target.name, e.args, target.span);
681
+ }
682
+ else {
683
+ t = checkCall(c, asCall, expected);
684
+ const form = asCall.res?.kind === "call" ? asCall.res.call.form : "call";
685
+ if (form !== "struct" && form !== "instance" && form !== "datatype") {
686
+ c.error("CL3114", e.span, "`new` builds a struct, a Roblox Instance or a datatype");
687
+ }
688
+ }
689
+ e.res = asCall.res;
690
+ return t;
691
+ }
692
+ function constructStruct(c, e, id, typeArgs, args, expected) {
693
+ const info = c.structs.get(id);
694
+ if (!info) {
695
+ checkArgsLoose(c, e);
696
+ return types_js_1.T.unknown;
697
+ }
698
+ c.resolveStruct(info, new Set());
699
+ if (info.isAbstract) {
700
+ c.error("CL3115", e.span, `abstract type ${info.name} cannot be constructed`, ["construct one of the structs that inherit from it"]);
701
+ }
702
+ const bindings = new Map();
703
+ if (typeArgs && typeArgs.length) {
704
+ if (typeArgs.length !== info.typeParams.length) {
705
+ c.error("CL3001", e.span, `${info.name} takes ${info.typeParams.length} type argument(s)`);
706
+ }
707
+ info.typeParams.forEach((p, i) => typeArgs[i] && bindings.set(p, c.resolveType(typeArgs[i])));
708
+ }
709
+ else if (expected && (0, types_js_1.prune)(expected).k === "struct" && (0, types_js_1.prune)(expected).id === id) {
710
+ const ex = (0, types_js_1.prune)(expected);
711
+ info.typeParams.forEach((p, i) => ex.args[i] && (0, types_js_1.prune)(ex.args[i]).k !== "param" && bindings.set(p, ex.args[i]));
712
+ }
713
+ const params = info.fields.map((f) => ({ name: f.name, type: f.type, optional: f.defaultValue !== null }));
714
+ const fn = { k: "func", params, ret: types_js_1.T.void, variadic: null };
715
+ const r = checkArgs(c, e, fn, e.callee.span, null, bindings);
716
+ setCall(e, { form: "struct", id }, r.args);
717
+ const args2 = info.typeParams.map((p) => bindings.get(p) ?? { k: "param", name: p });
718
+ void args;
719
+ return { k: "struct", id, name: info.name, args: info.typeParams.length ? args2 : [] };
720
+ }
721
+ function constructInstance(c, e, cls, args, span) {
722
+ const problem = c.api.creationError(cls);
723
+ if (problem) {
724
+ c.error("RBX5056", span, problem);
725
+ }
726
+ if (c.api.classes[cls]?.deprecated) {
727
+ c.warn("RBX5010", span, `${cls} is deprecated`);
728
+ }
729
+ const props = [];
730
+ let parent = null;
731
+ for (const a of args) {
732
+ if (a.name === null) {
733
+ if (parent) {
734
+ c.error("RBX5057", a.value.span, `${cls}(…) takes properties by name; only one positional argument (the Parent) is allowed`, [`${cls}(Name: "X", Parent: workspace)`]);
735
+ (0, exprs_js_1.checkExpr)(c, a.value);
736
+ continue;
737
+ }
738
+ parent = a.value;
739
+ const pt = (0, exprs_js_1.checkExpr)(c, a.value);
740
+ c.expectAssignable(types_js_1.T.option(types_js_1.T.instance("Instance")), pt, a.value.span, "Parent");
741
+ continue;
742
+ }
743
+ const hit = c.api.member(cls, a.name);
744
+ if (!hit || (hit.member.kind !== "property" && hit.member.kind !== "callback")) {
745
+ const props2 = [...c.api.members(cls)].filter(([, h]) => h.member.kind === "property").map(([n]) => n);
746
+ const s = (0, diagnostics_js_1.suggest)(a.name, props2);
747
+ c.error("RBX5058", a.nameSpan ?? a.value.span, `${cls} has no property '${a.name}'`, s ? [`did you mean '${s}'?`] : undefined);
748
+ (0, exprs_js_1.checkExpr)(c, a.value);
749
+ continue;
750
+ }
751
+ if (hit.member.readonly) {
752
+ c.error("RBX5030", a.nameSpan ?? a.value.span, `${hit.owner}.${a.name} is read-only`);
753
+ }
754
+ const want = hit.member.type ? c.api.convert(hit.member.type) : types_js_1.T.any;
755
+ const vt = (0, exprs_js_1.checkExpr)(c, a.value, want);
756
+ c.expectAssignable(want, vt, a.value.span, `${cls}.${a.name}`);
757
+ if (a.name === "Parent") {
758
+ parent = a.value;
759
+ continue;
760
+ }
761
+ props.push(a);
762
+ }
763
+ setCall(e, { form: "instance", cls, props, parent }, []);
764
+ return types_js_1.T.instance(cls);
765
+ }
766
+ function constructDatatype(c, e, name, args) {
767
+ if (name !== "Vector3" && name !== "Vector2") {
768
+ c.requireRoblox(e.callee.span, name);
769
+ }
770
+ const positional = args.filter((a) => a.name === null);
771
+ const named = args.filter((a) => a.name !== null);
772
+ const ctor = c.api.datatypeStatic(name, "new");
773
+ if (!ctor || ctor.kind !== "function") {
774
+ c.error("RBX5059", e.callee.span, `${name} has no constructor`, Object.keys(c.api.datatypes[name]?.statics ?? {}).length ? [`use ${name}.${Object.keys(c.api.datatypes[name].statics)[0]}(…)`] : undefined);
775
+ checkArgsLoose(c, e);
776
+ return types_js_1.T.datatype(name);
777
+ }
778
+ const fn = (0, types_js_1.prune)(c.api.staticFunctionType(ctor));
779
+ const fake = { kind: "Call", callee: e.callee, args: positional, span: e.span };
780
+ const r = checkArgs(c, fake, fn, e.callee.span, null);
781
+ for (const a of named) {
782
+ const m = c.api.datatypeMember(name, a.name);
783
+ if (!m || m.kind !== "property") {
784
+ c.error("RBX5058", a.nameSpan ?? a.value.span, `${name} has no property '${a.name}'`);
785
+ (0, exprs_js_1.checkExpr)(c, a.value);
786
+ continue;
787
+ }
788
+ const want = m.type ? c.api.convert(m.type) : types_js_1.T.any;
789
+ const vt = (0, exprs_js_1.checkExpr)(c, a.value, want);
790
+ c.expectAssignable(want, vt, a.value.span, `${name}.${a.name}`);
791
+ }
792
+ setCall(e, { form: "datatype", name, props: named }, r.args, r.rest);
793
+ return types_js_1.T.datatype(name);
794
+ }
795
+ function fireSignal(c, e, sym) {
796
+ sym.used = true;
797
+ e.callee.res = { kind: "signal", sym };
798
+ e.callee.ty = sym.type;
799
+ const types = checkArgsLoose(c, e);
800
+ if (sym.signalParams) {
801
+ if (types.length !== sym.signalParams.length) {
802
+ c.error("CL3102", e.span, `signal ${sym.name} passes ${sym.signalParams.length} value(s), got ${types.length}`);
803
+ }
804
+ types.forEach((t, i) => sym.signalParams[i] && c.expectAssignable(sym.signalParams[i], t, e.args[i].value.span, `signal ${sym.name}`));
805
+ }
806
+ setCall(e, { form: "signalFire", sym }, e.args.map((a) => a.value));
807
+ return types_js_1.T.void;
808
+ }
809
+ // ---------- generic calls ----------
810
+ function finishCall(c, e, t, calleeSpan, form, expected, typeArgs) {
811
+ const p = (0, types_js_1.prune)(t);
812
+ const callee = e.callee;
813
+ const explicitTypeArgs = typeArgs ?? (callee.kind === "Name" ? callee.typeArgs : null);
814
+ if (p.k === "func") {
815
+ const decl = calleeDecl(c, callee);
816
+ const bindings = new Map();
817
+ if (explicitTypeArgs && explicitTypeArgs.length) {
818
+ const names = p.generics ?? [];
819
+ if (names.length !== explicitTypeArgs.length) {
820
+ c.error("CL3001", calleeSpan, `this function takes ${names.length} type argument(s), got ${explicitTypeArgs.length}`);
821
+ }
822
+ names.forEach((n, i) => explicitTypeArgs[i] && bindings.set(n, c.resolveType(explicitTypeArgs[i])));
823
+ for (const w of decl?.where ?? []) {
824
+ const bound = bindings.get(w.name);
825
+ const constraint = c.resolveType(w.type);
826
+ if (bound && (0, typeops_js_1.assignable)(c, constraint, bound) === "no") {
827
+ c.error("CL3116", calleeSpan, `${w.name} must be ${(0, types_js_1.typeToString)(constraint)}, got ${(0, types_js_1.typeToString)(bound)}`);
828
+ }
829
+ }
830
+ }
831
+ const r = checkArgs(c, e, p, calleeSpan, decl, bindings);
832
+ setCall(e, form, r.args, r.rest);
833
+ const sym = callee.res && (callee.res.kind === "func" || callee.res.kind === "local") ? callee.res.sym : null;
834
+ if (sym?.contextOnly) {
835
+ c.checkContext(sym.contextOnly, calleeSpan, `${sym.name}()`, `it is marked [[${sym.contextOnly}]]`);
836
+ }
837
+ let ret = r.fn.ret;
838
+ if (decl && (0, types_js_1.prune)(ret).k === "infer" && !(0, types_js_1.prune)(ret).resolved) {
839
+ c.ensureBodyChecked(decl);
840
+ ret = r.fn.ret;
841
+ }
842
+ if (decl?.fn?.mayYield || (decl && c.isChecking(decl))) {
843
+ c.noteYield();
844
+ }
845
+ if (callee.kind === "Member" && callee.res?.kind === "method") {
846
+ const info = c.structs.get(callee.res.struct);
847
+ const m = info?.methods.get(callee.name);
848
+ const root = (0, exprs_js_1.rootSymbol)(callee.object);
849
+ if (m && root) {
850
+ c.mutatingCalls.push({ root, method: m, span: e.span });
851
+ }
852
+ }
853
+ const out = (0, typeops_js_1.substitute)((0, types_js_1.prune)(ret), bindings);
854
+ return (0, types_js_1.prune)(out).k === "infer" ? types_js_1.T.any : out;
855
+ }
856
+ checkArgsLoose(c, e);
857
+ setCall(e, form, e.args.map((a) => a.value));
858
+ if ((0, types_js_1.isDynamic)(p)) {
859
+ if (callee.kind === "Member" && callee.res?.kind === "dynamic" && /^[A-Z]/.test(callee.name) && c.strict) {
860
+ c.warn("CL3117", callee.nameSpan, `'${callee.name}' is called on a dynamic value; for a Luau method use '::'`, [`x::${callee.name}(…) emits x:${callee.name}(…)`]);
861
+ }
862
+ return types_js_1.T.any;
863
+ }
864
+ if (p.k === "clSignal") {
865
+ return types_js_1.T.void;
866
+ }
867
+ c.error("CL3118", calleeSpan, `${(0, types_js_1.typeToString)(p)} is not callable`);
868
+ void expected;
869
+ return types_js_1.T.unknown;
870
+ }
871
+ function calleeDecl(c, callee) {
872
+ const res = callee.res;
873
+ if (!res) {
874
+ return null;
875
+ }
876
+ if (res.kind === "func") {
877
+ return res.sym.decl ?? null;
878
+ }
879
+ if (res.kind === "method") {
880
+ return c.structs.get(res.struct)?.methods.get(callee.kind === "Member" || callee.kind === "SelfField" ? callee.name : "")?.decl ?? null;
881
+ }
882
+ return null;
883
+ }
884
+ /** Match arguments to parameters (named, defaults, variadic), choose an overload, bind generics. */
885
+ function checkArgs(c, e, fn, calleeSpan, decl, bindings = new Map()) {
886
+ const overloads = fn.overloads ?? [fn];
887
+ let chosen = overloads[0];
888
+ if (overloads.length > 1) {
889
+ const positional = e.args.filter((a) => a.name === null).map((a) => (0, exprs_js_1.checkExpr)(c, a.value));
890
+ const fits = (o) => {
891
+ const required = o.params.filter((p) => !p.optional).length;
892
+ if (positional.length < required || (positional.length > o.params.length && !o.variadic)) {
893
+ return false;
894
+ }
895
+ return positional.every((t, i) => {
896
+ const want = o.params[i]?.type ?? o.variadic ?? types_js_1.T.any;
897
+ return (0, typeops_js_1.assignable)(c, want, t) === "ok";
898
+ });
899
+ };
900
+ const match = overloads.find(fits);
901
+ if (!match) {
902
+ const sigs = overloads.map((o) => `(${o.params.map((p) => `${p.name}: ${(0, types_js_1.typeToString)(p.type)}${p.optional ? "?" : ""}`).join(", ")})`);
903
+ c.error("CL3119", calleeSpan, `no overload accepts (${positional.map((t) => (0, types_js_1.typeToString)(t)).join(", ")})`, sigs.slice(0, 6).map((s) => `candidate ${s}`));
904
+ return { args: e.args.map((a) => a.value), rest: [], fn: chosen };
905
+ }
906
+ chosen = match;
907
+ }
908
+ const params = chosen.params;
909
+ const slots = params.map(() => null);
910
+ const rest = [];
911
+ let position = 0;
912
+ let named = false;
913
+ for (const a of e.args) {
914
+ if (a.name !== null) {
915
+ named = true;
916
+ const idx = params.findIndex((p) => p.name === a.name);
917
+ if (idx < 0) {
918
+ const s = (0, diagnostics_js_1.suggest)(a.name, params.map((p) => p.name));
919
+ c.error("CL3120", a.nameSpan ?? a.value.span, `no parameter named '${a.name}'`, s ? [`did you mean '${s}'?`] : [`parameters: ${params.map((p) => p.name).join(", ") || "none"}`]);
920
+ (0, exprs_js_1.checkExpr)(c, a.value);
921
+ continue;
922
+ }
923
+ if (slots[idx]) {
924
+ c.error("CL3121", a.nameSpan ?? a.value.span, `'${a.name}' is passed twice`);
925
+ }
926
+ slots[idx] = a.value;
927
+ continue;
928
+ }
929
+ if (named) {
930
+ c.error("CL3122", a.value.span, "positional arguments must come before named ones");
931
+ }
932
+ if (position < params.length) {
933
+ slots[position++] = a.value;
934
+ }
935
+ else if (chosen.variadic) {
936
+ rest.push(a.value);
937
+ position++;
938
+ }
939
+ else {
940
+ position++;
941
+ }
942
+ }
943
+ const given = e.args.length;
944
+ const max = chosen.variadic ? Infinity : params.length;
945
+ if (given > max) {
946
+ c.error("CL3123", calleeSpan, `wrong number of arguments: expected ${params.length}, got ${given}`);
947
+ }
948
+ params.forEach((p, i) => {
949
+ const arg = slots[i];
950
+ if (!arg) {
951
+ if (!p.optional) {
952
+ c.error("CL3124", calleeSpan, `missing argument '${p.name}' (${(0, types_js_1.typeToString)((0, typeops_js_1.substitute)(p.type, bindings))})`);
953
+ }
954
+ return;
955
+ }
956
+ let want = (0, typeops_js_1.substitute)(p.type, bindings);
957
+ const wantPruned = (0, types_js_1.prune)(want);
958
+ const learning = wantPruned.k === "infer" && !wantPruned.resolved;
959
+ const t = (0, exprs_js_1.checkExpr)(c, arg, learning ? undefined : want);
960
+ if (learning) {
961
+ // Learn the parameter type from its first call site.
962
+ const learned = (0, checker_js_1.widenLiteral)(t);
963
+ if ((0, types_js_1.prune)(learned).k !== "null" && !(0, types_js_1.isDynamic)(learned)) {
964
+ wantPruned.resolved = learned;
965
+ }
966
+ want = learned;
967
+ }
968
+ else {
969
+ (0, typeops_js_1.bindGenerics)(p.type, t, bindings);
970
+ want = (0, typeops_js_1.substitute)(p.type, bindings);
971
+ c.expectAssignable(want, t, arg.span, `argument '${p.name}'`);
972
+ }
973
+ if (decl) {
974
+ c.deferredCopies.push({ arg, decl, index: i });
975
+ }
976
+ else {
977
+ c.markCopy(arg);
978
+ }
979
+ });
980
+ for (const r of rest) {
981
+ const t = (0, exprs_js_1.checkExpr)(c, r, chosen.variadic ?? undefined);
982
+ if (chosen.variadic && !(0, types_js_1.isDynamic)(chosen.variadic)) {
983
+ c.expectAssignable((0, typeops_js_1.substitute)(chosen.variadic, bindings), t, r.span, "argument");
984
+ }
985
+ c.markCopy(r);
986
+ }
987
+ const resolvedFn = bindings.size ? (0, typeops_js_1.substitute)(chosen, bindings) : chosen;
988
+ return { args: trimTrailingNulls(slots), rest, fn: resolvedFn };
989
+ }
990
+ function trimTrailingNulls(args) {
991
+ let end = args.length;
992
+ while (end > 0 && args[end - 1] === null) {
993
+ end--;
994
+ }
995
+ return args.slice(0, end);
996
+ }
997
+ function joinTypes(c, types) {
998
+ return types.reduce((a, b) => (0, typeops_js_1.join)(c, a, b), types_js_1.T.never);
999
+ }