redscript-mc 3.0.1 → 3.0.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 (225) hide show
  1. package/.github/workflows/ci.yml +1 -0
  2. package/README.md +119 -313
  3. package/README.zh.md +118 -314
  4. package/ROADMAP.md +5 -5
  5. package/dist/data/impl_test/function/counter/get.mcfunction +5 -0
  6. package/dist/data/impl_test/function/counter/inc.mcfunction +7 -0
  7. package/dist/data/impl_test/function/counter/new.mcfunction +4 -0
  8. package/dist/data/impl_test/function/load.mcfunction +1 -0
  9. package/dist/data/impl_test/function/test_impl.mcfunction +10 -0
  10. package/dist/data/minecraft/tags/function/load.json +5 -0
  11. package/dist/data/playground/function/load.mcfunction +1 -0
  12. package/dist/data/playground/function/start.mcfunction +4 -0
  13. package/dist/data/playground/function/start__say_macro_t1.mcfunction +1 -0
  14. package/dist/data/playground/function/stop.mcfunction +5 -0
  15. package/dist/data/playground/function/stop__say_macro_t0.mcfunction +1 -0
  16. package/dist/data/stdlib_queue8_test/function/__queue_append_apply.mcfunction +4 -0
  17. package/dist/data/stdlib_queue8_test/function/__queue_peek_apply.mcfunction +4 -0
  18. package/dist/data/stdlib_queue8_test/function/__queue_size_raw_apply.mcfunction +4 -0
  19. package/dist/data/stdlib_queue8_test/function/load.mcfunction +1 -0
  20. package/dist/data/stdlib_queue8_test/function/queue_clear.mcfunction +6 -0
  21. package/dist/data/stdlib_queue8_test/function/queue_empty__merge_1.mcfunction +5 -0
  22. package/dist/data/stdlib_queue8_test/function/queue_empty__then_0.mcfunction +5 -0
  23. package/dist/data/stdlib_queue8_test/function/queue_peek__merge_1.mcfunction +13 -0
  24. package/dist/data/stdlib_queue8_test/function/queue_peek__then_0.mcfunction +5 -0
  25. package/dist/data/stdlib_queue8_test/function/queue_pop__merge_1.mcfunction +15 -0
  26. package/dist/data/stdlib_queue8_test/function/queue_pop__then_0.mcfunction +5 -0
  27. package/dist/data/stdlib_queue8_test/function/queue_push__const_11.mcfunction +6 -0
  28. package/dist/data/stdlib_queue8_test/function/queue_push__const_22.mcfunction +6 -0
  29. package/dist/data/stdlib_queue8_test/function/queue_size.mcfunction +13 -0
  30. package/dist/data/stdlib_queue8_test/function/test_queue_push_and_size.mcfunction +13 -0
  31. package/dist/data/test/function/load.mcfunction +1 -0
  32. package/dist/data/test/function/say_at.mcfunction +6 -0
  33. package/dist/data/test/function/test.mcfunction +4 -0
  34. package/dist/pack.mcmeta +6 -0
  35. package/dist/package.json +1 -1
  36. package/dist/src/__tests__/formatter-extra.test.d.ts +7 -0
  37. package/dist/src/__tests__/formatter-extra.test.js +123 -0
  38. package/dist/src/__tests__/global-vars.test.d.ts +13 -0
  39. package/dist/src/__tests__/global-vars.test.js +156 -0
  40. package/dist/src/__tests__/lint/new-rules.test.d.ts +9 -0
  41. package/dist/src/__tests__/lint/new-rules.test.js +402 -0
  42. package/dist/src/__tests__/lsp-rename.test.d.ts +8 -0
  43. package/dist/src/__tests__/lsp-rename.test.js +157 -0
  44. package/dist/src/__tests__/mc-integration/say-fstring.test.d.ts +11 -0
  45. package/dist/src/__tests__/mc-integration/say-fstring.test.js +220 -0
  46. package/dist/src/__tests__/mc-integration/stdlib-coverage-2.test.js +1 -1
  47. package/dist/src/__tests__/mc-integration/stdlib-coverage-3.test.js +1 -1
  48. package/dist/src/__tests__/mc-integration/stdlib-coverage-4.test.js +1 -1
  49. package/dist/src/__tests__/mc-integration/stdlib-coverage-5.test.js +1 -1
  50. package/dist/src/__tests__/mc-integration/stdlib-coverage-6.test.js +1 -1
  51. package/dist/src/__tests__/mc-integration/stdlib-coverage-7.test.js +1 -1
  52. package/dist/src/__tests__/mc-integration/stdlib-coverage-8.test.js +1 -1
  53. package/dist/src/__tests__/mc-syntax.test.js +4 -1
  54. package/dist/src/__tests__/monomorphize-coverage.test.d.ts +9 -0
  55. package/dist/src/__tests__/monomorphize-coverage.test.js +204 -0
  56. package/dist/src/__tests__/optimizer-cse.test.d.ts +7 -0
  57. package/dist/src/__tests__/optimizer-cse.test.js +226 -0
  58. package/dist/src/__tests__/parser.test.js +4 -13
  59. package/dist/src/__tests__/repl-server-extra.test.js +6 -7
  60. package/dist/src/__tests__/repl-server.test.js +5 -7
  61. package/dist/src/__tests__/stdlib/queue.test.js +6 -6
  62. package/dist/src/cli.js +0 -0
  63. package/dist/src/lexer/index.js +2 -1
  64. package/dist/src/lint/index.d.ts +12 -5
  65. package/dist/src/lint/index.js +730 -5
  66. package/dist/src/lsp/main.js +0 -0
  67. package/dist/src/mc-test/client.d.ts +21 -0
  68. package/dist/src/mc-test/client.js +34 -0
  69. package/dist/src/mir/lower.js +108 -6
  70. package/dist/src/optimizer/interprocedural.js +37 -2
  71. package/dist/src/parser/decl-parser.d.ts +19 -0
  72. package/dist/src/parser/decl-parser.js +323 -0
  73. package/dist/src/parser/expr-parser.d.ts +46 -0
  74. package/dist/src/parser/expr-parser.js +759 -0
  75. package/dist/src/parser/index.d.ts +8 -129
  76. package/dist/src/parser/index.js +13 -2262
  77. package/dist/src/parser/stmt-parser.d.ts +28 -0
  78. package/dist/src/parser/stmt-parser.js +577 -0
  79. package/dist/src/parser/type-parser.d.ts +20 -0
  80. package/dist/src/parser/type-parser.js +257 -0
  81. package/dist/src/parser/utils.d.ts +34 -0
  82. package/dist/src/parser/utils.js +141 -0
  83. package/docs/dev/README-mc-integration-tests.md +141 -0
  84. package/docs/lint-rules.md +162 -0
  85. package/docs/stdlib/bigint.md +2 -0
  86. package/editors/vscode/README.md +63 -41
  87. package/editors/vscode/out/extension.js +1881 -1776
  88. package/editors/vscode/out/lsp-server.js +4257 -3651
  89. package/editors/vscode/package-lock.json +3 -3
  90. package/editors/vscode/package.json +1 -1
  91. package/examples/loops-demo.mcrs +87 -0
  92. package/package.json +1 -1
  93. package/redscript-docs/docs/en/stdlib/advanced.md +629 -0
  94. package/redscript-docs/docs/en/stdlib/bigint.md +316 -0
  95. package/redscript-docs/docs/en/stdlib/bits.md +292 -0
  96. package/redscript-docs/docs/en/stdlib/bossbar.md +177 -0
  97. package/redscript-docs/docs/en/stdlib/calculus.md +289 -0
  98. package/redscript-docs/docs/en/stdlib/color.md +353 -0
  99. package/redscript-docs/docs/en/stdlib/combat.md +88 -0
  100. package/redscript-docs/docs/en/stdlib/cooldown.md +82 -0
  101. package/redscript-docs/docs/en/stdlib/dialog.md +155 -0
  102. package/redscript-docs/docs/en/stdlib/easing.md +558 -0
  103. package/redscript-docs/docs/en/stdlib/ecs.md +475 -0
  104. package/redscript-docs/docs/en/stdlib/effects.md +324 -0
  105. package/redscript-docs/docs/en/stdlib/events.md +3 -0
  106. package/redscript-docs/docs/en/stdlib/expr.md +45 -0
  107. package/redscript-docs/docs/en/stdlib/fft.md +141 -0
  108. package/redscript-docs/docs/en/stdlib/geometry.md +430 -0
  109. package/redscript-docs/docs/en/stdlib/graph.md +259 -0
  110. package/redscript-docs/docs/en/stdlib/heap.md +185 -0
  111. package/redscript-docs/docs/en/stdlib/interactions.md +179 -0
  112. package/redscript-docs/docs/en/stdlib/inventory.md +97 -0
  113. package/redscript-docs/docs/en/stdlib/linalg.md +557 -0
  114. package/redscript-docs/docs/en/stdlib/list.md +559 -0
  115. package/redscript-docs/docs/en/stdlib/map.md +140 -0
  116. package/redscript-docs/docs/en/stdlib/math.md +193 -0
  117. package/redscript-docs/docs/en/stdlib/math_hp.md +149 -0
  118. package/redscript-docs/docs/en/stdlib/matrix.md +403 -0
  119. package/redscript-docs/docs/en/stdlib/mobs.md +965 -0
  120. package/redscript-docs/docs/en/stdlib/noise.md +244 -0
  121. package/redscript-docs/docs/en/stdlib/ode.md +253 -0
  122. package/redscript-docs/docs/en/stdlib/parabola.md +342 -0
  123. package/redscript-docs/docs/en/stdlib/particles.md +311 -0
  124. package/redscript-docs/docs/en/stdlib/pathfind.md +255 -0
  125. package/redscript-docs/docs/en/stdlib/physics.md +493 -0
  126. package/redscript-docs/docs/en/stdlib/player.md +78 -0
  127. package/redscript-docs/docs/en/stdlib/quaternion.md +673 -0
  128. package/redscript-docs/docs/en/stdlib/queue.md +134 -0
  129. package/redscript-docs/docs/en/stdlib/random.md +223 -0
  130. package/redscript-docs/docs/en/stdlib/result.md +143 -0
  131. package/redscript-docs/docs/en/stdlib/scheduler.md +183 -0
  132. package/redscript-docs/docs/en/stdlib/set_int.md +190 -0
  133. package/redscript-docs/docs/en/stdlib/sets.md +101 -0
  134. package/redscript-docs/docs/en/stdlib/signal.md +400 -0
  135. package/redscript-docs/docs/en/stdlib/sort.md +104 -0
  136. package/redscript-docs/docs/en/stdlib/spawn.md +147 -0
  137. package/redscript-docs/docs/en/stdlib/state.md +142 -0
  138. package/redscript-docs/docs/en/stdlib/strings.md +154 -0
  139. package/redscript-docs/docs/en/stdlib/tags.md +3451 -0
  140. package/redscript-docs/docs/en/stdlib/teams.md +153 -0
  141. package/redscript-docs/docs/en/stdlib/timer.md +246 -0
  142. package/redscript-docs/docs/en/stdlib/vec.md +158 -0
  143. package/redscript-docs/docs/en/stdlib/world.md +298 -0
  144. package/redscript-docs/docs/zh/stdlib/advanced.md +615 -0
  145. package/redscript-docs/docs/zh/stdlib/bigint.md +316 -0
  146. package/redscript-docs/docs/zh/stdlib/bits.md +292 -0
  147. package/redscript-docs/docs/zh/stdlib/bossbar.md +170 -0
  148. package/redscript-docs/docs/zh/stdlib/calculus.md +287 -0
  149. package/redscript-docs/docs/zh/stdlib/color.md +353 -0
  150. package/redscript-docs/docs/zh/stdlib/combat.md +88 -0
  151. package/redscript-docs/docs/zh/stdlib/cooldown.md +84 -0
  152. package/redscript-docs/docs/zh/stdlib/dialog.md +152 -0
  153. package/redscript-docs/docs/zh/stdlib/easing.md +558 -0
  154. package/redscript-docs/docs/zh/stdlib/ecs.md +472 -0
  155. package/redscript-docs/docs/zh/stdlib/effects.md +324 -0
  156. package/redscript-docs/docs/zh/stdlib/events.md +3 -0
  157. package/redscript-docs/docs/zh/stdlib/expr.md +37 -0
  158. package/redscript-docs/docs/zh/stdlib/fft.md +128 -0
  159. package/redscript-docs/docs/zh/stdlib/geometry.md +430 -0
  160. package/redscript-docs/docs/zh/stdlib/graph.md +259 -0
  161. package/redscript-docs/docs/zh/stdlib/heap.md +185 -0
  162. package/redscript-docs/docs/zh/stdlib/interactions.md +160 -0
  163. package/redscript-docs/docs/zh/stdlib/inventory.md +94 -0
  164. package/redscript-docs/docs/zh/stdlib/linalg.md +543 -0
  165. package/redscript-docs/docs/zh/stdlib/list.md +561 -0
  166. package/redscript-docs/docs/zh/stdlib/map.md +132 -0
  167. package/redscript-docs/docs/zh/stdlib/math.md +193 -0
  168. package/redscript-docs/docs/zh/stdlib/math_hp.md +143 -0
  169. package/redscript-docs/docs/zh/stdlib/matrix.md +396 -0
  170. package/redscript-docs/docs/zh/stdlib/mobs.md +965 -0
  171. package/redscript-docs/docs/zh/stdlib/noise.md +244 -0
  172. package/redscript-docs/docs/zh/stdlib/ode.md +243 -0
  173. package/redscript-docs/docs/zh/stdlib/parabola.md +337 -0
  174. package/redscript-docs/docs/zh/stdlib/particles.md +307 -0
  175. package/redscript-docs/docs/zh/stdlib/pathfind.md +255 -0
  176. package/redscript-docs/docs/zh/stdlib/physics.md +493 -0
  177. package/redscript-docs/docs/zh/stdlib/player.md +78 -0
  178. package/redscript-docs/docs/zh/stdlib/quaternion.md +669 -0
  179. package/redscript-docs/docs/zh/stdlib/queue.md +124 -0
  180. package/redscript-docs/docs/zh/stdlib/random.md +222 -0
  181. package/redscript-docs/docs/zh/stdlib/result.md +147 -0
  182. package/redscript-docs/docs/zh/stdlib/scheduler.md +173 -0
  183. package/redscript-docs/docs/zh/stdlib/set_int.md +180 -0
  184. package/redscript-docs/docs/zh/stdlib/sets.md +107 -0
  185. package/redscript-docs/docs/zh/stdlib/signal.md +373 -0
  186. package/redscript-docs/docs/zh/stdlib/sort.md +104 -0
  187. package/redscript-docs/docs/zh/stdlib/spawn.md +142 -0
  188. package/redscript-docs/docs/zh/stdlib/state.md +134 -0
  189. package/redscript-docs/docs/zh/stdlib/strings.md +107 -0
  190. package/redscript-docs/docs/zh/stdlib/tags.md +3451 -0
  191. package/redscript-docs/docs/zh/stdlib/teams.md +150 -0
  192. package/redscript-docs/docs/zh/stdlib/timer.md +254 -0
  193. package/redscript-docs/docs/zh/stdlib/vec.md +158 -0
  194. package/redscript-docs/docs/zh/stdlib/world.md +289 -0
  195. package/src/__tests__/formatter-extra.test.ts +139 -0
  196. package/src/__tests__/global-vars.test.ts +171 -0
  197. package/src/__tests__/lint/new-rules.test.ts +437 -0
  198. package/src/__tests__/lsp-rename.test.ts +171 -0
  199. package/src/__tests__/mc-integration/say-fstring.test.ts +211 -0
  200. package/src/__tests__/mc-integration/stdlib-coverage-2.test.ts +1 -1
  201. package/src/__tests__/mc-integration/stdlib-coverage-3.test.ts +1 -1
  202. package/src/__tests__/mc-integration/stdlib-coverage-4.test.ts +1 -1
  203. package/src/__tests__/mc-integration/stdlib-coverage-5.test.ts +1 -1
  204. package/src/__tests__/mc-integration/stdlib-coverage-6.test.ts +1 -1
  205. package/src/__tests__/mc-integration/stdlib-coverage-7.test.ts +1 -1
  206. package/src/__tests__/mc-integration/stdlib-coverage-8.test.ts +1 -1
  207. package/src/__tests__/mc-syntax.test.ts +3 -0
  208. package/src/__tests__/monomorphize-coverage.test.ts +220 -0
  209. package/src/__tests__/optimizer-cse.test.ts +250 -0
  210. package/src/__tests__/parser.test.ts +4 -13
  211. package/src/__tests__/repl-server-extra.test.ts +6 -6
  212. package/src/__tests__/repl-server.test.ts +5 -6
  213. package/src/__tests__/stdlib/queue.test.ts +6 -6
  214. package/src/lexer/index.ts +2 -1
  215. package/src/lint/index.ts +713 -5
  216. package/src/mc-test/client.ts +40 -0
  217. package/src/mir/lower.ts +111 -2
  218. package/src/optimizer/interprocedural.ts +40 -2
  219. package/src/parser/decl-parser.ts +349 -0
  220. package/src/parser/expr-parser.ts +838 -0
  221. package/src/parser/index.ts +17 -2558
  222. package/src/parser/stmt-parser.ts +585 -0
  223. package/src/parser/type-parser.ts +276 -0
  224. package/src/parser/utils.ts +173 -0
  225. package/src/stdlib/queue.mcrs +19 -6
