mt-lang 0.3.8 → 0.3.10

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 (200) hide show
  1. checksums.yaml +4 -4
  2. data/lib/milk_tea/base.rb +1 -2
  3. data/lib/milk_tea/bindings/bindgen/ast_parser.rb +0 -2
  4. data/lib/milk_tea/bindings/bindgen/declaration.rb +0 -2
  5. data/lib/milk_tea/bindings/bindgen/emitter.rb +0 -2
  6. data/lib/milk_tea/bindings/bindgen/overrides.rb +0 -2
  7. data/lib/milk_tea/bindings/bindgen/type_mapper.rb +51 -53
  8. data/lib/milk_tea/bindings/imported_bindings/generator.rb +7 -9
  9. data/lib/milk_tea/bindings/imported_bindings/method_source.rb +0 -2
  10. data/lib/milk_tea/bindings/imported_bindings/naming.rb +6 -8
  11. data/lib/milk_tea/bindings/imported_bindings.rb +0 -2
  12. data/lib/milk_tea/bindings/opengl_registry.rb +3 -7
  13. data/lib/milk_tea/bindings/steamworks.rb +12 -16
  14. data/lib/milk_tea/bindings/upstream_sources.rb +10 -14
  15. data/lib/milk_tea/bindings/vendored_box2d.rb +10 -12
  16. data/lib/milk_tea/bindings/vendored_cjson.rb +6 -8
  17. data/lib/milk_tea/bindings/vendored_flecs.rb +10 -12
  18. data/lib/milk_tea/bindings/vendored_glfw.rb +11 -13
  19. data/lib/milk_tea/bindings/vendored_libuv.rb +11 -13
  20. data/lib/milk_tea/bindings/vendored_pcre2.rb +11 -13
  21. data/lib/milk_tea/bindings/vendored_raylib.rb +6 -10
  22. data/lib/milk_tea/bindings/vendored_sdl3.rb +11 -13
  23. data/lib/milk_tea/bindings/vendored_steamworks.rb +1 -5
  24. data/lib/milk_tea/core/ast.rb +103 -45
  25. data/lib/milk_tea/core/async_runtime_installer.rb +0 -2
  26. data/lib/milk_tea/core/bindings/attribute_binding.rb +1 -3
  27. data/lib/milk_tea/core/c_backend/aggregate_utils.rb +147 -84
  28. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +394 -396
  29. data/lib/milk_tea/core/c_backend/expressions.rb +489 -476
  30. data/lib/milk_tea/core/c_backend/feature_detection.rb +446 -437
  31. data/lib/milk_tea/core/c_backend/format_helpers.rb +0 -2
  32. data/lib/milk_tea/core/c_backend/reachability.rb +339 -341
  33. data/lib/milk_tea/core/c_backend/reinterpret.rb +231 -217
  34. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +531 -526
  35. data/lib/milk_tea/core/c_backend/statements.rb +528 -530
  36. data/lib/milk_tea/core/c_backend/type_collectors.rb +1344 -1346
  37. data/lib/milk_tea/core/c_backend/type_declaration.rb +215 -213
  38. data/lib/milk_tea/core/c_backend/type_system.rb +347 -349
  39. data/lib/milk_tea/core/c_backend.rb +17 -9
  40. data/lib/milk_tea/core/compatibility_helpers.rb +0 -2
  41. data/lib/milk_tea/core/compile_time/const_eval.rb +7 -12
  42. data/lib/milk_tea/core/compile_time.rb +32 -38
  43. data/lib/milk_tea/core/control_flow/builder.rb +3 -5
  44. data/lib/milk_tea/core/control_flow/constant_propagation.rb +14 -17
  45. data/lib/milk_tea/core/control_flow/definite_assignment.rb +0 -1
  46. data/lib/milk_tea/core/ir.rb +10 -10
  47. data/lib/milk_tea/core/lexer/character_classes.rb +5 -7
  48. data/lib/milk_tea/core/lexer/format_strings.rb +7 -9
  49. data/lib/milk_tea/core/lexer/heredocs.rb +22 -22
  50. data/lib/milk_tea/core/lexer/indentation.rb +0 -2
  51. data/lib/milk_tea/core/lexer/numbers.rb +0 -2
  52. data/lib/milk_tea/core/lexer/recovery.rb +0 -2
  53. data/lib/milk_tea/core/lexer/strings.rb +0 -2
  54. data/lib/milk_tea/core/lexer/symbols.rb +0 -2
  55. data/lib/milk_tea/core/lexer/trivia.rb +0 -2
  56. data/lib/milk_tea/core/lexer.rb +0 -2
  57. data/lib/milk_tea/core/lowering/async/analysis.rb +0 -2
  58. data/lib/milk_tea/core/lowering/async/lowering.rb +1264 -1265
  59. data/lib/milk_tea/core/lowering/async/normalization.rb +0 -2
  60. data/lib/milk_tea/core/lowering/async.rb +627 -630
  61. data/lib/milk_tea/core/lowering/block.rb +413 -416
  62. data/lib/milk_tea/core/lowering/calls.rb +1381 -1384
  63. data/lib/milk_tea/core/lowering/declarations.rb +180 -183
  64. data/lib/milk_tea/core/lowering/dyn.rb +48 -50
  65. data/lib/milk_tea/core/lowering/events.rb +983 -985
  66. data/lib/milk_tea/core/lowering/expressions.rb +1513 -1518
  67. data/lib/milk_tea/core/lowering/foreign_cstr.rb +181 -184
  68. data/lib/milk_tea/core/lowering/functions.rb +185 -188
  69. data/lib/milk_tea/core/lowering/loops.rb +1028 -1031
  70. data/lib/milk_tea/core/lowering/proc.rb +363 -366
  71. data/lib/milk_tea/core/lowering/resolve.rb +2133 -2135
  72. data/lib/milk_tea/core/lowering/scans.rb +179 -181
  73. data/lib/milk_tea/core/lowering/str_buffer.rb +102 -105
  74. data/lib/milk_tea/core/lowering/utils.rb +1040 -1044
  75. data/lib/milk_tea/core/lowering.rb +5 -7
  76. data/lib/milk_tea/core/module_binder.rb +0 -2
  77. data/lib/milk_tea/core/module_loader/errors.rb +4 -2
  78. data/lib/milk_tea/core/module_loader.rb +113 -20
  79. data/lib/milk_tea/core/module_path_resolver.rb +0 -2
  80. data/lib/milk_tea/core/module_roots.rb +6 -8
  81. data/lib/milk_tea/core/parser/attributes.rb +0 -2
  82. data/lib/milk_tea/core/parser/blocks.rb +0 -2
  83. data/lib/milk_tea/core/parser/declarations.rb +0 -2
  84. data/lib/milk_tea/core/parser/expressions.rb +31 -14
  85. data/lib/milk_tea/core/parser/recovery.rb +0 -2
  86. data/lib/milk_tea/core/parser/statements.rb +0 -2
  87. data/lib/milk_tea/core/parser/types.rb +8 -10
  88. data/lib/milk_tea/core/parser.rb +1 -3
  89. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +31 -33
  90. data/lib/milk_tea/core/pretty_printer/base_formatter.rb +0 -2
  91. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +0 -2
  92. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +0 -2
  93. data/lib/milk_tea/core/semantic_analyzer/attributes.rb +14 -16
  94. data/lib/milk_tea/core/semantic_analyzer/calls.rb +0 -2
  95. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1 -14
  96. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +18 -21
  97. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +0 -3
  98. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +12 -15
  99. data/lib/milk_tea/core/semantic_analyzer/generics.rb +57 -59
  100. data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +0 -2
  101. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +5 -9
  102. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +0 -3
  103. data/lib/milk_tea/core/semantic_analyzer/statements.rb +35 -40
  104. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +10 -12
  105. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +0 -6
  106. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +102 -105
  107. data/lib/milk_tea/core/semantic_analyzer.rb +17 -8
  108. data/lib/milk_tea/core/types/layout.rb +21 -22
  109. data/lib/milk_tea/core/types/predicates.rb +0 -2
  110. data/lib/milk_tea/core/types/registry.rb +16 -18
  111. data/lib/milk_tea/core/types/visitor.rb +0 -4
  112. data/lib/milk_tea/dap/backends/lldb_dap.rb +0 -2
  113. data/lib/milk_tea/dap/protocol.rb +1 -1
  114. data/lib/milk_tea/dap/server/breakpoints.rb +0 -2
  115. data/lib/milk_tea/dap/server/debug_map.rb +0 -2
  116. data/lib/milk_tea/dap/server/handlers.rb +0 -2
  117. data/lib/milk_tea/dap/server/launch.rb +0 -2
  118. data/lib/milk_tea/dap/server/lldb_backend.rb +8 -10
  119. data/lib/milk_tea/dap/server/pause_diagnostics.rb +14 -16
  120. data/lib/milk_tea/dap/server/utilities.rb +0 -2
  121. data/lib/milk_tea/dap/server/wire.rb +0 -2
  122. data/lib/milk_tea/dap/server.rb +0 -2
  123. data/lib/milk_tea/lsp/diagnostics.rb +16 -16
  124. data/lib/milk_tea/lsp/server/call_hierarchy.rb +11 -12
  125. data/lib/milk_tea/lsp/server/code_actions.rb +355 -357
  126. data/lib/milk_tea/lsp/server/code_lens.rb +23 -25
  127. data/lib/milk_tea/lsp/server/completion.rb +873 -875
  128. data/lib/milk_tea/lsp/server/configuration.rb +129 -131
  129. data/lib/milk_tea/lsp/server/debug_info.rb +0 -2
  130. data/lib/milk_tea/lsp/server/definition.rb +564 -565
  131. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +191 -193
  132. data/lib/milk_tea/lsp/server/execute_command.rb +0 -2
  133. data/lib/milk_tea/lsp/server/folding_range.rb +0 -4
  134. data/lib/milk_tea/lsp/server/formatting.rb +478 -480
  135. data/lib/milk_tea/lsp/server/hover.rb +1282 -1284
  136. data/lib/milk_tea/lsp/server/inlay_hints.rb +179 -181
  137. data/lib/milk_tea/lsp/server/lifecycle.rb +192 -194
  138. data/lib/milk_tea/lsp/server/linked_editing_range.rb +0 -2
  139. data/lib/milk_tea/lsp/server/on_type_formatting.rb +0 -2
  140. data/lib/milk_tea/lsp/server/progress.rb +0 -2
  141. data/lib/milk_tea/lsp/server/references.rb +316 -318
  142. data/lib/milk_tea/lsp/server/rename.rb +445 -447
  143. data/lib/milk_tea/lsp/server/selection_range.rb +0 -4
  144. data/lib/milk_tea/lsp/server/semantic_tokens.rb +1318 -1320
  145. data/lib/milk_tea/lsp/server/signature_help.rb +146 -148
  146. data/lib/milk_tea/lsp/server/text_documents.rb +99 -101
  147. data/lib/milk_tea/lsp/server/type_hierarchy.rb +2 -4
  148. data/lib/milk_tea/lsp/server/utilities.rb +397 -399
  149. data/lib/milk_tea/lsp/server.rb +0 -3
  150. data/lib/milk_tea/lsp/workspace/analysis.rb +40 -42
  151. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +4 -10
  152. data/lib/milk_tea/lsp/workspace/utilities.rb +19 -19
  153. data/lib/milk_tea/packages/atomic_write.rb +4 -5
  154. data/lib/milk_tea/packages/dependency_solver.rb +15 -15
  155. data/lib/milk_tea/packages/lock.rb +4 -4
  156. data/lib/milk_tea/packages/manager_cli.rb +1 -1
  157. data/lib/milk_tea/packages/manifest.rb +3 -3
  158. data/lib/milk_tea/packages/registry_store.rb +4 -6
  159. data/lib/milk_tea/packages/services.rb +16 -16
  160. data/lib/milk_tea/packages/source_cache.rb +4 -6
  161. data/lib/milk_tea/packages/source_resolver.rb +44 -44
  162. data/lib/milk_tea/packages/version.rb +8 -8
  163. data/lib/milk_tea/tooling/build.rb +3 -7
  164. data/lib/milk_tea/tooling/build_cache.rb +0 -2
  165. data/lib/milk_tea/tooling/debug_info_formatter.rb +31 -33
  166. data/lib/milk_tea/tooling/debug_map.rb +8 -10
  167. data/lib/milk_tea/tooling/docs_app.rb +0 -2
  168. data/lib/milk_tea/tooling/linter/doc_tags.rb +25 -27
  169. data/lib/milk_tea/tooling/linter/fix_engine.rb +87 -24
  170. data/lib/milk_tea/tooling/linter/flow_rules.rb +44 -46
  171. data/lib/milk_tea/tooling/linter/imports_platform.rb +14 -16
  172. data/lib/milk_tea/tooling/linter/release_rules.rb +3 -5
  173. data/lib/milk_tea/tooling/linter/reserved_names.rb +23 -25
  174. data/lib/milk_tea/tooling/linter/rules.rb +87 -89
  175. data/lib/milk_tea/tooling/linter/source_helpers.rb +40 -42
  176. data/lib/milk_tea/tooling/linter/trailing_comma.rb +17 -19
  177. data/lib/milk_tea/tooling/linter/visitors.rb +162 -50
  178. data/lib/milk_tea/tooling/linter.rb +0 -2
  179. data/lib/milk_tea/tooling/project_scaffold.rb +0 -2
  180. data/lib/milk_tea/tooling/run.rb +0 -2
  181. data/lib/milk_tea/tooling/toolchain_cli.rb +0 -2
  182. data/std/cli.mt +13 -32
  183. data/std/color.mt +28 -28
  184. data/std/encoding.mt +4 -4
  185. data/std/graph.mt +1 -2
  186. data/std/hash.mt +14 -14
  187. data/std/http/server.mt +3 -3
  188. data/std/input.mt +1 -1
  189. data/std/json.mt +1 -1
  190. data/std/linear_algebra.mt +6 -6
  191. data/std/net/mux.mt +1 -1
  192. data/std/noise.mt +23 -23
  193. data/std/parse.mt +2 -2
  194. data/std/process.mt +3 -4
  195. data/std/sparse_set.mt +1 -1
  196. data/std/steering.mt +5 -5
  197. data/std/str.mt +1 -1
  198. data/std/tween.mt +4 -4
  199. data/std/utility.mt +3 -3
  200. metadata +2 -2
