cluaupp 1.5.2 → 2.0.0-beta.2

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 (210) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +29 -41
  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/README.md +1 -1
  12. package/editors/vscode/package.json +50 -15
  13. package/editors/vscode/snippets/flare.json +1 -1
  14. package/editors/vscode/snippets/hive.json +1 -1
  15. package/editors/vscode/syntaxes/clpp.tmLanguage.json +211 -0
  16. package/examples/README.md +9 -9
  17. package/examples/game/cluaupp.config.json +2 -2
  18. package/examples/game/default.project.json +4 -15
  19. package/examples/game/out/ReplicatedStorage/Shared/GameInfo.luau +8 -0
  20. package/examples/game/out/ReplicatedStorage/Shared/GameInfo.luau.map.json +13 -0
  21. package/examples/game/out/ServerScriptService/Leaderstats.server.luau +26 -0
  22. package/examples/game/out/ServerScriptService/Leaderstats.server.luau.map.json +13 -0
  23. package/examples/game/out/ServerScriptService/Main.server.luau +15 -0
  24. package/examples/game/out/ServerScriptService/Main.server.luau.map.json +13 -0
  25. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Main.client.luau +10 -0
  26. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Main.client.luau.map.json +13 -0
  27. package/examples/game/src/ReplicatedStorage/Shared/GameInfo.clp +1 -0
  28. package/examples/game/src/ServerScriptService/Leaderstats.server.clp +18 -0
  29. package/examples/game/src/ServerScriptService/Main.server.clp +9 -0
  30. package/examples/game/src/StarterPlayer/StarterPlayerScripts/Main.client.clp +4 -0
  31. package/generated/api/build-profile.js +1 -1
  32. package/generated/api/loader.js +2 -2
  33. package/generated/api/surface/build.js +328 -0
  34. package/generated/api/surface/luau-defs.js +595 -0
  35. package/generated/api/surface/model.js +8 -0
  36. package/generated/api-cli.js +2 -2
  37. package/generated/cli.js +18 -17
  38. package/generated/clpp/contract.js +6 -3
  39. package/generated/clpp/handshake.js +36 -0
  40. package/generated/clpp/modules.js +20 -9
  41. package/generated/clpp/runner.js +62 -31
  42. package/generated/compile.js +18 -1
  43. package/generated/compiler/ast.js +3 -0
  44. package/generated/compiler/checker/calls.js +999 -0
  45. package/generated/compiler/checker/checker.js +1215 -0
  46. package/generated/compiler/checker/exprs.js +791 -0
  47. package/generated/compiler/checker/flow.js +168 -0
  48. package/generated/compiler/checker/members.js +763 -0
  49. package/generated/compiler/checker/statements.js +792 -0
  50. package/generated/compiler/checker/symbols.js +90 -0
  51. package/generated/compiler/checker/typeops.js +348 -0
  52. package/generated/compiler/checker/yields.js +115 -0
  53. package/generated/compiler/diagnostics.js +138 -0
  54. package/generated/compiler/emit/emitter.js +2356 -0
  55. package/generated/compiler/emit/luau-types.js +137 -0
  56. package/generated/compiler/emit/names.js +130 -0
  57. package/generated/compiler/emit/runtime.js +432 -0
  58. package/generated/compiler/lexer.js +403 -0
  59. package/generated/compiler/parser.js +1881 -0
  60. package/generated/compiler/project.js +332 -0
  61. package/generated/compiler/roblox/api.js +388 -0
  62. package/generated/compiler/roblox/context.js +100 -0
  63. package/generated/compiler/source.js +59 -0
  64. package/generated/compiler/stdlib.js +232 -0
  65. package/generated/compiler/types.js +250 -0
  66. package/generated/intellisense.js +1 -15
  67. package/generated/lsp.js +1 -1
  68. package/generated/target/build-cache.js +4 -4
  69. package/generated/target/datamodel-generate.js +12 -0
  70. package/generated/target/gen-rojo-tree.js +15 -0
  71. package/generated/target/source-map.js +61 -8
  72. package/generated/target/studio-bridge.js +1 -0
  73. package/generated/utils/project.js +104 -31
  74. package/include/README.md +5 -0
  75. package/native/bin/.gitkeep +1 -0
  76. package/package.json +17 -6
  77. package/plugins/CluauppNav/Plugin.luau +28 -4
  78. package/rust/cluaupp/Cargo.lock +795 -0
  79. package/rust/cluaupp/Cargo.toml +17 -0
  80. package/rust/cluaupp/src/main.rs +717 -0
  81. package/scripts/postinstall-native.js +56 -0
  82. package/skills/README.md +3 -3
  83. package/skills/clpp-hive-flare/SKILL.md +30 -26
  84. package/skills/clpp-language/SKILL.md +67 -74
  85. package/skills/clpp-language/compiler.md +24 -36
  86. package/skills/clpp-style/SKILL.md +31 -44
  87. package/src/api/build-profile.ts +1 -1
  88. package/src/api/loader.ts +2 -2
  89. package/src/api/surface/build.ts +374 -0
  90. package/src/api/surface/luau-defs.ts +635 -0
  91. package/src/api/surface/model.ts +96 -0
  92. package/src/api-cli.ts +2 -2
  93. package/src/cli.ts +19 -18
  94. package/src/clpp/contract.ts +20 -3
  95. package/src/clpp/handshake.ts +25 -0
  96. package/src/clpp/modules.ts +17 -10
  97. package/src/clpp/runner.ts +52 -34
  98. package/src/compile.ts +20 -2
  99. package/src/compiler/ast.ts +336 -0
  100. package/src/compiler/checker/calls.ts +1025 -0
  101. package/src/compiler/checker/checker.ts +1352 -0
  102. package/src/compiler/checker/exprs.ts +805 -0
  103. package/src/compiler/checker/flow.ts +174 -0
  104. package/src/compiler/checker/members.ts +793 -0
  105. package/src/compiler/checker/statements.ts +802 -0
  106. package/src/compiler/checker/symbols.ts +219 -0
  107. package/src/compiler/checker/typeops.ts +355 -0
  108. package/src/compiler/checker/yields.ts +120 -0
  109. package/src/compiler/diagnostics.ts +163 -0
  110. package/src/compiler/emit/emitter.ts +2486 -0
  111. package/src/compiler/emit/luau-types.ts +143 -0
  112. package/src/compiler/emit/names.ts +130 -0
  113. package/src/compiler/emit/runtime.ts +438 -0
  114. package/src/compiler/lexer.ts +444 -0
  115. package/src/compiler/parser.ts +1943 -0
  116. package/src/compiler/project.ts +354 -0
  117. package/src/compiler/roblox/api.ts +408 -0
  118. package/src/compiler/roblox/context.ts +108 -0
  119. package/src/compiler/source.ts +67 -0
  120. package/src/compiler/stdlib.ts +256 -0
  121. package/src/compiler/types.ts +313 -0
  122. package/src/intellisense.ts +1 -13
  123. package/src/lsp.ts +1 -1
  124. package/src/target/build-cache.ts +5 -8
  125. package/src/target/datamodel-generate.ts +12 -1
  126. package/src/target/gen-rojo-tree.ts +15 -0
  127. package/src/target/source-map.ts +65 -27
  128. package/src/target/studio-bridge.ts +3 -0
  129. package/src/types.ts +3 -0
  130. package/src/utils/project.ts +99 -37
  131. package/templates/README.md +7 -0
  132. package/templates/{game → game-modern}/cluaupp.config.json +2 -2
  133. package/templates/{game → game-modern}/package.json +1 -2
  134. package/templates/game-modern/rokit.toml +2 -0
  135. package/templates/game-modern/src/ReplicatedStorage/Shared/GameInfo.clp +1 -0
  136. package/templates/game-modern/src/ServerScriptService/Handlers/Guard.server.clp +4 -0
  137. package/templates/game-modern/src/ServerScriptService/Main.server.clp +8 -0
  138. package/templates/game-modern/src/StarterPlayer/StarterPlayerScripts/Main.client.clp +4 -0
  139. package/examples/game/out/ReplicatedStorage/Shared/Constants/Datas/TemplateData.luau +0 -29
  140. package/examples/game/out/ReplicatedStorage/Shared/Constants/Datas/TemplateData.luau.map.json +0 -13
  141. package/examples/game/out/ReplicatedStorage/Shared/Net/Net.luau +0 -83
  142. package/examples/game/out/ReplicatedStorage/Shared/Net/Net.luau.map.json +0 -13
  143. package/examples/game/out/ServerScriptService/Boot/DataBoot.server.luau +0 -18
  144. package/examples/game/out/ServerScriptService/Boot/DataBoot.server.luau.map.json +0 -13
  145. package/examples/game/out/ServerScriptService/Configuration/ServerConfig.luau +0 -14
  146. package/examples/game/out/ServerScriptService/Configuration/ServerConfig.luau.map.json +0 -13
  147. package/examples/game/out/ServerScriptService/Handlers/Guard.server.luau +0 -15
  148. package/examples/game/out/ServerScriptService/Handlers/Guard.server.luau.map.json +0 -13
  149. package/examples/game/out/ServerScriptService/Handlers/PlayerHandler.server.luau +0 -50
  150. package/examples/game/out/ServerScriptService/Handlers/PlayerHandler.server.luau.map.json +0 -13
  151. package/examples/game/out/StarterPlayer/StarterCharacterScripts/Character/CharacterReady.client.luau +0 -12
  152. package/examples/game/out/StarterPlayer/StarterCharacterScripts/Character/CharacterReady.client.luau.map.json +0 -13
  153. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Controllers/DataController.client.luau +0 -16
  154. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Controllers/DataController.client.luau.map.json +0 -13
  155. package/examples/game/src/Client/Controllers/DataController.client.clpp +0 -7
  156. package/examples/game/src/Client/Main.client.clpp +0 -4
  157. package/examples/game/src/Declarations/Net.flare +0 -5
  158. package/examples/game/src/Include/GameTypes.clh +0 -40
  159. package/examples/game/src/Modules/Core/Guard.server.clpp +0 -7
  160. package/examples/game/src/Modules/Core/PlayerHandler.server.clpp +0 -23
  161. package/examples/game/src/Server/Boot/DataBoot.server.clpp +0 -13
  162. package/examples/game/src/Server/Main.server.clpp +0 -4
  163. package/templates/game/.clangd +0 -25
  164. package/templates/game/.vscode/extensions.json +0 -8
  165. package/templates/game/.vscode/settings.json +0 -17
  166. package/templates/game/Packages/.gitkeep +0 -0
  167. package/templates/game/compile_flags.txt +0 -6
  168. package/templates/game/default.project.json +0 -41
  169. package/templates/game/include/clpp/datatypes.clh +0 -575
  170. package/templates/game/include/clpp/generated/enums.clh +0 -4920
  171. package/templates/game/include/clpp/generated/instances.clh +0 -87182
  172. package/templates/game/include/clpp/libs/arrayindexer.clh +0 -12
  173. package/templates/game/include/clpp/libs/axiom.clh +0 -154
  174. package/templates/game/include/clpp/libs/bloom.clh +0 -58
  175. package/templates/game/include/clpp/libs/coil.clh +0 -37
  176. package/templates/game/include/clpp/libs/crest.clh +0 -22
  177. package/templates/game/include/clpp/libs/echo.clh +0 -52
  178. package/templates/game/include/clpp/libs/ember.clh +0 -17
  179. package/templates/game/include/clpp/libs/flare.clh +0 -65
  180. package/templates/game/include/clpp/libs/gleam.clh +0 -111
  181. package/templates/game/include/clpp/libs/guide.clh +0 -42
  182. package/templates/game/include/clpp/libs/helm.clh +0 -31
  183. package/templates/game/include/clpp/libs/hive.clh +0 -20
  184. package/templates/game/include/clpp/libs/keep.clh +0 -158
  185. package/templates/game/include/clpp/libs/lens.clh +0 -24
  186. package/templates/game/include/clpp/libs/mint.clh +0 -28
  187. package/templates/game/include/clpp/libs/net.clh +0 -31
  188. package/templates/game/include/clpp/libs/occlude.clh +0 -15
  189. package/templates/game/include/clpp/libs/pin.clh +0 -28
  190. package/templates/game/include/clpp/libs/promise.clh +0 -24
  191. package/templates/game/include/clpp/libs/roster.clh +0 -123
  192. package/templates/game/include/clpp/libs/shift.clh +0 -22
  193. package/templates/game/include/clpp/libs/spark.clh +0 -28
  194. package/templates/game/include/clpp/libs/stage.clh +0 -23
  195. package/templates/game/include/clpp/libs/sweep.clh +0 -23
  196. package/templates/game/include/clpp/libs/trace.clh +0 -18
  197. package/templates/game/include/clpp/libs/ward.clh +0 -19
  198. package/templates/game/include/clpp/libs.clh +0 -31
  199. package/templates/game/include/clpp/roblox.clh +0 -41
  200. package/templates/game/rokit.toml +0 -5
  201. package/templates/game/src/Client/Controllers/DataController.client.clpp +0 -7
  202. package/templates/game/src/Client/Main.client.clpp +0 -4
  203. package/templates/game/src/Declarations/Net.flare +0 -5
  204. package/templates/game/src/Include/GameTypes.clh +0 -40
  205. package/templates/game/src/Modules/Core/Guard.server.clpp +0 -7
  206. package/templates/game/src/Modules/Core/PlayerHandler.server.clpp +0 -24
  207. package/templates/game/src/Server/Boot/DataBoot.server.clpp +0 -13
  208. package/templates/game/src/Server/Main.server.clpp +0 -4
  209. package/templates/game/stylua.toml +0 -9
  210. package/templates/game/wally.toml +0 -12