@@ -6,11 +6,16 @@
6
6
  * Run after HIR lowering, before MIR.
7
7
  *
8
8
  * Rules:
9
- * unused-variable — let x = 5 but x never read
10
- * unused-import — import math::sin but sin never called
11
- * magic-number — literal number > 1 used directly (0 and 1 ignored)
12
- * dead-branch — if (const == const) always-true/false condition
13
- * function-too-long — function body exceeds 50 lines
9
+ * unused-variable — let x = 5 but x never read
10
+ * unused-import — import math::sin but sin never called
11
+ * magic-number — literal number > 1 used directly (0 and 1 ignored)
12
+ * dead-branch — if (const == const) always-true/false condition
13
+ * function-too-long — function body exceeds 50 lines
14
+ * no-dead-assignment — variable assigned but never read after the assignment
15
+ * prefer-match-exhaustive — Option match missing Some or None arm
16
+ * no-empty-catch — empty else branch in if_let_some (silent failure)
17
+ * naming-convention — variables must be camelCase; types must be PascalCase
18
+ * no-magic-numbers — any literal number other than 0 or 1 used in an expression
14
19
  */
15
20
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
16
21
  if (k2 === undefined) k2 = k;
@@ -65,6 +70,7 @@ function lintSource(source, imports, hir, options = {}) {
65
70
  const warnings = [];
66
71
  const file = options.filePath;
67
72
  const maxLines = options.maxFunctionLines ?? 50;
73
+ const allowedNumbers = options.allowedNumbers ?? [0, 1];
68
74
  // Rule: unused-import
69
75
  warnings.push(...checkUnusedImports(imports, hir, file));
70
76
  for (const fn of hir.functions) {
@@ -80,7 +86,19 @@ function lintSource(source, imports, hir, options = {}) {
80
86
  const fnWarning = checkFunctionLength(fn, maxLines, file);
81
87
  if (fnWarning)
82
88
  warnings.push(fnWarning);
89
+ // Rule: no-dead-assignment
90
+ warnings.push(...checkNoDeadAssignment(fn, file));
91
+ // Rule: prefer-match-exhaustive
92
+ warnings.push(...checkPreferMatchExhaustive(fn, file));
93
+ // Rule: no-empty-catch
94
+ warnings.push(...checkNoEmptyCatch(fn, file));
95
+ // Rule: naming-convention
96
+ warnings.push(...checkNamingConvention(fn, file));
97
+ // Rule: no-magic-numbers
98
+ warnings.push(...checkNoMagicNumbers(fn, allowedNumbers, file));
83
99
  }
100
+ // Rule: naming-convention — type names in structs/enums
101
+ warnings.push(...checkNamingConventionModule(hir, file));
84
102
  return warnings;
85
103
  }
86
104
  /**
@@ -927,4 +945,711 @@ function countStmts(block) {
927
945
  }
928
946
  return count;
929
947
  }
948
+ // ---------------------------------------------------------------------------
949
+ // Rule: no-dead-assignment
950
+ // ---------------------------------------------------------------------------
951
+ //
952
+ // Detects variables that are assigned (via `assign` expr) but whose value is
953
+ // never subsequently read. This is stricter than unused-variable: the variable
954
+ // IS read at some point, but a particular write is "dead" because it is
955
+ // overwritten before being read.
956
+ //
957
+ // Implementation: single-pass, scope-insensitive. We track every assignment
958
+ // target and warn if the same name is assigned twice with no intervening read.
959
+ function checkNoDeadAssignment(fn, file) {
960
+ const warnings = [];
961
+ // Map from name → span of the last unread assignment
962
+ const pendingWrite = new Map();
963
+ noDeadAssignBlock(fn.body, pendingWrite, warnings, file);
964
+ return warnings;
965
+ }
966
+ function noDeadAssignBlock(block, pending, out, file) {
967
+ for (const stmt of block) {
968
+ noDeadAssignStmt(stmt, pending, out, file);
969
+ }
970
+ }
971
+ function noDeadAssignStmt(stmt, pending, out, file) {
972
+ switch (stmt.kind) {
973
+ case 'let':
974
+ // Initial let binding — register as pending write
975
+ noDeadAssignExprReads(stmt.init, pending, out, file);
976
+ pending.set(stmt.name, { span: stmt.span });
977
+ break;
978
+ case 'let_destruct':
979
+ noDeadAssignExprReads(stmt.init, pending, out, file);
980
+ for (const name of stmt.names)
981
+ pending.set(name, { span: stmt.span });
982
+ break;
983
+ case 'const_decl':
984
+ noDeadAssignExprReads(stmt.value, pending, out, file);
985
+ break;
986
+ case 'expr':
987
+ noDeadAssignExprReads(stmt.expr, pending, out, file);
988
+ break;
989
+ case 'return':
990
+ if (stmt.value)
991
+ noDeadAssignExprReads(stmt.value, pending, out, file);
992
+ break;
993
+ case 'if':
994
+ noDeadAssignExprReads(stmt.cond, pending, out, file);
995
+ noDeadAssignBlock(stmt.then, pending, out, file);
996
+ if (stmt.else_)
997
+ noDeadAssignBlock(stmt.else_, pending, out, file);
998
+ break;
999
+ case 'while':
1000
+ noDeadAssignExprReads(stmt.cond, pending, out, file);
1001
+ noDeadAssignBlock(stmt.body, pending, out, file);
1002
+ if (stmt.step)
1003
+ noDeadAssignBlock(stmt.step, pending, out, file);
1004
+ break;
1005
+ case 'foreach':
1006
+ noDeadAssignExprReads(stmt.iterable, pending, out, file);
1007
+ noDeadAssignBlock(stmt.body, pending, out, file);
1008
+ break;
1009
+ case 'match':
1010
+ noDeadAssignExprReads(stmt.expr, pending, out, file);
1011
+ for (const arm of stmt.arms)
1012
+ noDeadAssignBlock(arm.body, pending, out, file);
1013
+ break;
1014
+ case 'execute':
1015
+ noDeadAssignBlock(stmt.body, pending, out, file);
1016
+ break;
1017
+ case 'if_let_some':
1018
+ noDeadAssignExprReads(stmt.init, pending, out, file);
1019
+ noDeadAssignBlock(stmt.then, pending, out, file);
1020
+ if (stmt.else_)
1021
+ noDeadAssignBlock(stmt.else_, pending, out, file);
1022
+ break;
1023
+ case 'while_let_some':
1024
+ noDeadAssignExprReads(stmt.init, pending, out, file);
1025
+ noDeadAssignBlock(stmt.body, pending, out, file);
1026
+ break;
1027
+ case 'labeled_loop':
1028
+ noDeadAssignStmt(stmt.body, pending, out, file);
1029
+ break;
1030
+ }
1031
+ }
1032
+ /** Processes an expression: reads clear pending writes; assign exprs register new pending writes. */
1033
+ function noDeadAssignExprReads(expr, pending, out, file) {
1034
+ if (!expr)
1035
+ return;
1036
+ switch (expr.kind) {
1037
+ case 'ident':
1038
+ // Reading a variable clears its pending write
1039
+ pending.delete(expr.name);
1040
+ break;
1041
+ case 'assign': {
1042
+ // RHS is read first
1043
+ noDeadAssignExprReads(expr.value, pending, out, file);
1044
+ // Then the target is written — if there's already a pending unread write, warn
1045
+ if (pending.has(expr.target)) {
1046
+ const prev = pending.get(expr.target);
1047
+ const warn = {
1048
+ rule: 'no-dead-assignment',
1049
+ message: `Assignment to "${expr.target}" is never read before being overwritten`,
1050
+ file,
1051
+ };
1052
+ if (prev.span) {
1053
+ warn.line = prev.span.line;
1054
+ warn.col = prev.span.col;
1055
+ }
1056
+ out.push(warn);
1057
+ }
1058
+ pending.set(expr.target, { span: expr.span });
1059
+ break;
1060
+ }
1061
+ case 'binary':
1062
+ noDeadAssignExprReads(expr.left, pending, out, file);
1063
+ noDeadAssignExprReads(expr.right, pending, out, file);
1064
+ break;
1065
+ case 'unary':
1066
+ noDeadAssignExprReads(expr.operand, pending, out, file);
1067
+ break;
1068
+ case 'call':
1069
+ for (const arg of expr.args)
1070
+ noDeadAssignExprReads(arg, pending, out, file);
1071
+ break;
1072
+ case 'invoke':
1073
+ noDeadAssignExprReads(expr.callee, pending, out, file);
1074
+ for (const arg of expr.args)
1075
+ noDeadAssignExprReads(arg, pending, out, file);
1076
+ break;
1077
+ case 'static_call':
1078
+ for (const arg of expr.args)
1079
+ noDeadAssignExprReads(arg, pending, out, file);
1080
+ break;
1081
+ case 'member':
1082
+ noDeadAssignExprReads(expr.obj, pending, out, file);
1083
+ break;
1084
+ case 'member_assign':
1085
+ noDeadAssignExprReads(expr.obj, pending, out, file);
1086
+ noDeadAssignExprReads(expr.value, pending, out, file);
1087
+ break;
1088
+ case 'index':
1089
+ noDeadAssignExprReads(expr.obj, pending, out, file);
1090
+ noDeadAssignExprReads(expr.index, pending, out, file);
1091
+ break;
1092
+ case 'index_assign':
1093
+ noDeadAssignExprReads(expr.obj, pending, out, file);
1094
+ noDeadAssignExprReads(expr.index, pending, out, file);
1095
+ noDeadAssignExprReads(expr.value, pending, out, file);
1096
+ break;
1097
+ case 'some_lit':
1098
+ noDeadAssignExprReads(expr.value, pending, out, file);
1099
+ break;
1100
+ case 'unwrap_or':
1101
+ noDeadAssignExprReads(expr.opt, pending, out, file);
1102
+ noDeadAssignExprReads(expr.default_, pending, out, file);
1103
+ break;
1104
+ case 'type_cast':
1105
+ noDeadAssignExprReads(expr.expr, pending, out, file);
1106
+ break;
1107
+ case 'array_lit':
1108
+ case 'tuple_lit':
1109
+ for (const e of expr.elements)
1110
+ noDeadAssignExprReads(e, pending, out, file);
1111
+ break;
1112
+ case 'struct_lit':
1113
+ for (const f of expr.fields)
1114
+ noDeadAssignExprReads(f.value, pending, out, file);
1115
+ break;
1116
+ case 'str_interp':
1117
+ for (const p of expr.parts) {
1118
+ if (typeof p !== 'string')
1119
+ noDeadAssignExprReads(p, pending, out, file);
1120
+ }
1121
+ break;
1122
+ case 'f_string':
1123
+ for (const p of expr.parts) {
1124
+ if (p.kind === 'expr')
1125
+ noDeadAssignExprReads(p.expr, pending, out, file);
1126
+ }
1127
+ break;
1128
+ case 'lambda':
1129
+ if (Array.isArray(expr.body)) {
1130
+ noDeadAssignBlock(expr.body, pending, out, file);
1131
+ }
1132
+ else {
1133
+ noDeadAssignExprReads(expr.body, pending, out, file);
1134
+ }
1135
+ break;
1136
+ case 'enum_construct':
1137
+ for (const f of expr.args)
1138
+ noDeadAssignExprReads(f.value, pending, out, file);
1139
+ break;
1140
+ default:
1141
+ break;
1142
+ }
1143
+ }
1144
+ // ---------------------------------------------------------------------------
1145
+ // Rule: prefer-match-exhaustive
1146
+ // ---------------------------------------------------------------------------
1147
+ //
1148
+ // When a match statement uses Option patterns (PatSome / PatNone), it should
1149
+ // cover both arms. Missing PatNone means the None case falls through silently;
1150
+ // missing PatSome means Some values are unhandled.
1151
+ function checkPreferMatchExhaustive(fn, file) {
1152
+ const warnings = [];
1153
+ checkPreferMatchExhaustiveBlock(fn.body, warnings, file);
1154
+ return warnings;
1155
+ }
1156
+ function checkPreferMatchExhaustiveBlock(block, out, file) {
1157
+ for (const stmt of block) {
1158
+ checkPreferMatchExhaustiveStmt(stmt, out, file);
1159
+ }
1160
+ }
1161
+ function checkPreferMatchExhaustiveStmt(stmt, out, file) {
1162
+ switch (stmt.kind) {
1163
+ case 'match': {
1164
+ const patKinds = new Set(stmt.arms.map(a => a.pattern.kind));
1165
+ const hasOptionPat = patKinds.has('PatSome') || patKinds.has('PatNone');
1166
+ const hasWild = patKinds.has('PatWild');
1167
+ if (hasOptionPat && !hasWild) {
1168
+ const hasSome = patKinds.has('PatSome');
1169
+ const hasNone = patKinds.has('PatNone');
1170
+ if (!hasSome) {
1171
+ const warn = {
1172
+ rule: 'prefer-match-exhaustive',
1173
+ message: `match on Option is missing a Some(_) arm`,
1174
+ file,
1175
+ };
1176
+ if (stmt.span) {
1177
+ warn.line = stmt.span.line;
1178
+ warn.col = stmt.span.col;
1179
+ }
1180
+ out.push(warn);
1181
+ }
1182
+ if (!hasNone) {
1183
+ const warn = {
1184
+ rule: 'prefer-match-exhaustive',
1185
+ message: `match on Option is missing a None arm`,
1186
+ file,
1187
+ };
1188
+ if (stmt.span) {
1189
+ warn.line = stmt.span.line;
1190
+ warn.col = stmt.span.col;
1191
+ }
1192
+ out.push(warn);
1193
+ }
1194
+ }
1195
+ // Recurse into arm bodies
1196
+ for (const arm of stmt.arms)
1197
+ checkPreferMatchExhaustiveBlock(arm.body, out, file);
1198
+ break;
1199
+ }
1200
+ case 'if':
1201
+ checkPreferMatchExhaustiveBlock(stmt.then, out, file);
1202
+ if (stmt.else_)
1203
+ checkPreferMatchExhaustiveBlock(stmt.else_, out, file);
1204
+ break;
1205
+ case 'while':
1206
+ checkPreferMatchExhaustiveBlock(stmt.body, out, file);
1207
+ if (stmt.step)
1208
+ checkPreferMatchExhaustiveBlock(stmt.step, out, file);
1209
+ break;
1210
+ case 'foreach':
1211
+ checkPreferMatchExhaustiveBlock(stmt.body, out, file);
1212
+ break;
1213
+ case 'execute':
1214
+ checkPreferMatchExhaustiveBlock(stmt.body, out, file);
1215
+ break;
1216
+ case 'if_let_some':
1217
+ checkPreferMatchExhaustiveBlock(stmt.then, out, file);
1218
+ if (stmt.else_)
1219
+ checkPreferMatchExhaustiveBlock(stmt.else_, out, file);
1220
+ break;
1221
+ case 'while_let_some':
1222
+ checkPreferMatchExhaustiveBlock(stmt.body, out, file);
1223
+ break;
1224
+ case 'labeled_loop':
1225
+ checkPreferMatchExhaustiveStmt(stmt.body, out, file);
1226
+ break;
1227
+ }
1228
+ }
1229
+ // ---------------------------------------------------------------------------
1230
+ // Rule: no-empty-catch
1231
+ // ---------------------------------------------------------------------------
1232
+ //
1233
+ // RedScript has no try/catch. The equivalent pattern is `if_let_some` with an
1234
+ // empty else_ block (silently ignoring the None case) or a match arm whose
1235
+ // body is empty. Both patterns silently swallow a failure — warn about them.
1236
+ function checkNoEmptyCatch(fn, file) {
1237
+ const warnings = [];
1238
+ checkNoEmptyCatchBlock(fn.body, warnings, file);
1239
+ return warnings;
1240
+ }
1241
+ function checkNoEmptyCatchBlock(block, out, file) {
1242
+ for (const stmt of block) {
1243
+ checkNoEmptyCatchStmt(stmt, out, file);
1244
+ }
1245
+ }
1246
+ function checkNoEmptyCatchStmt(stmt, out, file) {
1247
+ switch (stmt.kind) {
1248
+ case 'if_let_some':
1249
+ if (stmt.else_ && stmt.else_.length === 0) {
1250
+ const warn = {
1251
+ rule: 'no-empty-catch',
1252
+ message: `Empty else block in if let Some — None case is silently ignored`,
1253
+ file,
1254
+ };
1255
+ if (stmt.span) {
1256
+ warn.line = stmt.span.line;
1257
+ warn.col = stmt.span.col;
1258
+ }
1259
+ out.push(warn);
1260
+ }
1261
+ checkNoEmptyCatchBlock(stmt.then, out, file);
1262
+ if (stmt.else_)
1263
+ checkNoEmptyCatchBlock(stmt.else_, out, file);
1264
+ break;
1265
+ case 'match':
1266
+ for (const arm of stmt.arms) {
1267
+ if (arm.body.length === 0) {
1268
+ const warn = {
1269
+ rule: 'no-empty-catch',
1270
+ message: `Empty match arm body — consider handling this case explicitly`,
1271
+ file,
1272
+ };
1273
+ if (stmt.span) {
1274
+ warn.line = stmt.span.line;
1275
+ warn.col = stmt.span.col;
1276
+ }
1277
+ out.push(warn);
1278
+ }
1279
+ checkNoEmptyCatchBlock(arm.body, out, file);
1280
+ }
1281
+ break;
1282
+ case 'if':
1283
+ checkNoEmptyCatchBlock(stmt.then, out, file);
1284
+ if (stmt.else_)
1285
+ checkNoEmptyCatchBlock(stmt.else_, out, file);
1286
+ break;
1287
+ case 'while':
1288
+ checkNoEmptyCatchBlock(stmt.body, out, file);
1289
+ if (stmt.step)
1290
+ checkNoEmptyCatchBlock(stmt.step, out, file);
1291
+ break;
1292
+ case 'foreach':
1293
+ checkNoEmptyCatchBlock(stmt.body, out, file);
1294
+ break;
1295
+ case 'execute':
1296
+ checkNoEmptyCatchBlock(stmt.body, out, file);
1297
+ break;
1298
+ case 'while_let_some':
1299
+ checkNoEmptyCatchBlock(stmt.body, out, file);
1300
+ break;
1301
+ case 'labeled_loop':
1302
+ checkNoEmptyCatchStmt(stmt.body, out, file);
1303
+ break;
1304
+ }
1305
+ }
1306
+ // ---------------------------------------------------------------------------
1307
+ // Rule: naming-convention
1308
+ // ---------------------------------------------------------------------------
1309
+ //
1310
+ // Variables (let bindings, foreach bindings) must use camelCase.
1311
+ // Type names (structs, enums) must use PascalCase.
1312
+ //
1313
+ // camelCase: starts with lowercase letter, no underscores (except leading _)
1314
+ // PascalCase: starts with uppercase letter
1315
+ function isCamelCase(name) {
1316
+ // Allow leading underscore (private convention), then must start lowercase
1317
+ const stripped = name.startsWith('_') ? name.slice(1) : name;
1318
+ if (stripped.length === 0)
1319
+ return true;
1320
+ // Must start with lowercase, no underscores after first char
1321
+ return /^[a-z][a-zA-Z0-9]*$/.test(stripped);
1322
+ }
1323
+ function isPascalCase(name) {
1324
+ return /^[A-Z][a-zA-Z0-9]*$/.test(name);
1325
+ }
1326
+ function checkNamingConvention(fn, file) {
1327
+ const warnings = [];
1328
+ checkNamingConventionBlock(fn.body, warnings, file);
1329
+ return warnings;
1330
+ }
1331
+ function checkNamingConventionBlock(block, out, file) {
1332
+ for (const stmt of block) {
1333
+ checkNamingConventionStmt(stmt, out, file);
1334
+ }
1335
+ }
1336
+ function checkNamingConventionStmt(stmt, out, file) {
1337
+ switch (stmt.kind) {
1338
+ case 'let':
1339
+ if (!isCamelCase(stmt.name)) {
1340
+ const warn = {
1341
+ rule: 'naming-convention',
1342
+ message: `Variable "${stmt.name}" should use camelCase`,
1343
+ file,
1344
+ };
1345
+ if (stmt.span) {
1346
+ warn.line = stmt.span.line;
1347
+ warn.col = stmt.span.col;
1348
+ }
1349
+ out.push(warn);
1350
+ }
1351
+ break;
1352
+ case 'let_destruct':
1353
+ for (const name of stmt.names) {
1354
+ if (!isCamelCase(name)) {
1355
+ const warn = {
1356
+ rule: 'naming-convention',
1357
+ message: `Variable "${name}" should use camelCase`,
1358
+ file,
1359
+ };
1360
+ if (stmt.span) {
1361
+ warn.line = stmt.span.line;
1362
+ warn.col = stmt.span.col;
1363
+ }
1364
+ out.push(warn);
1365
+ }
1366
+ }
1367
+ break;
1368
+ case 'foreach':
1369
+ if (!isCamelCase(stmt.binding)) {
1370
+ const warn = {
1371
+ rule: 'naming-convention',
1372
+ message: `Loop variable "${stmt.binding}" should use camelCase`,
1373
+ file,
1374
+ };
1375
+ if (stmt.span) {
1376
+ warn.line = stmt.span.line;
1377
+ warn.col = stmt.span.col;
1378
+ }
1379
+ out.push(warn);
1380
+ }
1381
+ checkNamingConventionBlock(stmt.body, out, file);
1382
+ break;
1383
+ case 'if':
1384
+ checkNamingConventionBlock(stmt.then, out, file);
1385
+ if (stmt.else_)
1386
+ checkNamingConventionBlock(stmt.else_, out, file);
1387
+ break;
1388
+ case 'while':
1389
+ checkNamingConventionBlock(stmt.body, out, file);
1390
+ if (stmt.step)
1391
+ checkNamingConventionBlock(stmt.step, out, file);
1392
+ break;
1393
+ case 'match':
1394
+ for (const arm of stmt.arms)
1395
+ checkNamingConventionBlock(arm.body, out, file);
1396
+ break;
1397
+ case 'execute':
1398
+ checkNamingConventionBlock(stmt.body, out, file);
1399
+ break;
1400
+ case 'if_let_some':
1401
+ if (!isCamelCase(stmt.binding)) {
1402
+ const warn = {
1403
+ rule: 'naming-convention',
1404
+ message: `Binding "${stmt.binding}" should use camelCase`,
1405
+ file,
1406
+ };
1407
+ if (stmt.span) {
1408
+ warn.line = stmt.span.line;
1409
+ warn.col = stmt.span.col;
1410
+ }
1411
+ out.push(warn);
1412
+ }
1413
+ checkNamingConventionBlock(stmt.then, out, file);
1414
+ if (stmt.else_)
1415
+ checkNamingConventionBlock(stmt.else_, out, file);
1416
+ break;
1417
+ case 'while_let_some':
1418
+ if (!isCamelCase(stmt.binding)) {
1419
+ const warn = {
1420
+ rule: 'naming-convention',
1421
+ message: `Binding "${stmt.binding}" should use camelCase`,
1422
+ file,
1423
+ };
1424
+ if (stmt.span) {
1425
+ warn.line = stmt.span.line;
1426
+ warn.col = stmt.span.col;
1427
+ }
1428
+ out.push(warn);
1429
+ }
1430
+ checkNamingConventionBlock(stmt.body, out, file);
1431
+ break;
1432
+ case 'labeled_loop':
1433
+ checkNamingConventionStmt(stmt.body, out, file);
1434
+ break;
1435
+ }
1436
+ }
1437
+ /** Check type names (structs and enums) at the module level. */
1438
+ function checkNamingConventionModule(hir, file) {
1439
+ const warnings = [];
1440
+ for (const s of hir.structs) {
1441
+ if (!isPascalCase(s.name)) {
1442
+ const warn = {
1443
+ rule: 'naming-convention',
1444
+ message: `Struct "${s.name}" should use PascalCase`,
1445
+ file,
1446
+ };
1447
+ if (s.span) {
1448
+ warn.line = s.span.line;
1449
+ warn.col = s.span.col;
1450
+ }
1451
+ warnings.push(warn);
1452
+ }
1453
+ }
1454
+ for (const e of hir.enums) {
1455
+ if (!isPascalCase(e.name)) {
1456
+ const warn = {
1457
+ rule: 'naming-convention',
1458
+ message: `Enum "${e.name}" should use PascalCase`,
1459
+ file,
1460
+ };
1461
+ if (e.span) {
1462
+ warn.line = e.span.line;
1463
+ warn.col = e.span.col;
1464
+ }
1465
+ warnings.push(warn);
1466
+ }
1467
+ }
1468
+ return warnings;
1469
+ }
1470
+ // ---------------------------------------------------------------------------
1471
+ // Rule: no-magic-numbers
1472
+ // ---------------------------------------------------------------------------
1473
+ //
1474
+ // Flags any numeric literal that is not in the allowedNumbers list (default
1475
+ // [0, 1]) when used outside of a const declaration. Unlike the older
1476
+ // magic-number rule (threshold > 1), this rule checks against an explicit
1477
+ // allow-list, making it configurable for different projects.
1478
+ function checkNoMagicNumbers(fn, allowed, file) {
1479
+ const warnings = [];
1480
+ const allowedSet = new Set(allowed);
1481
+ checkNoMagicNumbersBlock(fn.body, warnings, file, allowedSet, /*inConst=*/ false);
1482
+ return warnings;
1483
+ }
1484
+ function checkNoMagicNumbersBlock(block, out, file, allowed, inConst) {
1485
+ for (const stmt of block) {
1486
+ checkNoMagicNumbersStmt(stmt, out, file, allowed, inConst);
1487
+ }
1488
+ }
1489
+ function checkNoMagicNumbersStmt(stmt, out, file, allowed, inConst) {
1490
+ switch (stmt.kind) {
1491
+ case 'const_decl':
1492
+ // Numbers in const declarations are the intended fix — skip
1493
+ break;
1494
+ case 'let':
1495
+ checkNoMagicNumbersExpr(stmt.init, out, file, allowed);
1496
+ break;
1497
+ case 'let_destruct':
1498
+ checkNoMagicNumbersExpr(stmt.init, out, file, allowed);
1499
+ break;
1500
+ case 'expr':
1501
+ checkNoMagicNumbersExpr(stmt.expr, out, file, allowed);
1502
+ break;
1503
+ case 'return':
1504
+ if (stmt.value)
1505
+ checkNoMagicNumbersExpr(stmt.value, out, file, allowed);
1506
+ break;
1507
+ case 'if':
1508
+ checkNoMagicNumbersExpr(stmt.cond, out, file, allowed);
1509
+ checkNoMagicNumbersBlock(stmt.then, out, file, allowed, false);
1510
+ if (stmt.else_)
1511
+ checkNoMagicNumbersBlock(stmt.else_, out, file, allowed, false);
1512
+ break;
1513
+ case 'while':
1514
+ checkNoMagicNumbersExpr(stmt.cond, out, file, allowed);
1515
+ checkNoMagicNumbersBlock(stmt.body, out, file, allowed, false);
1516
+ if (stmt.step)
1517
+ checkNoMagicNumbersBlock(stmt.step, out, file, allowed, false);
1518
+ break;
1519
+ case 'foreach':
1520
+ checkNoMagicNumbersExpr(stmt.iterable, out, file, allowed);
1521
+ checkNoMagicNumbersBlock(stmt.body, out, file, allowed, false);
1522
+ break;
1523
+ case 'match':
1524
+ checkNoMagicNumbersExpr(stmt.expr, out, file, allowed);
1525
+ for (const arm of stmt.arms)
1526
+ checkNoMagicNumbersBlock(arm.body, out, file, allowed, false);
1527
+ break;
1528
+ case 'execute':
1529
+ checkNoMagicNumbersBlock(stmt.body, out, file, allowed, false);
1530
+ break;
1531
+ case 'if_let_some':
1532
+ checkNoMagicNumbersExpr(stmt.init, out, file, allowed);
1533
+ checkNoMagicNumbersBlock(stmt.then, out, file, allowed, false);
1534
+ if (stmt.else_)
1535
+ checkNoMagicNumbersBlock(stmt.else_, out, file, allowed, false);
1536
+ break;
1537
+ case 'while_let_some':
1538
+ checkNoMagicNumbersExpr(stmt.init, out, file, allowed);
1539
+ checkNoMagicNumbersBlock(stmt.body, out, file, allowed, false);
1540
+ break;
1541
+ case 'labeled_loop':
1542
+ checkNoMagicNumbersStmt(stmt.body, out, file, allowed, inConst);
1543
+ break;
1544
+ }
1545
+ }
1546
+ function checkNoMagicNumbersExpr(expr, out, file, allowed) {
1547
+ if (!expr)
1548
+ return;
1549
+ switch (expr.kind) {
1550
+ case 'int_lit':
1551
+ case 'float_lit':
1552
+ case 'byte_lit':
1553
+ case 'short_lit':
1554
+ case 'long_lit':
1555
+ case 'double_lit':
1556
+ if (!allowed.has(expr.value)) {
1557
+ const warn = {
1558
+ rule: 'no-magic-numbers',
1559
+ message: `Magic number ${expr.value} — extract to a named const`,
1560
+ file,
1561
+ };
1562
+ if (expr.span) {
1563
+ warn.line = expr.span.line;
1564
+ warn.col = expr.span.col;
1565
+ }
1566
+ out.push(warn);
1567
+ }
1568
+ break;
1569
+ case 'binary':
1570
+ checkNoMagicNumbersExpr(expr.left, out, file, allowed);
1571
+ checkNoMagicNumbersExpr(expr.right, out, file, allowed);
1572
+ break;
1573
+ case 'unary':
1574
+ checkNoMagicNumbersExpr(expr.operand, out, file, allowed);
1575
+ break;
1576
+ case 'call':
1577
+ for (const arg of expr.args)
1578
+ checkNoMagicNumbersExpr(arg, out, file, allowed);
1579
+ break;
1580
+ case 'invoke':
1581
+ checkNoMagicNumbersExpr(expr.callee, out, file, allowed);
1582
+ for (const arg of expr.args)
1583
+ checkNoMagicNumbersExpr(arg, out, file, allowed);
1584
+ break;
1585
+ case 'static_call':
1586
+ for (const arg of expr.args)
1587
+ checkNoMagicNumbersExpr(arg, out, file, allowed);
1588
+ break;
1589
+ case 'member':
1590
+ checkNoMagicNumbersExpr(expr.obj, out, file, allowed);
1591
+ break;
1592
+ case 'member_assign':
1593
+ checkNoMagicNumbersExpr(expr.obj, out, file, allowed);
1594
+ checkNoMagicNumbersExpr(expr.value, out, file, allowed);
1595
+ break;
1596
+ case 'index':
1597
+ checkNoMagicNumbersExpr(expr.obj, out, file, allowed);
1598
+ checkNoMagicNumbersExpr(expr.index, out, file, allowed);
1599
+ break;
1600
+ case 'index_assign':
1601
+ checkNoMagicNumbersExpr(expr.obj, out, file, allowed);
1602
+ checkNoMagicNumbersExpr(expr.index, out, file, allowed);
1603
+ checkNoMagicNumbersExpr(expr.value, out, file, allowed);
1604
+ break;
1605
+ case 'assign':
1606
+ checkNoMagicNumbersExpr(expr.value, out, file, allowed);
1607
+ break;
1608
+ case 'some_lit':
1609
+ checkNoMagicNumbersExpr(expr.value, out, file, allowed);
1610
+ break;
1611
+ case 'unwrap_or':
1612
+ checkNoMagicNumbersExpr(expr.opt, out, file, allowed);
1613
+ checkNoMagicNumbersExpr(expr.default_, out, file, allowed);
1614
+ break;
1615
+ case 'type_cast':
1616
+ checkNoMagicNumbersExpr(expr.expr, out, file, allowed);
1617
+ break;
1618
+ case 'array_lit':
1619
+ case 'tuple_lit':
1620
+ for (const e of expr.elements)
1621
+ checkNoMagicNumbersExpr(e, out, file, allowed);
1622
+ break;
1623
+ case 'struct_lit':
1624
+ for (const f of expr.fields)
1625
+ checkNoMagicNumbersExpr(f.value, out, file, allowed);
1626
+ break;
1627
+ case 'str_interp':
1628
+ for (const p of expr.parts) {
1629
+ if (typeof p !== 'string')
1630
+ checkNoMagicNumbersExpr(p, out, file, allowed);
1631
+ }
1632
+ break;
1633
+ case 'f_string':
1634
+ for (const p of expr.parts) {
1635
+ if (p.kind === 'expr')
1636
+ checkNoMagicNumbersExpr(p.expr, out, file, allowed);
1637
+ }
1638
+ break;
1639
+ case 'lambda':
1640
+ if (Array.isArray(expr.body)) {
1641
+ checkNoMagicNumbersBlock(expr.body, out, file, allowed, false);
1642
+ }
1643
+ else {
1644
+ checkNoMagicNumbersExpr(expr.body, out, file, allowed);
1645
+ }
1646
+ break;
1647
+ case 'enum_construct':
1648
+ for (const f of expr.args)
1649
+ checkNoMagicNumbersExpr(f.value, out, file, allowed);
1650
+ break;
1651
+ default:
1652
+ break;
1653
+ }
1654
+ }
930
1655
  //# sourceMappingURL=index.js.map