@@ -3,28 +3,26 @@
3
3
  module MilkTea
4
4
  class Linter
5
5
  module LinterFlowRules
6
- private
7
-
8
6
  def emit_dead_assignment_warnings(stmts)
9
7
  analysis = dead_assignment_analysis(stmts)
10
8
  return unless analysis
11
-
9
+
12
10
  graph = analysis.graph
13
11
  liveness = analysis.liveness
14
12
  readable_bindings = analysis.readable_bindings
15
13
  locally_declared = analysis.locally_declared
16
-
14
+
17
15
  graph.each_node do |node|
18
16
  node.writes_info.each do |write|
19
17
  next if write[:origin] == :call_argument
20
18
  next if write[:origin] == :declaration
21
-
19
+
22
20
  binding_key = write[:binding_key]
23
21
  name = write[:name]
24
22
  next unless readable_bindings.include?(binding_key)
25
23
  next unless locally_declared.include?(binding_key)
26
24
  next if liveness.live_out[node.id].include?(binding_key)
27
-
25
+
28
26
  @warnings << Warning.new(
29
27
  path: @path,
30
28
  line: write[:line],
@@ -40,17 +38,17 @@ module MilkTea
40
38
  def emit_unreachable_warnings(stmts)
41
39
  analysis = statement_flow_analysis(stmts)
42
40
  return unless analysis
43
-
41
+
44
42
  graph = analysis.graph
45
43
  reachable = analysis.reachability
46
-
44
+
47
45
  graph.each_node do |node|
48
46
  next if reachable.reachable_ids.include?(node.id)
49
47
  next if node.kind == :exit
50
48
  next unless node.statement
51
-
49
+
52
50
  statement = node.statement
53
- line = statement.respond_to?(:line) ? statement.line : nil
51
+ line = statement.line
54
52
  @warnings << Warning.new(
55
53
  path: @path,
56
54
  line:,
@@ -65,10 +63,10 @@ module MilkTea
65
63
  # is taken from a local variable that is also written later in the same body.
66
64
  def emit_borrow_warnings(stmts)
67
65
  return if stmts.nil? || stmts.empty?
68
-
66
+
69
67
  borrowed = collect_borrowed_names(stmts)
70
68
  return if borrowed.empty?
71
-
69
+
72
70
  written = collect_written_names(stmts)
73
71
  (borrowed & written).each do |name|
74
72
  # Find the earliest borrow site for the warning location
@@ -87,7 +85,7 @@ module MilkTea
87
85
  end
88
86
  def cfg_binding_resolution
89
87
  return @cfg_binding_resolution if @cfg_binding_resolution_computed
90
-
88
+
91
89
  binding_resolution = @sema_facts&.binding_resolution
92
90
  @cfg_binding_resolution = if binding_resolution
93
91
  ControlFlow::BindingResolution.new(
@@ -101,7 +99,7 @@ module MilkTea
101
99
  end
102
100
  def statement_flow_analysis(stmts)
103
101
  return nil if stmts.nil? || stmts.empty?
104
-
102
+
105
103
  @statement_flow_analysis_cache[stmts.object_id] ||= begin
106
104
  binding_resolution = cfg_binding_resolution
107
105
  graph = profile_phase("flow.graph") do
@@ -118,7 +116,7 @@ module MilkTea
118
116
  end
119
117
  def dead_assignment_analysis(stmts)
120
118
  return nil if stmts.nil? || stmts.empty?
121
-
119
+
122
120
  @dead_assignment_analysis_cache[stmts.object_id] ||= begin
123
121
  binding_resolution = cfg_binding_resolution
124
122
  graph = profile_phase("dead_assignment.graph") do
@@ -147,7 +145,7 @@ module MilkTea
147
145
  def cfg_identifier_binding_key(identifier)
148
146
  binding_resolution = cfg_binding_resolution
149
147
  return identifier.name unless binding_resolution
150
-
148
+
151
149
  binding_resolution.identifier_binding_ids[identifier.object_id] || identifier.name
152
150
  end
153
151
  def ignored_binding_name?(name)
@@ -159,18 +157,18 @@ module MilkTea
159
157
  # Uses ConstantPropagation to detect conditions that are always true/false.
160
158
  # Skips `while true` — it is an idiomatic infinite loop.
161
159
  # Skips if conditions inside loops, since variables can change across iterations.
162
-
160
+
163
161
  def emit_constant_condition_warnings(stmts)
164
162
  return if stmts.nil? || stmts.empty?
165
-
163
+
166
164
  analysis = statement_flow_analysis(stmts)
167
165
  return unless analysis
168
-
166
+
169
167
  binding_resolution = cfg_binding_resolution
170
168
  graph = analysis.graph
171
169
  cp = analysis.constant_propagation
172
170
  loop_bodies = analysis.loop_body_nodes
173
-
171
+
174
172
  graph.each_node do |node|
175
173
  cond_expr, line, keyword_pattern, skip_node =
176
174
  case node.kind
@@ -188,9 +186,9 @@ module MilkTea
188
186
  else
189
187
  next
190
188
  end
191
-
189
+
192
190
  next if skip_node || cond_expr.nil?
193
-
191
+
194
192
  in_state = cp.in_states[node.id] || {}
195
193
  const_val = ControlFlow::ConstantPropagation.constant_value_of(
196
194
  cond_expr,
@@ -199,7 +197,7 @@ module MilkTea
199
197
  strict_binding_ids: !binding_resolution.nil?
200
198
  )
201
199
  next unless const_val == true || const_val == false
202
-
200
+
203
201
  ctx = node.kind == :while_condition ? "loop condition" : "branch condition"
204
202
  line, column, length = condition_span(cond_expr, line:, keyword_pattern:)
205
203
  @warnings << Warning.new(
@@ -218,7 +216,7 @@ module MilkTea
218
216
  # A back-edge exists when a node is reachable from its own successors.
219
217
  private def compute_loop_body_nodes(graph)
220
218
  loop_nodes = Set.new
221
-
219
+
222
220
  # Find all back-edges: detect cycles by checking if any successor of a node
223
221
  # can reach back to that node.
224
222
  graph.each_node do |node|
@@ -230,38 +228,38 @@ module MilkTea
230
228
  end
231
229
  end
232
230
  end
233
-
231
+
234
232
  loop_nodes
235
233
  end
236
-
234
+
237
235
  # Returns true if target_id is reachable from start_id via forward edges.
238
236
  private def reachable_from?(graph, start_id, target_id)
239
237
  visited = Set.new
240
238
  queue = [start_id]
241
-
239
+
242
240
  while queue.any?
243
241
  node_id = queue.shift
244
242
  return true if node_id == target_id
245
243
  next if visited.include?(node_id)
246
-
244
+
247
245
  visited.add(node_id)
248
246
  node = graph.nodes[node_id]
249
247
  node.succs.each { |succ| queue.push(succ) }
250
248
  end
251
-
249
+
252
250
  false
253
251
  end
254
-
252
+
255
253
  # Mark all nodes reachable from start_id as being inside a loop.
256
254
  private def mark_reachable_nodes(graph, start_id, loop_nodes)
257
255
  visited = Set.new
258
256
  queue = [start_id]
259
-
257
+
260
258
  while queue.any?
261
259
  node_id = queue.shift
262
260
  loop_nodes.add(node_id)
263
261
  next if visited.include?(node_id)
264
-
262
+
265
263
  visited.add(node_id)
266
264
  node = graph.nodes[node_id]
267
265
  node.succs.each { |succ| queue.push(succ) unless visited.include?(succ) }
@@ -270,31 +268,31 @@ module MilkTea
270
268
  # ── redundant-null-check ───────────────────────────────────────────────
271
269
  # After a variable has been narrowed to non-null by a prior check,
272
270
  # a subsequent `x != nil` guard is always true.
273
-
271
+
274
272
  def emit_redundant_null_check_warnings(stmts)
275
273
  return if stmts.nil? || stmts.empty?
276
-
274
+
277
275
  analysis = statement_flow_analysis(stmts)
278
276
  return unless analysis
279
-
277
+
280
278
  graph = analysis.graph
281
279
  nf = analysis.nullability
282
-
280
+
283
281
  graph.each_node do |node|
284
282
  next unless node.kind == :if_condition
285
-
283
+
286
284
  branch = node.statement
287
285
  next unless branch.is_a?(AST::IfBranch)
288
-
286
+
289
287
  identifier = null_check_identifier(branch.condition)
290
288
  next unless identifier
291
289
  next if ignored_binding_name?(identifier.name)
292
-
290
+
293
291
  nonnull = nf.nonnull_before(branch)
294
292
  next unless nonnull.include?(cfg_identifier_binding_key(identifier))
295
-
293
+
296
294
  line, column, length = condition_span(branch.condition, line: node.line, keyword_pattern: "else if|if")
297
-
295
+
298
296
  @warnings << Warning.new(
299
297
  path: @path,
300
298
  line:,
@@ -307,12 +305,12 @@ module MilkTea
307
305
  )
308
306
  end
309
307
  end
310
-
308
+
311
309
  # Returns the Identifier being nil-tested if `cond` is `x != nil` or
312
310
  # `nil != x`, otherwise nil.
313
311
  def null_check_identifier(cond)
314
312
  return nil unless cond.is_a?(AST::BinaryOp) && cond.operator == "!="
315
-
313
+
316
314
  if cond.left.is_a?(AST::Identifier) && cond.right.is_a?(AST::NullLiteral)
317
315
  cond.left
318
316
  elsif cond.left.is_a?(AST::NullLiteral) && cond.right.is_a?(AST::Identifier)
@@ -322,13 +320,13 @@ module MilkTea
322
320
  # ── loop-single-iteration ──────────────────────────────────────────────
323
321
  # A loop whose body unconditionally exits (return/break) before the
324
322
  # back-edge is taken will execute at most once.
325
-
323
+
326
324
  def emit_loop_single_iteration_warnings(stmts)
327
325
  return if stmts.nil? || stmts.empty?
328
-
326
+
329
327
  walk_stmts_for_loop_check(stmts)
330
328
  end
331
-
329
+
332
330
  def walk_stmts_for_loop_check(stmts)
333
331
  stmts.each do |stmt|
334
332
  case stmt
@@ -3,8 +3,6 @@
3
3
  module MilkTea
4
4
  class Linter
5
5
  module LinterImportsPlatform
6
- private
7
-
8
6
  # ── unused-import ────────────────────────────────────────────────────
9
7
 
10
8
  def check_unused_imports(source_file)
@@ -73,7 +71,7 @@ module MilkTea
73
71
  end
74
72
  return first_export_site if first_export_site
75
73
 
76
- first_declaration = source_file.declarations.find { |declaration| declaration.respond_to?(:line) && declaration.line }
74
+ first_declaration = source_file.declarations.find { |declaration| declaration.line }
77
75
  return declaration_anchor(first_declaration) if first_declaration
78
76
 
79
77
  { line: source_file.line || 1, column: 1, length: 1, symbol_name: nil }
@@ -128,9 +126,9 @@ module MilkTea
128
126
 
129
127
  symbol_name = declaration.respond_to?(:name) ? declaration.name : nil
130
128
  length = symbol_name.to_s.empty? ? 1 : symbol_name.to_s.length
131
- line = declaration.respond_to?(:line) && declaration.line ? declaration.line : 1
129
+ line = declaration.line ? declaration.line : 1
132
130
 
133
- if declaration.respond_to?(:column) && declaration.column
131
+ if declaration.column
134
132
  return { line:, column: declaration.column, length:, symbol_name: }
135
133
  end
136
134
 
@@ -314,17 +312,17 @@ module MilkTea
314
312
  unless attribute.arguments.empty?
315
313
  rendered_arguments = attribute.arguments.map do |argument|
316
314
  value = case argument.value
317
- when AST::IntegerLiteral, AST::FloatLiteral
318
- argument.value.lexeme
319
- when AST::StringLiteral
320
- argument.value.lexeme
321
- when AST::Identifier
322
- argument.value.name
323
- when AST::MemberAccess
324
- "#{argument.value.receiver.name}.#{argument.value.member}"
325
- else
326
- "..."
327
- end
315
+ when AST::IntegerLiteral, AST::FloatLiteral
316
+ argument.value.lexeme
317
+ when AST::StringLiteral
318
+ argument.value.lexeme
319
+ when AST::Identifier
320
+ argument.value.name
321
+ when AST::MemberAccess
322
+ "#{argument.value.receiver.name}.#{argument.value.member}"
323
+ else
324
+ "..."
325
+ end
328
326
  argument.name ? "#{argument.name} = #{value}" : value
329
327
  end
330
328
  text << "(#{rendered_arguments.join(', ')})"
@@ -3,8 +3,6 @@
3
3
  module MilkTea
4
4
  class Linter
5
5
  module LinterReleaseRules
6
- private
7
-
8
6
  # Entry point — called from visit_function and proc expr in full_tier mode.
9
7
  def emit_owning_release_warnings(function_or_body)
10
8
  body = function_or_body.is_a?(Array) ? function_or_body : function_or_body.body
@@ -83,7 +81,7 @@ module MilkTea
83
81
  if type_name
84
82
  result[stmt.name] = {
85
83
  line: stmt.line,
86
- column: stmt.respond_to?(:column) ? stmt.column : nil,
84
+ column: stmt.column,
87
85
  type_name: type_name,
88
86
  }
89
87
  end
@@ -289,7 +287,7 @@ module MilkTea
289
287
  if type_name
290
288
  result[stmt.name] = {
291
289
  line: stmt.line,
292
- column: stmt.respond_to?(:column) ? stmt.column : nil,
290
+ column: stmt.column,
293
291
  type_name: type_name,
294
292
  }
295
293
  end
@@ -725,7 +723,7 @@ module MilkTea
725
723
  end
726
724
 
727
725
  def extract_line(stmt)
728
- stmt.respond_to?(:line) ? stmt.line : nil
726
+ stmt.line
729
727
  end
730
728
 
731
729
  def owning_base_name(qname)
@@ -3,8 +3,6 @@
3
3
  module MilkTea
4
4
  class Linter
5
5
  module LinterReservedNames
6
- private
7
-
8
6
  def declare_reserved_value_type_module_binding(name, kind_label:, line:, column:, unavailable_names:)
9
7
  declare_reserved_module_binding(
10
8
  name,
@@ -15,7 +13,7 @@ module MilkTea
15
13
  reserved_names: RESERVED_VALUE_TYPE_NAMES,
16
14
  )
17
15
  end
18
-
16
+
19
17
  def declare_reserved_import_alias_module_binding(name, kind_label:, line:, column:, unavailable_names:)
20
18
  declare_reserved_module_binding(
21
19
  name,
@@ -26,7 +24,7 @@ module MilkTea
26
24
  reserved_names: RESERVED_IMPORT_ALIAS_NAMES,
27
25
  )
28
26
  end
29
-
27
+
30
28
  def declare_reserved_module_binding(name, kind_label:, line:, column:, unavailable_names:, reserved_names:)
31
29
  replacement_name = nil
32
30
  replacement_base_name = nil
@@ -37,7 +35,7 @@ module MilkTea
37
35
  unavailable_names,
38
36
  )
39
37
  end
40
-
38
+
41
39
  binding = Binding.new(
42
40
  name:,
43
41
  line:,
@@ -52,13 +50,13 @@ module MilkTea
52
50
  register_reserved_primitive_name_fix(binding, kind_label:, replacement_name:) if replacement_name
53
51
  @module_bindings[name] = binding
54
52
  end
55
-
53
+
56
54
  def declare_reserved_primitive_module_binding(name, kind_label:, line:, column:, unavailable_names:)
57
55
  declare_reserved_value_type_module_binding(name, kind_label:, line:, column:, unavailable_names:)
58
56
  end
59
57
  def warn_reserved_primitive_name(name, line:, column:, kind_label:, reserved_names: RESERVED_VALUE_TYPE_NAMES)
60
58
  return unless reserved_names.include?(name)
61
-
59
+
62
60
  @warnings << Warning.new(
63
61
  path: @path,
64
62
  line:,
@@ -70,10 +68,10 @@ module MilkTea
70
68
  symbol_name: name,
71
69
  )
72
70
  end
73
-
71
+
74
72
  def warn_large_event_capacity(event_decl, owner_name:)
75
73
  label = owner_name ? "#{owner_name}.#{event_decl.name}" : event_decl.name
76
-
74
+
77
75
  @warnings << Warning.new(
78
76
  path: @path,
79
77
  line: event_decl.line,
@@ -85,16 +83,16 @@ module MilkTea
85
83
  symbol_name: event_decl.name,
86
84
  )
87
85
  end
88
-
86
+
89
87
  def warn_redundant_ignored_match_binding(name, line:, column:)
90
88
  return unless name == "_"
91
-
89
+
92
90
  span = nil
93
91
  if line && column
94
92
  source_line = @source_lines[line - 1].to_s
95
93
  span = self.class.redundant_ignored_match_binding_span(source_line, column:)
96
94
  end
97
-
95
+
98
96
  @warnings << Warning.new(
99
97
  path: @path,
100
98
  line:,
@@ -105,7 +103,7 @@ module MilkTea
105
103
  severity: :hint,
106
104
  )
107
105
  end
108
-
106
+
109
107
  def warn_reserved_primitive_type_params(type_params, kind_label:)
110
108
  Array(type_params).each do |type_param|
111
109
  warn_reserved_primitive_name(
@@ -120,7 +118,7 @@ module MilkTea
120
118
  def register_reserved_primitive_name_fix(binding, kind_label:, replacement_name:)
121
119
  warn_reserved_primitive_name(binding.name, line: binding.line, column: binding.column, kind_label:)
122
120
  return unless binding.line && binding.column
123
-
121
+
124
122
  binding.fix_index = @reserved_primitive_name_fixes.length
125
123
  @reserved_primitive_name_fixes << ReservedPrimitiveNameFix.new(
126
124
  kind: kind_label,
@@ -129,7 +127,7 @@ module MilkTea
129
127
  sites: [ReservedPrimitiveNameSite.new(line: binding.line, column: binding.column, length: binding.name.length)],
130
128
  )
131
129
  end
132
-
130
+
133
131
  def suggested_reserved_primitive_name(name, kind_label:)
134
132
  case kind_label
135
133
  when "function"
@@ -140,20 +138,20 @@ module MilkTea
140
138
  "#{name}_value"
141
139
  end
142
140
  end
143
-
141
+
144
142
  def next_available_reserved_primitive_name(base_name, unavailable_names)
145
143
  unavailable = unavailable_names.to_set
146
144
  return base_name unless unavailable.include?(base_name)
147
-
145
+
148
146
  suffix = 2
149
147
  loop do
150
148
  candidate = "#{base_name}_#{suffix}"
151
149
  return candidate unless unavailable.include?(candidate)
152
-
150
+
153
151
  suffix += 1
154
152
  end
155
153
  end
156
-
154
+
157
155
  def visible_binding_names(excluding_name: nil)
158
156
  names = visible_bindings.each_with_object(Set.new) do |binding, result|
159
157
  result << binding.name
@@ -162,24 +160,24 @@ module MilkTea
162
160
  names.delete(excluding_name) if excluding_name
163
161
  names
164
162
  end
165
-
163
+
166
164
  def visible_bindings
167
165
  @module_bindings.values + @scopes.flat_map(&:values)
168
166
  end
169
-
167
+
170
168
  def resolve_reserved_primitive_name_conflicts!(declared_name)
171
169
  visible_bindings.each do |binding|
172
170
  next unless binding.replacement_name == declared_name
173
-
171
+
174
172
  replacement_name = next_available_reserved_primitive_name(
175
173
  binding.replacement_base_name || binding.replacement_name,
176
174
  visible_binding_names(excluding_name: binding.name),
177
175
  )
178
176
  next if replacement_name == binding.replacement_name
179
-
177
+
180
178
  binding.replacement_name = replacement_name
181
179
  next unless binding.fix_index
182
-
180
+
183
181
  fix = @reserved_primitive_name_fixes[binding.fix_index]
184
182
  @reserved_primitive_name_fixes[binding.fix_index] = fix.with(replacement_name:)
185
183
  end
@@ -187,7 +185,7 @@ module MilkTea
187
185
  def record_reserved_primitive_identifier_use(binding, identifier)
188
186
  return unless binding.fix_index
189
187
  return unless identifier&.line && identifier&.column
190
-
188
+
191
189
  @reserved_primitive_name_fixes[binding.fix_index].sites << ReservedPrimitiveNameSite.new(
192
190
  line: identifier.line,
193
191
  column: identifier.column,