@@ -0,0 +1,792 @@
1
+ "use strict";
2
+ /** Statement checking. */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.isNumeric = void 0;
5
+ exports.checkStatement = checkStatement;
6
+ exports.checkHandler = checkHandler;
7
+ const types_js_1 = require("../types.js");
8
+ Object.defineProperty(exports, "isNumeric", { enumerable: true, get: function () { return types_js_1.isNumeric; } });
9
+ const checker_js_1 = require("./checker.js");
10
+ const exprs_js_1 = require("./exprs.js");
11
+ const flow_js_1 = require("./flow.js");
12
+ const members_js_1 = require("./members.js");
13
+ const typeops_js_1 = require("./typeops.js");
14
+ const yields_js_1 = require("./yields.js");
15
+ function checkStatement(c, stmt) {
16
+ switch (stmt.kind) {
17
+ case "Var":
18
+ checkVar(c, stmt);
19
+ return;
20
+ case "Assign":
21
+ checkAssign(c, stmt);
22
+ return;
23
+ case "Expr":
24
+ checkExprStatement(c, stmt);
25
+ return;
26
+ case "If":
27
+ checkIf(c, stmt);
28
+ return;
29
+ case "While": {
30
+ const cond = (0, exprs_js_1.checkCondition)(c, stmt.cond);
31
+ void cond;
32
+ c.loopDepth++;
33
+ const scope = c.pushScope("loop");
34
+ (0, flow_js_1.applyNarrowings)(scope, (0, flow_js_1.conditionNarrowings)(c, stmt.cond, true));
35
+ for (const s of stmt.body) {
36
+ checkStatement(c, s);
37
+ }
38
+ c.popScope();
39
+ c.loopDepth--;
40
+ checkInfiniteLoop(c, stmt, stmt.cond.kind === "Bool" && stmt.cond.value, stmt.body);
41
+ return;
42
+ }
43
+ case "For": {
44
+ c.pushScope("loop");
45
+ if (stmt.init) {
46
+ checkStatement(c, stmt.init);
47
+ }
48
+ if (stmt.cond) {
49
+ (0, exprs_js_1.checkCondition)(c, stmt.cond);
50
+ }
51
+ c.loopDepth++;
52
+ c.pushScope("block");
53
+ for (const s of stmt.body) {
54
+ checkStatement(c, s);
55
+ }
56
+ c.popScope();
57
+ c.loopDepth--;
58
+ if (stmt.step) {
59
+ checkStatement(c, stmt.step);
60
+ }
61
+ c.popScope();
62
+ checkInfiniteLoop(c, stmt, stmt.cond === null || (stmt.cond.kind === "Bool" && stmt.cond.value), stmt.body);
63
+ return;
64
+ }
65
+ case "ForIn":
66
+ checkForIn(c, stmt);
67
+ return;
68
+ case "Break":
69
+ if (c.loopDepth === 0 && c.switchDepth === 0) {
70
+ c.error("CL4003", stmt.span, "break outside of a loop or switch");
71
+ }
72
+ return;
73
+ case "Continue":
74
+ if (c.loopDepth === 0) {
75
+ c.error("CL4003", stmt.span, "continue outside of a loop");
76
+ }
77
+ return;
78
+ case "Return":
79
+ checkReturn(c, stmt);
80
+ return;
81
+ case "Switch":
82
+ checkSwitch(c, stmt);
83
+ return;
84
+ case "Match":
85
+ checkMatch(c, stmt);
86
+ return;
87
+ case "Try": {
88
+ c.pushScope("block");
89
+ for (const s of stmt.body) {
90
+ checkStatement(c, s);
91
+ }
92
+ c.popScope();
93
+ c.pushScope("block");
94
+ if (stmt.catchName) {
95
+ const sym = c.declare(stmt.catchName.name, "var", types_js_1.T.string, stmt.catchName.span);
96
+ sym.used = true;
97
+ stmt.catchName.sym = sym;
98
+ }
99
+ for (const s of stmt.handler) {
100
+ checkStatement(c, s);
101
+ }
102
+ c.popScope();
103
+ return;
104
+ }
105
+ case "Output": {
106
+ const t = (0, exprs_js_1.checkExpr)(c, stmt.value);
107
+ if ((0, types_js_1.prune)(t).k === "void") {
108
+ c.error("CL3013", stmt.value.span, "this expression has no value to print");
109
+ }
110
+ return;
111
+ }
112
+ case "Connect":
113
+ checkConnect(c, stmt);
114
+ return;
115
+ case "Signal": {
116
+ if (c.scope === c.fileScope && stmt.name.sym) {
117
+ return; // declared in the declare pass
118
+ }
119
+ const params = stmt.params.map((p) => c.resolveType(p));
120
+ const sym = c.declare(stmt.name.name, "signal", { k: "clSignal", params: params.length ? params : null }, stmt.name.span);
121
+ sym.signalParams = params.length ? params : null;
122
+ sym.used = true;
123
+ stmt.name.sym = sym;
124
+ return;
125
+ }
126
+ case "Using": {
127
+ const mod = c.scope.lookup(stmt.module);
128
+ if (!mod || mod.kind !== "module") {
129
+ c.error("CL2001", stmt.span, `'${stmt.module}' is not a linked module`);
130
+ return;
131
+ }
132
+ mod.used = true;
133
+ const fake = {
134
+ kind: "Member",
135
+ object: { kind: "Name", name: stmt.module, typeArgs: null, span: stmt.span },
136
+ name: stmt.member,
137
+ nameSpan: stmt.memberSpan,
138
+ typeArgs: null,
139
+ span: stmt.span,
140
+ };
141
+ const t = (0, exprs_js_1.checkExpr)(c, fake);
142
+ const sym = c.declare(stmt.member, "const", t, stmt.memberSpan);
143
+ sym.used = true;
144
+ stmt.target = fake;
145
+ stmt.sym = sym;
146
+ if (fake.res?.kind === "const") {
147
+ sym.constValue = fake.res.value;
148
+ }
149
+ return;
150
+ }
151
+ case "Block":
152
+ c.pushScope("block");
153
+ for (const s of stmt.body) {
154
+ checkStatement(c, s);
155
+ }
156
+ c.popScope();
157
+ return;
158
+ case "Decl": {
159
+ const decl = stmt.decl;
160
+ if (decl.kind === "Func" && c.fn) {
161
+ const sym = c.declare(decl.name, "func", c.signatureOf(decl, null), decl.nameSpan);
162
+ sym.decl = decl;
163
+ sym.used = true;
164
+ c.funcSyms.set(decl, sym);
165
+ checkNestedFunction(c, decl, sym);
166
+ return;
167
+ }
168
+ if (c.fn || c.scope !== c.fileScope) {
169
+ c.error("CL2030", stmt.span, `${decl.kind.toLowerCase()} declarations must be at the top level of the file`);
170
+ }
171
+ return;
172
+ }
173
+ default:
174
+ return;
175
+ }
176
+ }
177
+ function checkNestedFunction(c, decl, sym) {
178
+ const outer = c.fn;
179
+ const ctx = { ...outer, decl, declaredRet: decl.returnType ? c.resolveType(decl.returnType) : null, returns: [], depth: outer.depth + 1, yields: false, sym };
180
+ c.fn = ctx;
181
+ c.pushScope("function");
182
+ c.declareParams(decl.params);
183
+ for (const s of decl.body || []) {
184
+ checkStatement(c, s);
185
+ }
186
+ c.finishFunction(ctx, decl.body || [], decl.nameSpan, decl.name);
187
+ c.popScope();
188
+ c.fn = outer;
189
+ if (ctx.yields) {
190
+ outer.yields = true;
191
+ }
192
+ const fnType = sym.type;
193
+ if (!decl.returnType && (0, types_js_1.prune)(fnType.ret).k === "infer") {
194
+ (0, types_js_1.prune)(fnType.ret).resolved = ctx.returns.length ? ctx.returns.reduce((a, b) => (sameish(a, b) ? a : types_js_1.T.any)) : types_js_1.T.void;
195
+ }
196
+ (decl.fn ||= {}).mayYield = ctx.yields;
197
+ }
198
+ function sameish(a, b) {
199
+ return (0, types_js_1.typeToString)(a) === (0, types_js_1.typeToString)(b);
200
+ }
201
+ // ---------- declarations ----------
202
+ function checkVar(c, stmt) {
203
+ const predeclared = stmt.predeclared === true;
204
+ let declared = null;
205
+ if (stmt.declaredType) {
206
+ declared = c.resolveType(stmt.declaredType);
207
+ }
208
+ else if (stmt.decltypeOf) {
209
+ declared = (0, checker_js_1.widenLiteral)((0, exprs_js_1.checkExpr)(c, stmt.decltypeOf));
210
+ }
211
+ else if (stmt.declKind === "atomic") {
212
+ declared = types_js_1.T.int;
213
+ }
214
+ let initType = null;
215
+ if (stmt.init) {
216
+ initType = (0, exprs_js_1.checkExpr)(c, stmt.init, declared ?? undefined, stmt.unpack);
217
+ if ((0, types_js_1.prune)(initType).k === "void") {
218
+ c.error("CL3013", stmt.init.span, "this expression has no value");
219
+ initType = types_js_1.T.unknown;
220
+ }
221
+ if (declared) {
222
+ c.expectAssignable(declared, initType, stmt.init.span, `cannot initialize '${stmt.names.map((n) => n.name).join(", ")}'`);
223
+ }
224
+ c.markCopy(stmt.init);
225
+ }
226
+ else if (!declared || ((0, types_js_1.prune)(declared).k !== "option" && (0, types_js_1.prune)(declared).k !== "any")) {
227
+ const name = stmt.names[0].name;
228
+ c.error("CL3014", stmt.span, `'${name}' needs an initial value`, declared && (0, types_js_1.prune)(declared).k === "instance" ? [`write \`${(0, types_js_1.typeToString)(declared)}? ${name} = null;\` for an Instance that starts empty`] : ["CL++ variables are always initialized: `let x = …;`"]);
229
+ }
230
+ if (stmt.declKind === "constexpr" && stmt.init && (0, checker_js_1.foldConstant)(stmt.init) === undefined) {
231
+ c.error("CL3015", stmt.init.span, "constexpr needs a value computable at compile time (literals and arithmetic on them)");
232
+ }
233
+ const mutable = stmt.declKind === "let mut" || stmt.declKind === "typed" || stmt.declKind === "auto" || stmt.declKind === "observable" || stmt.declKind === "decltype";
234
+ if (stmt.unpack) {
235
+ const src = (0, types_js_1.prune)(initType ?? types_js_1.T.unknown);
236
+ stmt.names.forEach((n, i) => {
237
+ let t = types_js_1.T.any;
238
+ if (src.k === "list") {
239
+ t = src.elem;
240
+ }
241
+ else if (src.k === "tuple") {
242
+ t = src.elems[i] ?? types_js_1.T.void;
243
+ if (i >= src.elems.length) {
244
+ c.error("CL3016", n.span, `the value only has ${src.elems.length} element(s)`);
245
+ }
246
+ }
247
+ else if (!(0, types_js_1.isDynamic)(src)) {
248
+ c.error("CL3016", stmt.init?.span ?? stmt.span, `cannot destructure ${(0, types_js_1.typeToString)(src)}; expected a list`);
249
+ }
250
+ const sym = c.declare(n.name, "var", (0, checker_js_1.widenLiteral)(t), n.span, mutable);
251
+ n.sym = sym;
252
+ });
253
+ return;
254
+ }
255
+ const n = stmt.names[0];
256
+ let type = declared ?? (0, checker_js_1.widenLiteral)(initType ?? types_js_1.T.unknown);
257
+ if (stmt.declaredType && stmt.declaredType.kind === "Literal") {
258
+ type = declared;
259
+ }
260
+ if (predeclared && n.sym) {
261
+ const pre = n.sym;
262
+ if ((0, types_js_1.prune)(pre.type).k === "unknown") {
263
+ pre.type = type;
264
+ }
265
+ return;
266
+ }
267
+ const kind = stmt.declKind === "const" || stmt.declKind === "constexpr" ? "const" : "var";
268
+ const sym = c.declare(n.name, kind, type, n.span, mutable);
269
+ n.sym = sym;
270
+ if (kind === "const" && stmt.init) {
271
+ const v = (0, checker_js_1.foldConstant)(stmt.init);
272
+ if (v !== undefined) {
273
+ sym.constValue = v;
274
+ }
275
+ }
276
+ if (stmt.declKind === "observable") {
277
+ sym.observable = true;
278
+ sym.handlers = [];
279
+ }
280
+ if (stmt.declKind === "atomic") {
281
+ sym.atomic = true;
282
+ }
283
+ if (initType && (0, types_js_1.prune)(initType).k !== "null" && (0, types_js_1.prune)(type).k === "option") {
284
+ // `Part? p = Part()` is known non-null until reassigned.
285
+ const key = `s${sym.id}`;
286
+ if ((0, types_js_1.prune)(initType).k !== "option") {
287
+ c.scope.narrowed.set(key, (0, types_js_1.nonNull)(type));
288
+ }
289
+ }
290
+ }
291
+ // ---------- assignment ----------
292
+ function checkAssign(c, stmt) {
293
+ const target = (0, members_js_1.checkAssignTarget)(c, stmt.target, stmt.op !== "=");
294
+ const targetType = target.type;
295
+ let valueType = (0, exprs_js_1.checkExpr)(c, stmt.value, stmt.op === "=" ? targetType : undefined);
296
+ if (stmt.op === "=") {
297
+ c.expectAssignable(targetType, valueType, stmt.value.span, "cannot assign");
298
+ c.markCopy(stmt.value);
299
+ }
300
+ else {
301
+ const op = stmt.op[0];
302
+ const tt = (0, types_js_1.prune)(targetType);
303
+ const vt = (0, types_js_1.prune)(valueType);
304
+ if ((0, types_js_1.isNumeric)(tt) && (0, types_js_1.isNumeric)(vt)) {
305
+ if (tt.k === "int" && (vt.k === "float" || (vt.k === "lit" && !Number.isInteger(vt.value)))) {
306
+ c.error("CL3012", stmt.value.span, `'${stmt.op}' would store a float in the int '${describeTarget(stmt.target)}'`, ["convert with int(...) or declare the variable as float"]);
307
+ }
308
+ }
309
+ else if (tt.k === "datatype" || vt.k === "datatype") {
310
+ const result = c.api.operator(op, tt, vt);
311
+ if (!result) {
312
+ c.error("CL3017", stmt.span, `'${stmt.op}' is not defined for ${(0, types_js_1.typeToString)(tt)} and ${(0, types_js_1.typeToString)(vt)}`);
313
+ }
314
+ else {
315
+ valueType = result;
316
+ }
317
+ }
318
+ else if (!(0, types_js_1.isDynamic)(tt) && !(0, types_js_1.isDynamic)(vt)) {
319
+ if (stmt.op === "+=" && (0, types_js_1.isStringLike)(tt)) {
320
+ c.error("CL3017", stmt.span, "strings are joined with `.:` in CL++", [`write \`${describeTarget(stmt.target)} = ${describeTarget(stmt.target)} .: …;\``]);
321
+ }
322
+ else {
323
+ c.error("CL3017", stmt.span, `'${stmt.op}' needs numbers, got ${(0, types_js_1.typeToString)(tt)} and ${(0, types_js_1.typeToString)(vt)}`);
324
+ }
325
+ }
326
+ }
327
+ const key = (0, exprs_js_1.exprPathKey)(stmt.target);
328
+ if (key) {
329
+ c.scope.invalidate(key);
330
+ const vt = (0, types_js_1.prune)(valueType);
331
+ if (stmt.op === "=" && (0, types_js_1.prune)(targetType).k === "option" && vt.k !== "option" && vt.k !== "null" && !(0, types_js_1.isDynamic)(vt)) {
332
+ c.scope.narrowed.set(key, (0, types_js_1.nonNull)(targetType));
333
+ }
334
+ }
335
+ }
336
+ function describeTarget(e) {
337
+ switch (e.kind) {
338
+ case "Name":
339
+ return e.name;
340
+ case "Member":
341
+ return `${describeTarget(e.object)}.${e.name}`;
342
+ case "SelfField":
343
+ return `@${e.name}`;
344
+ case "Index":
345
+ return `${describeTarget(e.object)}[…]`;
346
+ default:
347
+ return "value";
348
+ }
349
+ }
350
+ function checkExprStatement(c, stmt) {
351
+ const e = stmt.expr;
352
+ (0, exprs_js_1.checkExpr)(c, e);
353
+ switch (e.kind) {
354
+ case "Call":
355
+ case "New":
356
+ case "Update":
357
+ case "Await":
358
+ case "Spawn":
359
+ case "Thread":
360
+ case "Join":
361
+ case "Parallel":
362
+ case "PCall":
363
+ case "Error":
364
+ return;
365
+ default:
366
+ c.warn("CL4004", e.span, "this expression has no effect", ["did you mean an assignment or a call?"]);
367
+ }
368
+ }
369
+ // ---------- control flow ----------
370
+ function checkIf(c, stmt) {
371
+ if (stmt.letName) {
372
+ const t = (0, exprs_js_1.checkExpr)(c, stmt.cond);
373
+ const scope = c.pushScope("block");
374
+ const sym = c.declare(stmt.letName.name, "var", (0, checker_js_1.widenLiteral)((0, types_js_1.nonNull)(t)), stmt.letName.span);
375
+ sym.used = true;
376
+ stmt.letName.sym = sym;
377
+ void scope;
378
+ for (const s of stmt.then) {
379
+ checkStatement(c, s);
380
+ }
381
+ c.popScope();
382
+ if (stmt.else) {
383
+ c.pushScope("block");
384
+ for (const s of stmt.else) {
385
+ checkStatement(c, s);
386
+ }
387
+ c.popScope();
388
+ }
389
+ return;
390
+ }
391
+ (0, exprs_js_1.checkCondition)(c, stmt.cond);
392
+ const yes = (0, flow_js_1.conditionNarrowings)(c, stmt.cond, true);
393
+ const no = (0, flow_js_1.conditionNarrowings)(c, stmt.cond, false);
394
+ const thenScope = c.pushScope("block");
395
+ (0, flow_js_1.applyNarrowings)(thenScope, yes);
396
+ for (const s of stmt.then) {
397
+ checkStatement(c, s);
398
+ }
399
+ c.popScope();
400
+ if (stmt.else) {
401
+ const elseScope = c.pushScope("block");
402
+ (0, flow_js_1.applyNarrowings)(elseScope, no);
403
+ for (const s of stmt.else) {
404
+ checkStatement(c, s);
405
+ }
406
+ c.popScope();
407
+ }
408
+ const thenExits = (0, flow_js_1.alwaysExits)(c, stmt.then);
409
+ const elseExits = stmt.else ? (0, flow_js_1.alwaysExits)(c, stmt.else) : false;
410
+ if (thenExits && !elseExits) {
411
+ (0, flow_js_1.applyNarrowings)(c.scope, no);
412
+ }
413
+ else if (elseExits && !thenExits) {
414
+ (0, flow_js_1.applyNarrowings)(c.scope, yes);
415
+ }
416
+ }
417
+ function iterationOf(c, t, span) {
418
+ const p = (0, types_js_1.prune)(t);
419
+ switch (p.k) {
420
+ case "int":
421
+ case "lit":
422
+ if (typeof p.value === "string") {
423
+ return { kind: "string", elem: types_js_1.T.string };
424
+ }
425
+ return { kind: "range", elem: types_js_1.T.int };
426
+ case "float":
427
+ c.error("CL3018", span, "for … in over a number needs an int count", ["convert with int(...)"]);
428
+ return { kind: "range", elem: types_js_1.T.int };
429
+ case "string":
430
+ return { kind: "string", elem: types_js_1.T.string };
431
+ case "list":
432
+ return { kind: "list", elem: p.elem };
433
+ case "tuple":
434
+ return { kind: "list", elem: p.elems.reduce((a, b) => ((0, types_js_1.typeToString)(a) === (0, types_js_1.typeToString)(b) ? a : types_js_1.T.any), p.elems[0] ?? types_js_1.T.any) };
435
+ case "dict":
436
+ return { kind: "dict", elem: p.key };
437
+ case "table":
438
+ return { kind: "dict", elem: types_js_1.T.string };
439
+ case "option":
440
+ c.strictError("CL3011", span, `${(0, types_js_1.typeToString)(p)} may be null; check it before iterating`);
441
+ return iterationOf(c, p.inner, span);
442
+ case "any":
443
+ case "unknown":
444
+ case "infer":
445
+ return { kind: "generic", elem: types_js_1.T.any };
446
+ case "instance":
447
+ c.error("CL3018", span, `cannot iterate over ${p.cls} itself`, ["iterate its children: `for (let child in x.GetChildren())`"]);
448
+ return { kind: "generic", elem: types_js_1.T.any };
449
+ default:
450
+ c.error("CL3018", span, `cannot iterate over ${(0, types_js_1.typeToString)(p)}`);
451
+ return { kind: "generic", elem: types_js_1.T.any };
452
+ }
453
+ }
454
+ function checkForIn(c, stmt) {
455
+ const it = (0, exprs_js_1.checkExpr)(c, stmt.iterable);
456
+ const { kind, elem } = iterationOf(c, it, stmt.iterable.span);
457
+ stmt.iterKind = kind;
458
+ let varType = (0, checker_js_1.widenLiteral)(elem);
459
+ if (stmt.declaredType) {
460
+ const declared = c.resolveType(stmt.declaredType);
461
+ const r = (0, typeops_js_1.assignable)(c, declared, elem);
462
+ const de = (0, types_js_1.prune)(declared);
463
+ const ee = (0, types_js_1.prune)(elem);
464
+ const downcast = de.k === "instance" && ee.k === "instance" && c.api.isA(de.cls, ee.cls);
465
+ if (r !== "ok" && !downcast) {
466
+ c.expectAssignable(declared, elem, stmt.iterable.span, `loop variable '${stmt.name.name}'`);
467
+ }
468
+ varType = declared;
469
+ if (downcast) {
470
+ stmt.downcast = true;
471
+ }
472
+ }
473
+ c.loopDepth++;
474
+ c.pushScope("loop");
475
+ const sym = c.declare(stmt.name.name, "var", varType, stmt.name.span, stmt.mutable);
476
+ sym.used = sym.used || stmt.name.name === "_";
477
+ stmt.name.sym = sym;
478
+ for (const s of stmt.body) {
479
+ checkStatement(c, s);
480
+ }
481
+ c.popScope();
482
+ c.loopDepth--;
483
+ if (!sym.used && !stmt.name.name.startsWith("_")) {
484
+ // reported by popScope as unused
485
+ }
486
+ }
487
+ function checkReturn(c, stmt) {
488
+ const fn = c.fn;
489
+ if (!fn) {
490
+ if (c.input.kind === "module") {
491
+ c.error("CL4005", stmt.span, "return outside of a function");
492
+ }
493
+ else if (stmt.value) {
494
+ c.error("CL4005", stmt.span, "a script cannot return a value");
495
+ }
496
+ if (stmt.value) {
497
+ (0, exprs_js_1.checkExpr)(c, stmt.value);
498
+ }
499
+ return;
500
+ }
501
+ const expected = fn.declaredRet;
502
+ if (!stmt.value) {
503
+ if (expected && !(0, types_js_1.isDynamic)(expected) && (0, types_js_1.prune)(expected).k !== "void" && (0, types_js_1.prune)(expected).k !== "option") {
504
+ c.error("CL4006", stmt.span, `'${fn.decl?.name ?? "function"}' must return ${(0, types_js_1.typeToString)(expected)}`);
505
+ }
506
+ fn.returns.push(types_js_1.T.void);
507
+ return;
508
+ }
509
+ const t = (0, exprs_js_1.checkExpr)(c, stmt.value, expected ?? undefined);
510
+ // Returning a local hands it over (it dies with the frame); only aliased places need a copy.
511
+ if (!(stmt.value.kind === "Name" && stmt.value.res?.kind === "local")) {
512
+ c.markCopy(stmt.value);
513
+ }
514
+ if (expected) {
515
+ if ((0, types_js_1.prune)(expected).k === "void" && (0, types_js_1.prune)(t).k !== "void") {
516
+ c.error("CL4007", stmt.value.span, `'${fn.decl?.name ?? "function"}' is declared -> void and cannot return a value`);
517
+ }
518
+ else {
519
+ c.expectAssignable(expected, t, stmt.value.span, "cannot return this value");
520
+ }
521
+ }
522
+ fn.returns.push(t);
523
+ }
524
+ function checkSwitch(c, stmt) {
525
+ const subject = (0, exprs_js_1.checkExpr)(c, stmt.subject);
526
+ c.switchDepth++;
527
+ for (const k of stmt.cases) {
528
+ if (k.test) {
529
+ const t = (0, exprs_js_1.checkExpr)(c, k.test, subject);
530
+ if ((0, typeops_js_1.assignable)(c, (0, checker_js_1.widenLiteral)(subject), t) === "no" && (0, typeops_js_1.assignable)(c, (0, checker_js_1.widenLiteral)(t), subject) === "no") {
531
+ c.error("CL3019", k.test.span, `case ${(0, types_js_1.typeToString)(t)} can never match a ${(0, types_js_1.typeToString)(subject)}`);
532
+ }
533
+ }
534
+ c.pushScope("block");
535
+ for (const s of k.body) {
536
+ checkStatement(c, s);
537
+ }
538
+ c.popScope();
539
+ }
540
+ c.switchDepth--;
541
+ }
542
+ // ---------- match ----------
543
+ function checkMatch(c, stmt) {
544
+ const subjectType = (0, exprs_js_1.checkExpr)(c, stmt.subject);
545
+ const subject = (0, types_js_1.prune)(subjectType);
546
+ const covered = new Set();
547
+ let wildcard = false;
548
+ for (const arm of stmt.arms) {
549
+ if (wildcard) {
550
+ c.warn("CL4008", arm.span, "this arm is unreachable: an earlier `_` arm matches everything");
551
+ }
552
+ c.pushScope("block");
553
+ const bound = checkPattern(c, arm.pattern, subject, covered);
554
+ if (arm.pattern.kind === "Wildcard" && !arm.guard) {
555
+ wildcard = true;
556
+ }
557
+ if (bound && arm.pattern.kind === "Case" && arm.pattern.binding) {
558
+ const sym = c.declare(arm.pattern.binding.name, "var", bound, arm.pattern.binding.span);
559
+ sym.used = true;
560
+ arm.pattern.binding.sym = sym;
561
+ }
562
+ if (arm.guard) {
563
+ (0, exprs_js_1.checkCondition)(c, arm.guard);
564
+ (0, flow_js_1.applyNarrowings)(c.scope, (0, flow_js_1.conditionNarrowings)(c, arm.guard, true));
565
+ }
566
+ for (const s of arm.body) {
567
+ checkStatement(c, s);
568
+ }
569
+ c.popScope();
570
+ if (arm.guard && arm.pattern.kind === "Case") {
571
+ covered.delete(arm.pattern.name);
572
+ }
573
+ }
574
+ const required = requiredCases(c, subject);
575
+ if (required && !wildcard) {
576
+ const missing = required.filter((r) => !covered.has(r));
577
+ if (missing.length) {
578
+ c.error("CL4009", stmt.span, `non-exhaustive match: missing ${missing.map((m) => `'${m}'`).join(", ")}`, ["add the missing arms or a `_ ~> …` arm"]);
579
+ }
580
+ else {
581
+ stmt.exhaustive = true;
582
+ }
583
+ }
584
+ else if (wildcard) {
585
+ stmt.exhaustive = true;
586
+ }
587
+ }
588
+ function requiredCases(c, subject) {
589
+ switch (subject.k) {
590
+ case "enum": {
591
+ const info = c.enums.get(subject.id);
592
+ return info ? info.cases.map((k) => k.name) : null;
593
+ }
594
+ case "option":
595
+ return ["Some", "None"];
596
+ case "result":
597
+ return ["Ok", "Err"];
598
+ case "bool":
599
+ return ["true", "false"];
600
+ default:
601
+ return null;
602
+ }
603
+ }
604
+ /** Resolve a pattern against the subject; returns the type bound by `Case(x)`. */
605
+ function checkPattern(c, pattern, subject, covered) {
606
+ if (pattern.kind === "Wildcard") {
607
+ return null;
608
+ }
609
+ if (pattern.kind === "Value") {
610
+ const t = (0, exprs_js_1.checkExpr)(c, pattern.expr, subject);
611
+ if (pattern.expr.kind === "Bool") {
612
+ covered.add(String(pattern.expr.value));
613
+ }
614
+ if (pattern.expr.res?.kind === "enumCase" && subject.k === "enum") {
615
+ const info = c.enums.get(subject.id);
616
+ const name = info?.cases[pattern.expr.res.index]?.name;
617
+ if (name) {
618
+ covered.add(name);
619
+ }
620
+ }
621
+ if ((0, typeops_js_1.assignable)(c, (0, checker_js_1.widenLiteral)(subject), t) === "no" && (0, typeops_js_1.assignable)(c, (0, checker_js_1.widenLiteral)(t), subject) === "no" && !(0, types_js_1.isDynamic)(subject)) {
622
+ c.error("CL3019", pattern.span, `pattern ${(0, types_js_1.typeToString)(t)} can never match a ${(0, types_js_1.typeToString)(subject)}`);
623
+ }
624
+ return null;
625
+ }
626
+ const name = pattern.name;
627
+ switch (subject.k) {
628
+ case "enum": {
629
+ const info = c.enums.get(subject.id);
630
+ const index = info ? info.cases.findIndex((k) => k.name === name) : -1;
631
+ if (!info || index < 0) {
632
+ return valueFallback(c, pattern, subject);
633
+ }
634
+ const kase = info.cases[index];
635
+ pattern.res = { kind: "enumCase", enumName: info.id, index, hasPayload: Boolean(kase.payload), anyPayload: info.hasPayload };
636
+ covered.add(name);
637
+ if (pattern.binding && !kase.payload) {
638
+ c.error("CL3031", pattern.span, `${info.name}.${name} carries no value to bind`);
639
+ }
640
+ return kase.payload;
641
+ }
642
+ case "option":
643
+ if (name === "Some") {
644
+ covered.add("Some");
645
+ pattern.res = { kind: "builtin", name: "Some" };
646
+ return subject.inner;
647
+ }
648
+ if (name === "None") {
649
+ covered.add("None");
650
+ pattern.res = { kind: "builtin", name: "None" };
651
+ return null;
652
+ }
653
+ return valueFallback(c, pattern, subject);
654
+ case "result":
655
+ if (name === "Ok" || name === "Err") {
656
+ covered.add(name);
657
+ pattern.res = { kind: "builtin", name };
658
+ return name === "Ok" ? subject.ok : subject.err;
659
+ }
660
+ return valueFallback(c, pattern, subject);
661
+ case "rbxEnumItem": {
662
+ const items = c.api.enums[subject.enumName]?.items || [];
663
+ if (items.includes(name)) {
664
+ pattern.res = { kind: "rbxEnumItem", enumName: subject.enumName, item: name };
665
+ return null;
666
+ }
667
+ return valueFallback(c, pattern, subject);
668
+ }
669
+ default:
670
+ if (name === "Some" || name === "None") {
671
+ if (!(0, types_js_1.isDynamic)(subject)) {
672
+ c.error("CL3032", pattern.span, `${name} only matches an Option, but the subject is ${(0, types_js_1.typeToString)(subject)}`);
673
+ }
674
+ pattern.res = { kind: "builtin", name };
675
+ return pattern.binding ? types_js_1.T.any : null;
676
+ }
677
+ return valueFallback(c, pattern, subject);
678
+ }
679
+ }
680
+ /** `case` name that is not a variant: compare against a constant/variable with that name. */
681
+ function valueFallback(c, pattern, subject) {
682
+ if (pattern.binding) {
683
+ c.error("CL3033", pattern.span, `'${pattern.name}' is not a case of ${(0, types_js_1.typeToString)(subject)}`);
684
+ return types_js_1.T.unknown;
685
+ }
686
+ const expr = pattern.qualifier
687
+ ? { kind: "Member", object: pattern.qualifier, name: pattern.name, nameSpan: pattern.span, typeArgs: null, span: pattern.span }
688
+ : { kind: "Name", name: pattern.name, typeArgs: null, span: pattern.span };
689
+ const t = (0, exprs_js_1.checkExpr)(c, expr, subject);
690
+ pattern.valueExpr = expr;
691
+ if ((0, typeops_js_1.assignable)(c, (0, checker_js_1.widenLiteral)(subject), t) === "no" && !(0, types_js_1.isDynamic)(t) && !(0, types_js_1.isDynamic)(subject)) {
692
+ const info = subject.k === "enum" ? c.enums.get(subject.id) : null;
693
+ c.error("CL3033", pattern.span, `'${pattern.name}' is not a case of ${(0, types_js_1.typeToString)(subject)}`, info ? [`cases: ${info.cases.map((k) => k.name).join(", ")}`] : undefined);
694
+ }
695
+ return null;
696
+ }
697
+ // ---------- connections ----------
698
+ function checkConnect(c, stmt) {
699
+ const signalType = (0, types_js_1.prune)((0, exprs_js_1.checkExpr)(c, stmt.signal));
700
+ if (signalType.k === "clSignal") {
701
+ const sym = (0, exprs_js_1.rootSymbol)(stmt.signal);
702
+ const expected = signalType.params ? types_js_1.T.func(signalType.params.map((p, i) => ({ name: `arg${i + 1}`, type: p, optional: false })), types_js_1.T.void) : types_js_1.T.func([], types_js_1.T.void);
703
+ const handlerType = (0, types_js_1.prune)((0, exprs_js_1.checkExpr)(c, stmt.handler, signalType.params ? expected : undefined));
704
+ if (handlerType.k !== "func" && !(0, types_js_1.isDynamic)(handlerType)) {
705
+ c.error("CL3034", stmt.handler.span, "a signal connects to a function name or `func (…) { … }`");
706
+ }
707
+ if (sym) {
708
+ (sym.handlers ||= []).push(stmt.handler);
709
+ sym.used = true;
710
+ }
711
+ stmt.target = "cl";
712
+ return;
713
+ }
714
+ if (signalType.k === "rbxSignal") {
715
+ const expected = types_js_1.T.func(signalType.args.map((a, i) => ({ name: `arg${i + 1}`, type: a, optional: false })), types_js_1.T.void);
716
+ const handlerType = (0, types_js_1.prune)((0, exprs_js_1.checkExpr)(c, stmt.handler, expected));
717
+ checkHandler(c, handlerType, signalType.args, stmt.handler.span);
718
+ stmt.target = "rbx";
719
+ return;
720
+ }
721
+ if (!(0, types_js_1.isDynamic)(signalType)) {
722
+ c.error("CL3034", stmt.signal.span, `'~>' connects a signal or a Roblox event, not ${(0, types_js_1.typeToString)(signalType)}`);
723
+ }
724
+ (0, exprs_js_1.checkExpr)(c, stmt.handler);
725
+ stmt.target = "rbx";
726
+ }
727
+ function checkHandler(c, handlerType, args, span) {
728
+ const h = (0, types_js_1.prune)(handlerType);
729
+ if (h.k !== "func") {
730
+ if (!(0, types_js_1.isDynamic)(h)) {
731
+ c.error("CL3034", span, `expected a function to handle the event, got ${(0, types_js_1.typeToString)(h)}`);
732
+ }
733
+ return;
734
+ }
735
+ if (h.params.length > args.length && !h.params.slice(args.length).every((p) => p.optional)) {
736
+ c.error("CL3035", span, `the handler takes ${h.params.length} parameter(s) but the event only passes ${args.length}`, [
737
+ `event arguments: (${args.map((a) => (0, types_js_1.typeToString)(a)).join(", ")})`,
738
+ ]);
739
+ return;
740
+ }
741
+ h.params.forEach((p, i) => {
742
+ const arg = args[i];
743
+ if (!arg) {
744
+ return;
745
+ }
746
+ const pt = (0, types_js_1.prune)(p.type);
747
+ if (pt.k === "infer" && !pt.resolved) {
748
+ pt.resolved = arg;
749
+ return;
750
+ }
751
+ const r = (0, typeops_js_1.assignable)(c, p.type, arg);
752
+ if (r === "no") {
753
+ c.error("CL3036", span, `handler parameter '${p.name}' is ${(0, types_js_1.typeToString)(p.type)}, but the event passes ${(0, types_js_1.typeToString)(arg)}`);
754
+ }
755
+ });
756
+ }
757
+ // ---------- loops that never yield ----------
758
+ function checkInfiniteLoop(c, stmt, forever, body) {
759
+ if (!forever || (0, flow_js_1.containsBreak)(body) || (0, flow_js_1.alwaysExits)(c, body) || containsReturn(body)) {
760
+ return;
761
+ }
762
+ if (!(0, yields_js_1.mayYield)(c, body)) {
763
+ c.error("CL4020", stmt.span, "this loop never ends and never yields: it freezes the game", [
764
+ "wait inside it (`task.wait()`, `RunService.Heartbeat.Wait()`) or connect to an event instead",
765
+ ]);
766
+ }
767
+ }
768
+ function containsReturn(body) {
769
+ const visit = (s) => {
770
+ switch (s.kind) {
771
+ case "Return":
772
+ return true;
773
+ case "If":
774
+ return s.then.some(visit) || (s.else ? s.else.some(visit) : false);
775
+ case "Block":
776
+ return s.body.some(visit);
777
+ case "While":
778
+ case "For":
779
+ case "ForIn":
780
+ return s.body.some(visit);
781
+ case "Try":
782
+ return s.body.some(visit) || s.handler.some(visit);
783
+ case "Match":
784
+ return s.arms.some((a) => a.body.some(visit));
785
+ case "Switch":
786
+ return s.cases.some((k) => k.body.some(visit));
787
+ default:
788
+ return false;
789
+ }
790
+ };
791
+ return body.some(visit);
792
+ }