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,763 @@
1
+ "use strict";
2
+ /** Member access: CL++ structs/modules/enums and the Roblox API (properties, methods, events, children). */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.isDynamic = void 0;
5
+ exports.checkMember = checkMember;
6
+ exports.memberOf = memberOf;
7
+ exports.dmType = dmType;
8
+ exports.checkScopeAccess = checkScopeAccess;
9
+ exports.checkAssignTarget = checkAssignTarget;
10
+ const diagnostics_js_1 = require("../diagnostics.js");
11
+ const context_js_1 = require("../roblox/context.js");
12
+ const stdlib_js_1 = require("../stdlib.js");
13
+ const types_js_1 = require("../types.js");
14
+ Object.defineProperty(exports, "isDynamic", { enumerable: true, get: function () { return types_js_1.isDynamic; } });
15
+ const exprs_js_1 = require("./exprs.js");
16
+ const typeops_js_1 = require("./typeops.js");
17
+ const VECTOR_COMPONENTS = {
18
+ Vector3: ["x", "y", "z"],
19
+ Vector2: ["x", "y"],
20
+ Vector3int16: ["x", "y", "z"],
21
+ Vector2int16: ["x", "y"],
22
+ };
23
+ const SIGNAL_METHODS = new Set(["Connect", "Once", "ConnectParallel", "Wait"]);
24
+ /** `string` library members usable with method syntax: `name.upper()` → `name:upper()`. */
25
+ const STRING_METHODS = new Set(["byte", "find", "format", "gmatch", "gsub", "len", "lower", "match", "rep", "reverse", "split", "sub", "upper"]);
26
+ function checkMember(c, e, expected, ctx = { forCall: false }) {
27
+ // observable: `coins.OnChange(f)`
28
+ if (e.name === "OnChange" && e.object.kind === "Name") {
29
+ const sym = c.scope.lookup(e.object.name);
30
+ if (sym && sym.observable) {
31
+ sym.used = true;
32
+ e.object.res = { kind: "local", sym };
33
+ e.object.ty = sym.type;
34
+ e.res = { kind: "builtin", name: "OnChange" };
35
+ return types_js_1.T.func([{ name: "handler", type: types_js_1.T.func([{ name: "value", type: sym.type, optional: false }], types_js_1.T.void), optional: false }], types_js_1.T.void);
36
+ }
37
+ }
38
+ const objType = (0, types_js_1.prune)((0, exprs_js_1.checkExpr)(c, e.object));
39
+ return memberOf(c, e, objType, expected, ctx);
40
+ }
41
+ function memberOf(c, e, objType, expected, ctx) {
42
+ const name = e.name;
43
+ switch (objType.k) {
44
+ case "namespace":
45
+ return namespaceMember(c, e, objType.ns, ctx);
46
+ case "rbxEnums": {
47
+ if (name === "GetEnums") {
48
+ e.res = { kind: "rbxMethod", owner: "Enums" };
49
+ return types_js_1.T.func([], types_js_1.T.list({ k: "rbxEnum", enumName: "*" }));
50
+ }
51
+ if (!c.api.hasEnum(name)) {
52
+ const s = (0, diagnostics_js_1.suggest)(name, Object.keys(c.api.enums));
53
+ c.error("RBX5001", e.nameSpan, `Enum.${name} is not a Roblox enum`, s ? [`did you mean Enum.${s}?`] : undefined);
54
+ return types_js_1.T.unknown;
55
+ }
56
+ e.res = { kind: "rbxEnum", enumName: name };
57
+ return { k: "rbxEnum", enumName: name };
58
+ }
59
+ case "rbxEnum": {
60
+ if (name === "GetEnumItems" || name === "FromName" || name === "FromValue") {
61
+ requireCall(c, e, ctx);
62
+ e.res = { kind: "rbxMethod", owner: "Enum" };
63
+ const item = { k: "rbxEnumItem", enumName: objType.enumName };
64
+ if (name === "GetEnumItems") {
65
+ return types_js_1.T.func([], types_js_1.T.list(item));
66
+ }
67
+ return types_js_1.T.func([{ name: name === "FromName" ? "name" : "value", type: name === "FromName" ? types_js_1.T.string : types_js_1.T.int, optional: false }], types_js_1.T.option(item));
68
+ }
69
+ const en = c.api.enums[objType.enumName];
70
+ if (!en || !en.items.includes(name)) {
71
+ const s = en ? (0, diagnostics_js_1.suggest)(name, en.items) : null;
72
+ c.error("RBX5003", e.nameSpan, `Enum.${objType.enumName} has no item '${name}'`, s ? [`did you mean Enum.${objType.enumName}.${s}?`] : en ? [`items: ${en.items.slice(0, 12).join(", ")}${en.items.length > 12 ? ", …" : ""}`] : undefined);
73
+ return types_js_1.T.unknown;
74
+ }
75
+ if (en.deprecated?.includes(name)) {
76
+ c.warn("RBX5010", e.nameSpan, `Enum.${objType.enumName}.${name} is deprecated`);
77
+ }
78
+ e.res = { kind: "rbxEnumItem", enumName: objType.enumName, item: name };
79
+ return { k: "rbxEnumItem", enumName: objType.enumName };
80
+ }
81
+ case "rbxEnumItem":
82
+ switch (name) {
83
+ case "Name":
84
+ e.res = { kind: "rbxProperty", owner: "EnumItem" };
85
+ return types_js_1.T.string;
86
+ case "Value":
87
+ e.res = { kind: "rbxProperty", owner: "EnumItem" };
88
+ return types_js_1.T.int;
89
+ case "EnumType":
90
+ e.res = { kind: "rbxProperty", owner: "EnumItem" };
91
+ return { k: "rbxEnum", enumName: objType.enumName };
92
+ case "IsA":
93
+ requireCall(c, e, ctx);
94
+ e.res = { kind: "rbxMethod", owner: "EnumItem" };
95
+ return types_js_1.T.func([{ name: "enumName", type: types_js_1.T.string, optional: false }], types_js_1.T.bool);
96
+ default:
97
+ c.error("RBX5004", e.nameSpan, `an EnumItem has Name, Value and EnumType, not '${name}'`);
98
+ return types_js_1.T.unknown;
99
+ }
100
+ case "instance":
101
+ return instanceMember(c, e, objType, ctx);
102
+ case "datatype":
103
+ return datatypeMember(c, e, objType.name, ctx);
104
+ case "rbxSignal": {
105
+ if (!SIGNAL_METHODS.has(name)) {
106
+ const s = (0, diagnostics_js_1.suggest)(name, [...SIGNAL_METHODS]);
107
+ c.error("RBX5005", e.nameSpan, `an event has Connect, Once, ConnectParallel and Wait, not '${name}'`, s ? [`did you mean '${s}'?`] : undefined);
108
+ return types_js_1.T.unknown;
109
+ }
110
+ requireCall(c, e, ctx);
111
+ e.res = { kind: "datatypeMember", datatype: "RBXScriptSignal", member: name, isMethod: true };
112
+ const handler = types_js_1.T.func(objType.args.map((a, i) => ({ name: `arg${i + 1}`, type: a, optional: false })), types_js_1.T.void);
113
+ if (name === "Wait") {
114
+ c.noteYield();
115
+ return types_js_1.T.func([], objType.args.length === 0 ? types_js_1.T.void : objType.args.length === 1 ? objType.args[0] : { k: "tuple", elems: objType.args });
116
+ }
117
+ if (name === "ConnectParallel") {
118
+ c.warn("PAR7001", e.nameSpan, "ConnectParallel handlers run in parallel: they cannot write Instance properties", ["only works in a script parented under an Actor"]);
119
+ }
120
+ return types_js_1.T.func([{ name: "handler", type: handler, optional: false }], types_js_1.T.datatype("RBXScriptConnection"));
121
+ }
122
+ case "struct":
123
+ return structMember(c, e, objType, ctx);
124
+ case "string":
125
+ case "lit": {
126
+ if (objType.k === "lit" && typeof objType.value !== "string") {
127
+ break;
128
+ }
129
+ if (STRING_METHODS.has(name)) {
130
+ requireCall(c, e, ctx);
131
+ c.requireRoblox(e.nameSpan, `string.${name}`);
132
+ const fn = (0, types_js_1.prune)(c.api.libraryFunction("string", name) ?? types_js_1.T.any);
133
+ e.res = { kind: "library", lib: "string", member: name, stringMethod: true };
134
+ if (fn.k === "func") {
135
+ return { ...fn, params: fn.params.slice(1) };
136
+ }
137
+ return types_js_1.T.any;
138
+ }
139
+ c.error("CL3090", e.nameSpan, `text has no member '${name}'`, name === "length" || name === "size" ? ["use len(text)"] : ["link @clpp.text and use Text." + name + "(…)"]);
140
+ return types_js_1.T.unknown;
141
+ }
142
+ case "list":
143
+ c.error("CL3091", e.nameSpan, `lists have no member '${name}'`, name === "length" || name === "size" || name === "Length" ? ["use len(list)"] : ["use push/pop/insert/remove/find/sort"]);
144
+ return types_js_1.T.unknown;
145
+ case "dict":
146
+ c.error("CL3092", e.nameSpan, `dictionaries are read with [key], not .${name}`, [`write d["${name}"]`]);
147
+ return types_js_1.T.unknown;
148
+ case "table": {
149
+ const f = objType.fields[name];
150
+ if (f) {
151
+ e.res = { kind: "dynamic" };
152
+ return f;
153
+ }
154
+ c.error("CL3093", e.nameSpan, `this table has no field '${name}'`, [`fields: ${Object.keys(objType.fields).join(", ")}`]);
155
+ return types_js_1.T.unknown;
156
+ }
157
+ case "option": {
158
+ c.strictError("CL3011", e.object.span, `${(0, types_js_1.typeToString)(objType)} may be null here`, [
159
+ `check it first: \`if (${describe(e.object)} != null) { … }\``,
160
+ ]);
161
+ const inner = memberOf(c, e, (0, types_js_1.prune)(objType.inner), expected, ctx);
162
+ return inner;
163
+ }
164
+ case "null":
165
+ c.error("CL3011", e.object.span, "this value is always null");
166
+ return types_js_1.T.unknown;
167
+ case "any":
168
+ case "unknown":
169
+ case "infer":
170
+ e.res = { kind: "dynamic" };
171
+ return types_js_1.T.any;
172
+ case "task":
173
+ c.error("CL3094", e.nameSpan, "a task has no members; get its result with `await`");
174
+ return types_js_1.T.unknown;
175
+ default:
176
+ break;
177
+ }
178
+ c.error("CL3095", e.nameSpan, `${(0, types_js_1.typeToString)(objType)} has no member '${name}'`);
179
+ return types_js_1.T.unknown;
180
+ }
181
+ function describe(e) {
182
+ switch (e.kind) {
183
+ case "Name":
184
+ return e.name;
185
+ case "Member":
186
+ return `${describe(e.object)}.${e.name}`;
187
+ case "SelfField":
188
+ return `@${e.name}`;
189
+ default:
190
+ return "value";
191
+ }
192
+ }
193
+ function requireCall(c, e, ctx) {
194
+ if (!ctx.forCall) {
195
+ c.error("CL3096", e.nameSpan, `'${e.name}' is a method; call it: ${describe(e)}(…)`, ["to pass it around, wrap it: `func () { " + describe(e) + "(); }`"]);
196
+ }
197
+ }
198
+ // ---------- namespaces ----------
199
+ function namespaceMember(c, e, ns, ctx) {
200
+ const name = e.name;
201
+ switch (ns.kind) {
202
+ case "module":
203
+ return moduleMember(c, e, ns.alias, ns.moduleId);
204
+ case "luauModule":
205
+ e.res = { kind: "moduleMember", alias: ns.alias, moduleId: ns.alias, member: name, memberKind: "value" };
206
+ return types_js_1.T.any;
207
+ case "library": {
208
+ const lib = c.api.library(ns.name);
209
+ const member = lib?.[name];
210
+ if (!member) {
211
+ if (ns.name === "task" && name === "defer") {
212
+ // CL++ core task.defer is the Roblox one
213
+ }
214
+ const s = lib ? (0, diagnostics_js_1.suggest)(name, Object.keys(lib)) : null;
215
+ c.error("RBX5006", e.nameSpan, `${ns.name}.${name} does not exist`, s ? [`did you mean ${ns.name}.${s}?`] : undefined);
216
+ return types_js_1.T.unknown;
217
+ }
218
+ if (member.deprecated) {
219
+ c.warn("RBX5010", e.nameSpan, `${ns.name}.${name} is deprecated`);
220
+ }
221
+ e.res = { kind: "library", lib: ns.name, member: name };
222
+ if (ns.name === "task" && name === "wait") {
223
+ c.noteYield();
224
+ }
225
+ if (ns.name === "coroutine" && name === "yield") {
226
+ c.noteYield();
227
+ }
228
+ return c.api.libraryFunction(ns.name, name) ?? types_js_1.T.any;
229
+ }
230
+ case "datatype": {
231
+ const st = c.api.datatypeStatic(ns.name, name);
232
+ if (!st) {
233
+ const statics = c.api.datatypes[ns.name]?.statics ?? {};
234
+ const s = (0, diagnostics_js_1.suggest)(name, Object.keys(statics));
235
+ c.error("RBX5007", e.nameSpan, `${ns.name}.${name} does not exist`, s ? [`did you mean ${ns.name}.${s}?`] : [`available: ${Object.keys(statics).join(", ")}`]);
236
+ return types_js_1.T.unknown;
237
+ }
238
+ if (st.deprecated) {
239
+ c.warn("RBX5010", e.nameSpan, `${ns.name}.${name} is deprecated`);
240
+ }
241
+ if (ns.name !== "Vector3" && ns.name !== "Vector2") {
242
+ c.requireRoblox(e.span, ns.name);
243
+ }
244
+ e.res = { kind: "datatypeStatic", datatype: ns.name, member: name };
245
+ return c.api.staticFunctionType(st);
246
+ }
247
+ case "instanceStatic":
248
+ if (name === "new" || name === "fromExisting") {
249
+ e.res = { kind: "datatypeStatic", datatype: "Instance", member: name };
250
+ return name === "new"
251
+ ? types_js_1.T.func([{ name: "className", type: types_js_1.T.string, optional: false }, { name: "parent", type: types_js_1.T.instance("Instance"), optional: true }], types_js_1.T.instance("Instance"))
252
+ : types_js_1.T.func([{ name: "existing", type: types_js_1.T.instance("Instance"), optional: false }], types_js_1.T.instance("Instance"));
253
+ }
254
+ c.error("RBX5007", e.nameSpan, `Instance.${name} does not exist`, ["Instance.new(className) or Instance.fromExisting(instance)"]);
255
+ return types_js_1.T.unknown;
256
+ case "struct": {
257
+ const info = c.structs.get(ns.id);
258
+ const m = info?.methods.get(name);
259
+ if (m && m.isStatic) {
260
+ e.res = { kind: "method", struct: ns.id, name, isStatic: true };
261
+ return m.type;
262
+ }
263
+ if (m) {
264
+ c.error("CL3097", e.nameSpan, `'${name}' is an instance method; call it on a ${ns.name} value`);
265
+ return m.type;
266
+ }
267
+ const s = info ? (0, diagnostics_js_1.suggest)(name, [...info.methods.keys()]) : null;
268
+ c.error("CL3098", e.nameSpan, `struct ${ns.name} has no static member '${name}'`, s ? [`did you mean '${s}'?`] : undefined);
269
+ return types_js_1.T.unknown;
270
+ }
271
+ case "enum": {
272
+ const info = c.enums.get(ns.id);
273
+ if (!info) {
274
+ return types_js_1.T.unknown;
275
+ }
276
+ const index = info.cases.findIndex((k) => k.name === name);
277
+ if (index < 0) {
278
+ const s = (0, diagnostics_js_1.suggest)(name, info.cases.map((k) => k.name));
279
+ c.error("CL3099", e.nameSpan, `enum ${info.name} has no case '${name}'`, s ? [`did you mean ${info.name}.${s}?`] : [`cases: ${info.cases.map((k) => k.name).join(", ")}`]);
280
+ return types_js_1.T.unknown;
281
+ }
282
+ const kase = info.cases[index];
283
+ e.res = { kind: "enumCase", enumName: info.id, index, hasPayload: Boolean(kase.payload), anyPayload: info.hasPayload };
284
+ const enumType = { k: "enum", id: info.id, name: info.name };
285
+ if (kase.payload) {
286
+ if (!ctx.forCall) {
287
+ c.error("CL3100", e.nameSpan, `${info.name}.${name} carries a ${(0, types_js_1.typeToString)(kase.payload)}; build it with ${info.name}.${name}(value)`);
288
+ }
289
+ return types_js_1.T.func([{ name: "value", type: kase.payload, optional: false }], enumType);
290
+ }
291
+ return enumType;
292
+ }
293
+ case "clNamespace": {
294
+ const funcs = ns.moduleId === c.input.moduleId ? c.exports.namespaces.get(ns.name) : undefined;
295
+ const f = funcs?.get(name);
296
+ if (!f) {
297
+ const s = funcs ? (0, diagnostics_js_1.suggest)(name, [...funcs.keys()]) : null;
298
+ c.error("CL2022", e.nameSpan, `namespace ${ns.name} has no function '${name}'`, s ? [`did you mean '${s}'?`] : undefined);
299
+ return types_js_1.T.unknown;
300
+ }
301
+ e.res = { kind: "moduleMember", alias: ns.name, moduleId: "namespace", member: name, memberKind: "func" };
302
+ return f.type;
303
+ }
304
+ case "stdlib": {
305
+ const fn = (0, stdlib_js_1.stdModule)(ns.module).get(name);
306
+ if (!fn) {
307
+ const s = (0, diagnostics_js_1.suggest)(name, [...(0, stdlib_js_1.stdModule)(ns.module).keys()]);
308
+ c.error("CL2023", e.nameSpan, `${ns.alias}.${name} does not exist`, s ? [`did you mean ${ns.alias}.${s}?`] : undefined);
309
+ return types_js_1.T.unknown;
310
+ }
311
+ e.res = { kind: "stdlib", module: ns.module, member: name };
312
+ return (0, stdlib_js_1.stdFunctionType)(fn);
313
+ }
314
+ default:
315
+ return types_js_1.T.unknown;
316
+ }
317
+ }
318
+ function moduleMember(c, e, alias, moduleId) {
319
+ const sym = c.scope.lookup(alias);
320
+ const target = sym?.link;
321
+ const exports = target && (target.kind === "clp" ? target.exports : target.kind === "luau" ? target.exports : undefined);
322
+ if (!exports) {
323
+ e.res = { kind: "moduleMember", alias, moduleId, member: e.name, memberKind: "value" };
324
+ return types_js_1.T.any;
325
+ }
326
+ const name = e.name;
327
+ const f = exports.funcs.get(name);
328
+ if (f) {
329
+ e.res = { kind: "moduleMember", alias, moduleId, member: name, memberKind: "func" };
330
+ if (f.contextOnly) {
331
+ c.checkContext(f.contextOnly, e.nameSpan, `${alias}.${name}`, `it is marked [[${f.contextOnly}]]`);
332
+ }
333
+ return f.type;
334
+ }
335
+ const k = exports.consts.get(name);
336
+ if (k) {
337
+ e.res = { kind: "const", value: k.value };
338
+ return k.type;
339
+ }
340
+ const s = exports.structs.get(name);
341
+ if (s) {
342
+ e.res = { kind: "moduleMember", alias, moduleId, member: name, memberKind: "struct" };
343
+ return { k: "namespace", ns: { kind: "struct", id: s.id, name: s.name } };
344
+ }
345
+ const en = exports.enums.get(name);
346
+ if (en) {
347
+ e.res = { kind: "moduleMember", alias, moduleId, member: name, memberKind: "enum" };
348
+ return { k: "namespace", ns: { kind: "enum", id: en.id, name: en.name } };
349
+ }
350
+ const nsFuncs = exports.namespaces.get(name);
351
+ if (nsFuncs) {
352
+ e.res = { kind: "moduleMember", alias, moduleId, member: name, memberKind: "value" };
353
+ return { k: "namespace", ns: { kind: "clNamespace", name, moduleId: exports.moduleId } };
354
+ }
355
+ const v = exports.variants.get(name);
356
+ if (v) {
357
+ e.res = { kind: "moduleMember", alias, moduleId, member: name, memberKind: "func" };
358
+ return types_js_1.T.func([{ name: "value", type: { k: "union", members: v.alternatives }, optional: false }], { k: "variant", id: v.id, name: v.name, alternatives: v.alternatives });
359
+ }
360
+ const names = [...exports.funcs.keys(), ...exports.consts.keys(), ...exports.structs.keys(), ...exports.enums.keys()];
361
+ const hint = (0, diagnostics_js_1.suggest)(name, names);
362
+ c.error("CL2024", e.nameSpan, `module ${alias} has no member '${name}'`, hint ? [`did you mean ${alias}.${hint}?`] : names.length ? [`it exports: ${names.slice(0, 10).join(", ")}${names.length > 10 ? ", …" : ""}`] : ["it exports nothing"]);
363
+ return types_js_1.T.unknown;
364
+ }
365
+ // ---------- Roblox instances ----------
366
+ function memberRuleFor(c, cls, member) {
367
+ let cur = cls;
368
+ let guard = 0;
369
+ while (cur && guard++ < 64) {
370
+ const rule = context_js_1.MEMBER_RULES[`${cur}.${member}`];
371
+ if (rule) {
372
+ return rule;
373
+ }
374
+ cur = c.api.classes[cur]?.superclass ?? null;
375
+ }
376
+ return undefined;
377
+ }
378
+ function checkSecurity(c, e, owner, member, write) {
379
+ const level = write ? member.writeSecurity : member.security;
380
+ if (member.notScriptable) {
381
+ c.error("RBX5040", e.nameSpan, `${owner}.${e.name} is not scriptable`);
382
+ return;
383
+ }
384
+ if (!level) {
385
+ return;
386
+ }
387
+ if (level === "PluginSecurity") {
388
+ if (c.input.context !== "plugin") {
389
+ c.error("RBX5041", e.nameSpan, `${owner}.${e.name} needs plugin security; game scripts cannot ${write ? "set" : "use"} it`);
390
+ }
391
+ return;
392
+ }
393
+ c.error("RBX5042", e.nameSpan, `${owner}.${e.name} is restricted (${level}); game scripts cannot ${write ? "set" : "use"} it`);
394
+ }
395
+ function deprecation(c, e, owner, member) {
396
+ if (!member.deprecated) {
397
+ return;
398
+ }
399
+ const use = typeof member.deprecated === "string" ? member.deprecated.replace(/^.*?:/, "") : null;
400
+ c.warn("RBX5010", e.nameSpan, `${owner}.${e.name} is deprecated${use ? `; use ${use}` : ""}`);
401
+ }
402
+ function instanceMember(c, e, obj, ctx) {
403
+ const name = e.name;
404
+ const api = c.api;
405
+ // game.Players → GetService("Players")
406
+ if (obj.cls === "DataModel" && api.isService(name) && !api.member("DataModel", name)) {
407
+ c.useService(name, e.nameSpan);
408
+ e.res = { kind: "service", service: name };
409
+ const dm = obj.dm?.children.get(name);
410
+ return { k: "instance", cls: name, ...(dm ? { dm } : {}) };
411
+ }
412
+ const hit = api.member(obj.cls, name);
413
+ if (hit) {
414
+ const m = hit.member;
415
+ deprecation(c, e, hit.owner, m);
416
+ checkSecurity(c, e, hit.owner, m, false);
417
+ const rule = memberRuleFor(c, obj.cls, name);
418
+ if (rule) {
419
+ c.checkContext(rule.only, e.nameSpan, `${obj.cls}.${name}`, rule.why, rule.severity);
420
+ }
421
+ const risky = context_js_1.RISKY_MEMBERS[`${hit.owner}.${name}`];
422
+ if (risky) {
423
+ c.warn("RBX5043", e.nameSpan, `${hit.owner}.${name}: ${risky}`);
424
+ }
425
+ switch (m.kind) {
426
+ case "property": {
427
+ e.res = { kind: "rbxProperty", owner: hit.owner };
428
+ const t = m.type ? api.convert(m.type) : types_js_1.T.any;
429
+ if (c.fn?.isParallel) {
430
+ // reading is fine in parallel; writes are checked in checkAssignTarget
431
+ }
432
+ return t;
433
+ }
434
+ case "method":
435
+ requireCall(c, e, ctx);
436
+ e.res = { kind: "rbxMethod", owner: hit.owner };
437
+ if (m.yields || name === "WaitForChild" || /Async$/.test(name)) {
438
+ c.noteYield();
439
+ }
440
+ return api.methodType(m);
441
+ case "event":
442
+ e.res = { kind: "rbxEvent", owner: hit.owner };
443
+ return { k: "rbxSignal", args: (m.args || []).map((a) => api.convert(a)) };
444
+ case "callback":
445
+ e.res = { kind: "rbxProperty", owner: hit.owner };
446
+ return m.type ? api.convert(m.type) : types_js_1.T.any;
447
+ default:
448
+ return types_js_1.T.any;
449
+ }
450
+ }
451
+ // Typed DataModel: children known from the project layout.
452
+ const child = obj.dm?.children.get(name);
453
+ if (child) {
454
+ e.res = { kind: "rbxChild", owner: obj.cls, childName: name, safe: true };
455
+ return dmType(child);
456
+ }
457
+ const members = [...api.members(obj.cls).keys()];
458
+ const typo = (0, diagnostics_js_1.suggest)(name, members);
459
+ if (typo) {
460
+ c.error("RBX5020", e.nameSpan, `${obj.cls} has no member '${name}'`, [`did you mean '${typo}'?`]);
461
+ return types_js_1.T.unknown;
462
+ }
463
+ if (/^[a-z]/.test(name) && !obj.dm) {
464
+ c.error("RBX5020", e.nameSpan, `${obj.cls} has no member '${name}'`, ["Roblox members are PascalCase"]);
465
+ return types_js_1.T.unknown;
466
+ }
467
+ const known = obj.dm ? [...obj.dm.children.keys()] : [];
468
+ const notes = [
469
+ `if '${name}' is a child Instance, prefer ${describe(e.object)}.WaitForChild("${name}") (it may not exist yet) or FindFirstChild`,
470
+ ];
471
+ if (known.length) {
472
+ notes.unshift(`known children: ${known.slice(0, 8).join(", ")}${known.length > 8 ? ", …" : ""}`);
473
+ }
474
+ c.strictError("RBX5021", e.nameSpan, `'${name}' is not a property of ${obj.cls}; it is read as a child Instance`, notes);
475
+ e.res = { kind: "rbxChild", owner: obj.cls, childName: name, safe: false };
476
+ return types_js_1.T.instance("Instance");
477
+ }
478
+ function dmType(node) {
479
+ return { k: "instance", cls: node.className, dm: node };
480
+ }
481
+ function datatypeMember(c, e, name, ctx) {
482
+ const member = e.name;
483
+ const components = VECTOR_COMPONENTS[name];
484
+ if (components && components.includes(member)) {
485
+ e.res = { kind: "vectorComponent", component: member.toUpperCase() };
486
+ return types_js_1.T.float;
487
+ }
488
+ if (name === "RBXScriptConnection") {
489
+ if (member === "Disconnect") {
490
+ requireCall(c, e, ctx);
491
+ e.res = { kind: "datatypeMember", datatype: name, member, isMethod: true };
492
+ return types_js_1.T.func([], types_js_1.T.void);
493
+ }
494
+ if (member === "Connected") {
495
+ e.res = { kind: "datatypeMember", datatype: name, member, isMethod: false };
496
+ return types_js_1.T.bool;
497
+ }
498
+ }
499
+ const m = c.api.datatypeMember(name, member);
500
+ if (!m) {
501
+ const dt = c.api.datatypes[name];
502
+ const s = dt ? (0, diagnostics_js_1.suggest)(member, Object.keys(dt.members)) : null;
503
+ c.error("RBX5022", e.nameSpan, `${name} has no member '${member}'`, s ? [`did you mean '${s}'?`] : components ? [`components: ${components.join(", ")} (or ${components.map((x) => x.toUpperCase()).join(", ")})`] : undefined);
504
+ return types_js_1.T.unknown;
505
+ }
506
+ if (m.deprecated) {
507
+ c.warn("RBX5010", e.nameSpan, `${name}.${member} is deprecated`);
508
+ }
509
+ if (m.kind === "method") {
510
+ requireCall(c, e, ctx);
511
+ e.res = { kind: "datatypeMember", datatype: name, member, isMethod: true };
512
+ return c.api.methodType(m);
513
+ }
514
+ if (components && /^[XYZ]$/.test(member)) {
515
+ e.res = { kind: "vectorComponent", component: member };
516
+ return types_js_1.T.float;
517
+ }
518
+ e.res = { kind: "datatypeMember", datatype: name, member, isMethod: false };
519
+ return m.type ? c.api.convert(m.type) : types_js_1.T.any;
520
+ }
521
+ // ---------- CL++ structs ----------
522
+ function structMember(c, e, obj, ctx) {
523
+ const info = c.structs.get(obj.id);
524
+ if (!info) {
525
+ return types_js_1.T.unknown;
526
+ }
527
+ const bindings = new Map();
528
+ info.typeParams.forEach((p, i) => {
529
+ const arg = obj.args[i];
530
+ if (arg && !((0, types_js_1.prune)(arg).k === "param" && (0, types_js_1.prune)(arg).name === p)) {
531
+ bindings.set(p, arg);
532
+ }
533
+ });
534
+ const field = info.fields.find((f) => f.name === e.name);
535
+ if (field) {
536
+ if (field.isPrivate && !canSeePrivate(c, info)) {
537
+ c.error("CL2041", e.nameSpan, `'${e.name}' is a private member of ${field.owner}`, ["only methods of the struct can use it"]);
538
+ }
539
+ e.res = { kind: "field", struct: info.id };
540
+ return (0, typeops_js_1.substitute)(field.type, bindings);
541
+ }
542
+ const method = info.methods.get(e.name);
543
+ if (method) {
544
+ if (method.isPrivate && !canSeePrivate(c, info)) {
545
+ c.error("CL2041", e.nameSpan, `'${e.name}' is a private method of ${method.owner}`);
546
+ }
547
+ requireCall(c, e, ctx);
548
+ e.res = { kind: "method", struct: info.id, name: e.name, isStatic: method.isStatic };
549
+ return (0, typeops_js_1.substitute)(method.type, bindings);
550
+ }
551
+ const s = (0, diagnostics_js_1.suggest)(e.name, [...info.fields.map((f) => f.name), ...info.methods.keys()]);
552
+ c.error("CL2006", e.nameSpan, `${info.name} has no field or method '${e.name}'`, s ? [`did you mean '${s}'?`] : [`fields: ${info.fields.map((f) => f.name).join(", ") || "none"}`]);
553
+ return types_js_1.T.unknown;
554
+ }
555
+ function canSeePrivate(c, info) {
556
+ const here = c.fn?.structId;
557
+ if (!here) {
558
+ return false;
559
+ }
560
+ if (here === info.id) {
561
+ return true;
562
+ }
563
+ return info.fields.some((f) => f.owner === c.structs.get(here)?.name);
564
+ }
565
+ // ---------- a::b ----------
566
+ const NATIVE_RENAMES = {
567
+ buffer: { size: "len", write_string: "writestring", read_string: "readstring", from_string: "fromstring", to_string: "tostring" },
568
+ string: { trim: "__trim", upper: "upper", lower: "lower", starts_with: "__startsWith", ends_with: "__endsWith" },
569
+ };
570
+ function checkScopeAccess(c, e) {
571
+ const objType = (0, types_js_1.prune)((0, exprs_js_1.checkExpr)(c, e.object));
572
+ if (objType.k === "namespace") {
573
+ let name = e.name;
574
+ if (objType.ns.kind === "library") {
575
+ const renamed = NATIVE_RENAMES[objType.ns.name]?.[name] ?? name.replace(/_/g, "");
576
+ if (renamed.startsWith("__")) {
577
+ const text = (0, stdlib_js_1.stdModule)("text");
578
+ const fn = text.get(renamed.slice(2));
579
+ if (fn) {
580
+ e.res = { kind: "stdlib", module: "text", member: fn.name };
581
+ return (0, stdlib_js_1.stdFunctionType)(fn);
582
+ }
583
+ }
584
+ name = renamed;
585
+ }
586
+ const fake = { kind: "Member", object: e.object, name, nameSpan: e.nameSpan, typeArgs: null, span: e.span };
587
+ const t = namespaceMember(c, fake, objType.ns, { forCall: true });
588
+ e.res = fake.res;
589
+ return t;
590
+ }
591
+ // explicit Luau method call on a value: obj::Method(args) → obj:Method(args)
592
+ e.res = { kind: "luauMethod" };
593
+ if (objType.k === "instance") {
594
+ const hit = c.api.member(objType.cls, e.name);
595
+ if (hit && hit.member.kind === "method") {
596
+ return c.api.methodType(hit.member);
597
+ }
598
+ }
599
+ if (objType.k === "datatype") {
600
+ const m = c.api.datatypeMember(objType.name, e.name);
601
+ if (m && m.kind === "method") {
602
+ return c.api.methodType(m);
603
+ }
604
+ }
605
+ return types_js_1.T.any;
606
+ }
607
+ function checkAssignTarget(c, target, compound) {
608
+ switch (target.kind) {
609
+ case "Name": {
610
+ const sym = c.scope.lookup(target.name);
611
+ if (!sym) {
612
+ const t = (0, exprs_js_1.checkExpr)(c, target);
613
+ return { type: t };
614
+ }
615
+ sym.used = sym.used || compound;
616
+ if (c.fn && sym.depth < c.fn.depth) {
617
+ sym.captured = true;
618
+ }
619
+ target.res = { kind: "local", sym };
620
+ target.ty = sym.type;
621
+ switch (sym.kind) {
622
+ case "var":
623
+ case "param":
624
+ if (!sym.mutable) {
625
+ c.diags.error("CL2040", target.span, `cannot assign to '${sym.name}': it is declared with let`, ["declare it with `let mut` to change it"], {
626
+ span: sym.span,
627
+ text: `mut ${sym.name}`,
628
+ title: `Make '${sym.name}' mutable (let mut)`,
629
+ });
630
+ }
631
+ if (sym.atomic) {
632
+ c.error("CL2042", target.span, `'${sym.name}' is atomic; change it with fetch_add(${sym.name}, n)`);
633
+ }
634
+ sym.assigned = true;
635
+ return { type: sym.type };
636
+ case "self":
637
+ c.error("CL2043", target.span, "cannot replace self; assign its fields instead (`@hp = …`)");
638
+ return { type: sym.type };
639
+ case "const":
640
+ c.error("CL2044", target.span, `'${sym.name}' is a constant`);
641
+ return { type: sym.type };
642
+ default:
643
+ c.error("CL2045", target.span, `cannot assign to ${sym.kind} '${sym.name}'`);
644
+ return { type: types_js_1.T.unknown };
645
+ }
646
+ }
647
+ case "SelfField": {
648
+ const t = (0, exprs_js_1.checkExpr)(c, target);
649
+ if (target.res?.kind === "field" && c.fn) {
650
+ c.fn.mutatesSelf = true;
651
+ }
652
+ else if (target.res?.kind === "method") {
653
+ c.error("CL2045", target.span, `cannot assign to method '${target.name}'`);
654
+ }
655
+ else if (target.res?.kind === "dataModel") {
656
+ c.error("CL2045", target.span, "cannot replace @game");
657
+ }
658
+ return { type: t };
659
+ }
660
+ case "Member": {
661
+ const objType = (0, types_js_1.prune)((0, exprs_js_1.checkExpr)(c, target.object));
662
+ const t = memberOf(c, target, objType, undefined, { forCall: false });
663
+ target.ty = t;
664
+ const res = target.res;
665
+ if (!res) {
666
+ return { type: t };
667
+ }
668
+ switch (res.kind) {
669
+ case "field": {
670
+ requireMutableRoot(c, target);
671
+ return { type: t };
672
+ }
673
+ case "rbxProperty": {
674
+ const owner = res.owner;
675
+ if (objType.k === "instance") {
676
+ const hit = c.api.member(objType.cls, target.name);
677
+ if (hit) {
678
+ if (hit.member.readonly) {
679
+ c.error("RBX5030", target.nameSpan, `${hit.owner}.${target.name} is read-only`);
680
+ }
681
+ checkSecurity(c, target, hit.owner, hit.member, true);
682
+ if (c.fn?.isParallel) {
683
+ c.error("PAR7002", target.span, `[[parallel]] code cannot write ${hit.owner}.${target.name}`, ["write Instance properties after task.synchronize()"]);
684
+ }
685
+ }
686
+ }
687
+ else {
688
+ c.error("RBX5030", target.nameSpan, `${owner}.${target.name} is read-only`);
689
+ }
690
+ return { type: t };
691
+ }
692
+ case "vectorComponent": {
693
+ const inner = checkAssignTarget(c, target.object, true);
694
+ void inner;
695
+ return { type: types_js_1.T.float };
696
+ }
697
+ case "datatypeMember":
698
+ c.error("RBX5031", target.nameSpan, `${(0, types_js_1.typeToString)(objType)} values are immutable; build a new one`, objType.k === "datatype" && objType.name === "CFrame" ? ["e.g. part.CFrame = CFrame.new(position) * rotation"] : undefined);
699
+ return { type: t };
700
+ case "rbxChild":
701
+ c.error("RBX5032", target.nameSpan, `cannot assign to the child '${target.name}'; set its Parent instead`);
702
+ return { type: t };
703
+ case "service":
704
+ c.error("RBX5032", target.nameSpan, "services cannot be replaced");
705
+ return { type: t };
706
+ case "rbxEvent":
707
+ case "rbxMethod":
708
+ c.error("RBX5033", target.nameSpan, `cannot assign to ${target.name}; connect to an event with \`${describe(target)} ~> handler;\``);
709
+ return { type: t };
710
+ case "moduleMember":
711
+ case "const":
712
+ case "enumCase":
713
+ case "rbxEnumItem":
714
+ case "library":
715
+ case "datatypeStatic":
716
+ case "stdlib":
717
+ c.error("CL2045", target.nameSpan, `cannot assign to ${describe(target)}`);
718
+ return { type: t };
719
+ default:
720
+ return { type: t };
721
+ }
722
+ }
723
+ case "Index": {
724
+ const objType = (0, types_js_1.prune)((0, exprs_js_1.checkExpr)(c, target.object));
725
+ if (objType.k === "string" || objType.k === "lit") {
726
+ c.error("CL2046", target.span, "text is immutable; build a new string");
727
+ }
728
+ else if (objType.k === "list" || objType.k === "dict") {
729
+ requireMutableRoot(c, target);
730
+ }
731
+ const t = (0, exprs_js_1.checkExpr)(c, target);
732
+ return { type: t };
733
+ }
734
+ default:
735
+ c.error("CL1108", target.span, "cannot assign to this expression");
736
+ return { type: types_js_1.T.unknown };
737
+ }
738
+ }
739
+ /** Fields/elements of a `let` binding are frozen; `self` fields mark the method as mutating. */
740
+ function requireMutableRoot(c, target) {
741
+ const root = (0, exprs_js_1.rootSymbol)(target);
742
+ if (!root) {
743
+ return;
744
+ }
745
+ if (root.kind === "self") {
746
+ if (c.fn) {
747
+ c.fn.mutatesSelf = true;
748
+ }
749
+ return;
750
+ }
751
+ if ((root.kind === "var" || root.kind === "param") && !root.mutable) {
752
+ c.diags.error("CL2040", target.span, `cannot change '${describe(target)}': '${root.name}' is declared with let`, ["declare it with `let mut`"], {
753
+ span: root.span,
754
+ text: `mut ${root.name}`,
755
+ title: `Make '${root.name}' mutable (let mut)`,
756
+ });
757
+ }
758
+ root.mutatedFields = true;
759
+ const key = (0, exprs_js_1.exprPathKey)(target);
760
+ if (key) {
761
+ c.scope.invalidate(key);
762
+ }
763
+ }