mt-lang 0.3.9 → 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 +8 -9
  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 +0 -2
  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 +112 -54
  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,8 +3,6 @@
3
3
  module MilkTea
4
4
  class Linter
5
5
  module LinterVisitors
6
- private
7
-
8
6
  def visit_source_file(source_file)
9
7
  @declared_callable_names = declared_callable_names(source_file)
10
8
  @declared_directional_functions = declared_directional_functions(source_file)
@@ -29,14 +27,14 @@ module MilkTea
29
27
  end
30
28
  def seed_module_bindings(source_file)
31
29
  @module_bindings = {}
32
-
30
+
33
31
  import_names = source_file.imports.filter_map do |import|
34
32
  import.alias_name || import.path.parts.last
35
33
  end
36
34
  source_file.imports.each do |import|
37
35
  local_name = import.alias_name || import.path.parts.last
38
36
  next if ignored_binding_name?(local_name)
39
-
37
+
40
38
  declare_reserved_import_alias_module_binding(
41
39
  local_name,
42
40
  kind_label: "import alias",
@@ -45,7 +43,7 @@ module MilkTea
45
43
  unavailable_names: import_names,
46
44
  )
47
45
  end
48
-
46
+
49
47
  source_file.declarations.each do |declaration|
50
48
  kind_label = case declaration
51
49
  when AST::FunctionDef, AST::ExternFunctionDecl, AST::ForeignFunctionDecl
@@ -61,7 +59,7 @@ module MilkTea
61
59
  end
62
60
  next unless kind_label
63
61
  next if ignored_binding_name?(declaration.name)
64
-
62
+
65
63
  declare_reserved_primitive_module_binding(
66
64
  declaration.name,
67
65
  kind_label:,
@@ -87,7 +85,7 @@ module MilkTea
87
85
  source_file.declarations.each_with_object({}) do |declaration, functions|
88
86
  next unless declaration.is_a?(AST::ExternFunctionDecl) || declaration.is_a?(AST::ForeignFunctionDecl)
89
87
  next unless declaration.params.any? { |param| %i[in out inout].include?(param.mode) }
90
-
88
+
91
89
  functions[declaration.name] = declaration
92
90
  end
93
91
  end
@@ -134,7 +132,7 @@ module MilkTea
134
132
  terminated = false
135
133
  stmts.each do |stmt|
136
134
  next if terminated # skip visitation only; ControlFlow emits the warning
137
-
135
+
138
136
  visit_statement(stmt)
139
137
  terminated = true if terminator?(stmt)
140
138
  end
@@ -299,7 +297,7 @@ module MilkTea
299
297
  @unsafe_depth -= 1
300
298
  when AST::ProcExpr
301
299
  with_scope do
302
- fallback_line = expression.respond_to?(:line) ? expression.line : nil
300
+ fallback_line = expression.line
303
301
  expression.params.each do |param|
304
302
  declare_param(
305
303
  param.name,
@@ -325,7 +323,7 @@ module MilkTea
325
323
  when AST::FormatString
326
324
  expression.parts.each do |part|
327
325
  next unless part.is_a?(AST::FormatExprPart)
328
-
326
+
329
327
  visit_expression(part.expression)
330
328
  end
331
329
  when AST::IntegerLiteral, AST::FloatLiteral, AST::StringLiteral, AST::BooleanLiteral, AST::NullLiteral
@@ -337,6 +335,7 @@ module MilkTea
337
335
  when AST::PrefixCast
338
336
  visit_expression(expression.expression)
339
337
  check_redundant_cast(expression)
338
+ check_redundant_cast_parens(expression)
340
339
  else
341
340
  nil
342
341
  end
@@ -369,28 +368,28 @@ module MilkTea
369
368
  end
370
369
  def mark_assignment_target_reads(target, operator)
371
370
  return if operator == "="
372
-
371
+
373
372
  mark_used(target.name, identifier: target) if target.is_a?(AST::Identifier)
374
373
  end
375
374
  def mark_mutated(target)
376
375
  return unless target.is_a?(AST::Identifier) || target.is_a?(AST::IndexAccess) || target.is_a?(AST::MemberAccess)
377
-
376
+
378
377
  # For direct identifier assignment (e.g., x = value), mark x as mutated.
379
378
  if target.is_a?(AST::Identifier)
380
379
  @scopes.reverse_each do |scope|
381
380
  binding = scope[target.name]
382
381
  next unless binding
383
-
382
+
384
383
  binding.mutated = true
385
384
  return
386
385
  end
387
386
  end
388
-
387
+
389
388
  # For index assignment (e.g., array[0] = value), mark the array as mutated.
390
389
  if target.is_a?(AST::IndexAccess)
391
390
  mark_mutated(target.receiver)
392
391
  end
393
-
392
+
394
393
  # For field assignment (e.g., rect.w = value), mark the struct variable as mutated.
395
394
  if target.is_a?(AST::MemberAccess)
396
395
  mark_mutated(target.receiver)
@@ -401,12 +400,12 @@ module MilkTea
401
400
  mark_mutated(expression)
402
401
  return
403
402
  end
404
-
403
+
405
404
  if expression.is_a?(AST::UnaryOp) && %w[out inout].include?(expression.operator)
406
405
  mark_mutated(expression.operand)
407
406
  return
408
407
  end
409
-
408
+
410
409
  # ref_of(x) and ptr_of(x) can expose writable aliases — treat as potential
411
410
  # mutation since callees may write through them (common C-FFI out-param pattern).
412
411
  if expression.is_a?(AST::Call) &&
@@ -418,10 +417,10 @@ module MilkTea
418
417
  end
419
418
  def mutating_argument_identifier?(expression)
420
419
  return false unless expression.is_a?(AST::Identifier)
421
-
420
+
422
421
  binding_resolution = @sema_facts&.binding_resolution
423
422
  return false unless binding_resolution
424
-
423
+
425
424
  binding_resolution.mutating_argument_identifier_ids&.key?(expression.object_id) ||
426
425
  binding_resolution.mutable_lvalue_argument_identifier_ids&.key?(expression.object_id)
427
426
  end
@@ -429,7 +428,7 @@ module MilkTea
429
428
  return unless expression.is_a?(AST::Call)
430
429
  return unless expression.callee.is_a?(AST::MemberAccess)
431
430
  return unless editable_receiver_expression?(expression.callee.receiver)
432
-
431
+
433
432
  mark_mutated(expression.callee.receiver)
434
433
  end
435
434
  def mark_alias_source_mutated(expression)
@@ -437,12 +436,12 @@ module MilkTea
437
436
  return unless expression.callee.is_a?(AST::Identifier)
438
437
  return unless %w[ref_of ptr_of].include?(expression.callee.name)
439
438
  return unless expression.arguments.length == 1
440
-
439
+
441
440
  mark_mutated(expression.arguments.first.value)
442
441
  end
443
442
  def editable_receiver_expression?(expression)
444
443
  return true unless @sema_facts
445
-
444
+
446
445
  @sema_facts&.binding_resolution&.editable_receiver_expression_ids&.key?(expression.object_id)
447
446
  end
448
447
  def with_scope
@@ -453,9 +452,9 @@ module MilkTea
453
452
  end
454
453
  def declare_local(name, line, column: nil, var: false)
455
454
  return if ignored_binding_name?(name)
456
-
455
+
457
456
  resolve_reserved_primitive_name_conflicts!(name)
458
-
457
+
459
458
  replacement_name = nil
460
459
  replacement_base_name = nil
461
460
  if RESERVED_VALUE_TYPE_NAMES.include?(name)
@@ -465,7 +464,7 @@ module MilkTea
465
464
  visible_binding_names(excluding_name: name),
466
465
  )
467
466
  end
468
-
467
+
469
468
  # shadow: check whether any outer scope already has a binding for this name
470
469
  if @scopes.length > 1
471
470
  @scopes[0..-2].each do |outer_scope|
@@ -479,7 +478,7 @@ module MilkTea
479
478
  end
480
479
  end
481
480
  end
482
-
481
+
483
482
  @scopes.last[name] = Binding.new(
484
483
  name:, line:, column:, used: false,
485
484
  binding_kind: :local,
@@ -492,9 +491,9 @@ module MilkTea
492
491
  end
493
492
  def declare_param(name, line: nil, column: nil)
494
493
  return if ignored_binding_name?(name)
495
-
494
+
496
495
  resolve_reserved_primitive_name_conflicts!(name)
497
-
496
+
498
497
  replacement_name = nil
499
498
  replacement_base_name = nil
500
499
  if RESERVED_VALUE_TYPE_NAMES.include?(name)
@@ -504,7 +503,7 @@ module MilkTea
504
503
  visible_binding_names(excluding_name: name),
505
504
  )
506
505
  end
507
-
506
+
508
507
  @scopes.last[name] = Binding.new(
509
508
  name:, line:, column:, used: false,
510
509
  binding_kind: :param,
@@ -801,8 +800,8 @@ module MilkTea
801
800
  cond_src = source_line_from(b.line, b.column)
802
801
  return false unless cond_src
803
802
 
804
- stmt_col = stmts[i].respond_to?(:column) ? stmts[i].column : nil
805
- stmt_line = stmts[i].respond_to?(:line) ? stmts[i].line : nil
803
+ stmt_col = stmts[i]&.column
804
+ stmt_line = stmts[i]&.line
806
805
  body_src = source_line_from(stmt_line, stmt_col)
807
806
  return false unless body_src
808
807
 
@@ -819,8 +818,8 @@ module MilkTea
819
818
  text << " "
820
819
  end
821
820
 
822
- stmt_col = stmts.last.respond_to?(:column) ? stmts.last.column : nil
823
- stmt_line = stmts.last.respond_to?(:line) ? stmts.last.line : nil
821
+ stmt_col = stmts.last&.column
822
+ stmt_line = stmts.last&.line
824
823
  else_body = source_line_from(stmt_line, stmt_col)
825
824
  return false unless else_body
826
825
  text << else_body
@@ -840,9 +839,9 @@ module MilkTea
840
839
  def visually_inline_if?(statement, stmts)
841
840
  n = statement.branches.length
842
841
  statement.branches.each_with_index.all? { |b, i|
843
- stmts[i].respond_to?(:line) && stmts[i].line == b.line
842
+ stmts[i].line == b.line
844
843
  } && (
845
- stmts[n].respond_to?(:line) && statement.else_line && stmts[n].line == statement.else_line
844
+ stmts[n].line && statement.else_line && stmts[n].line == statement.else_line
846
845
  )
847
846
  end
848
847
 
@@ -897,8 +896,8 @@ module MilkTea
897
896
  next unless node_fingerprint(body_of.call(first)) == node_fingerprint(body_of.call(second))
898
897
 
899
898
  pattern = second.pattern
900
- pattern_line = pattern.respond_to?(:line) ? pattern.line : second.binding_line
901
- pattern_column = pattern.respond_to?(:column) ? pattern.column : second.binding_column
899
+ pattern_line = pattern.line || second.binding_line
900
+ pattern_column = pattern.column || second.binding_column
902
901
  emit_conciseness_hint(
903
902
  "prefer-or-pattern",
904
903
  line: pattern_line,
@@ -935,8 +934,8 @@ module MilkTea
935
934
  source_text = expr_source_name(copied.first.value.receiver)
936
935
  emit_conciseness_hint(
937
936
  "prefer-struct-with",
938
- line: call.callee.respond_to?(:line) ? call.callee.line : nil,
939
- column: call.callee.respond_to?(:column) ? call.callee.column : nil,
937
+ line: call.callee.line,
938
+ column: call.callee.column,
940
939
  message: "copies #{copied.size} field(s) from `#{source_text}`; use `#{source_text}.with(#{changed_names.join(", ")} = …)`",
941
940
  )
942
941
  end
@@ -968,8 +967,8 @@ module MilkTea
968
967
 
969
968
  emit_conciseness_hint(
970
969
  "prefer-try",
971
- line: scrutinee.respond_to?(:line) ? scrutinee.line : nil,
972
- column: scrutinee.respond_to?(:column) ? scrutinee.column : nil,
970
+ line: scrutinee.line,
971
+ column: scrutinee.column,
973
972
  message: "this #{base} match only propagates the failure branch; consider `expr?`",
974
973
  )
975
974
  end
@@ -1045,6 +1044,65 @@ module MilkTea
1045
1044
  # check_boundary_widening_cast at the specific boundary sites.
1046
1045
  end
1047
1046
 
1047
+ # ── redundant cast parentheses ────────────────────────────────────────────────────
1048
+ # `T<-(x)` when `T<-x` would be equivalent because the inner expression needs
1049
+ # no precedence protection. Parens are required when the inner expression
1050
+ # contains a binary operator, range, or inline conditional.
1051
+
1052
+ def check_redundant_cast_parens(cast)
1053
+
1054
+ line_idx = Integer(cast.line) - 1
1055
+ return if line_idx < 0 || line_idx >= @source_lines.length
1056
+
1057
+ line = @source_lines[line_idx].to_s
1058
+ col = Integer(cast.column) - 1
1059
+ arrow = line.index("<-", col)
1060
+ return unless arrow
1061
+ arrow_end = arrow + 2
1062
+ return if arrow_end >= line.length
1063
+ return unless line[arrow_end] == "("
1064
+
1065
+ close = match_paren(line, arrow_end)
1066
+ return unless close
1067
+
1068
+ inner = line[arrow_end + 1...close].strip
1069
+ return if inner.empty?
1070
+ return if inner_has_top_level_operators?(inner)
1071
+
1072
+ emit_redundant_cast(cast, type_ref_name(cast.target_type) || "",
1073
+ "parentheses around cast expression are redundant")
1074
+ end
1075
+
1076
+ def match_paren(line, open_pos)
1077
+ depth = 0
1078
+ (open_pos...line.length).each do |i|
1079
+ case line[i]
1080
+ when "(" then depth += 1
1081
+ when ")" then depth -= 1; return i if depth == 0
1082
+ end
1083
+ end
1084
+ nil
1085
+ end
1086
+
1087
+ BINARY_OP_CHARS = %w[+ - * / % & | ^ < > = ! ? :]
1088
+
1089
+ def inner_has_top_level_operators?(inner)
1090
+ depth = 0
1091
+ i = 0
1092
+ while i < inner.length
1093
+ case inner[i]
1094
+ when "(", "[", "{"
1095
+ depth += 1
1096
+ when ")", "]", "}"
1097
+ depth -= 1 if depth > 0
1098
+ when *BINARY_OP_CHARS
1099
+ return true if depth == 0
1100
+ end
1101
+ i += 1
1102
+ end
1103
+ false
1104
+ end
1105
+
1048
1106
  # Reports a widening cast as redundant only when it sits *directly* at a
1049
1107
  # coercion slot: the RHS of a typed `let`/`var`, a `return` value, or the
1050
1108
  # RHS of a plain `=` assignment. At those positions the slot type is
@@ -1088,8 +1146,8 @@ module MilkTea
1088
1146
  end
1089
1147
 
1090
1148
  def emit_redundant_cast(cast_expr, target_name, reason)
1091
- line = cast_expr.respond_to?(:line) ? cast_expr.line : expression_line(cast_expr)
1092
- column = cast_expr.respond_to?(:column) ? cast_expr.column : expression_column(cast_expr)
1149
+ line = cast_expr.line || expression_line(cast_expr)
1150
+ column = cast_expr.column || expression_column(cast_expr)
1093
1151
 
1094
1152
  @warnings << Warning.new(
1095
1153
  path: @path,
@@ -1146,15 +1204,15 @@ module MilkTea
1146
1204
  end
1147
1205
  end
1148
1206
  # ── borrow facts helpers ──────────────────────────────────────────────────
1149
-
1207
+
1150
1208
  BORROW_CALL_NAMES = %w[ref_of ptr_of].freeze
1151
-
1209
+
1152
1210
  def collect_borrowed_names(stmts)
1153
1211
  names = Set.new
1154
1212
  stmts.each { |s| collect_borrows_from_stmt(s, names) }
1155
1213
  names
1156
1214
  end
1157
-
1215
+
1158
1216
  def collect_borrows_from_stmt(stmt, names)
1159
1217
  case stmt
1160
1218
  when AST::LocalDecl
@@ -1190,7 +1248,7 @@ module MilkTea
1190
1248
  stmt.else_body&.each { |s| collect_borrows_from_stmt(s, names) }
1191
1249
  end
1192
1250
  end
1193
-
1251
+
1194
1252
  def collect_borrows_from_expr(expr, names, inside_call_argument: false)
1195
1253
  case expr
1196
1254
  when nil then nil
@@ -1222,13 +1280,13 @@ module MilkTea
1222
1280
  collect_borrows_from_expr(expr.index, names, inside_call_argument:)
1223
1281
  end
1224
1282
  end
1225
-
1283
+
1226
1284
  def collect_written_names(stmts)
1227
1285
  names = Set.new
1228
1286
  stmts.each { |s| collect_writes_from_stmt(s, names) }
1229
1287
  names
1230
1288
  end
1231
-
1289
+
1232
1290
  def collect_writes_from_stmt(stmt, names)
1233
1291
  case stmt
1234
1292
  when AST::Assignment
@@ -1252,15 +1310,15 @@ module MilkTea
1252
1310
  stmt.body&.each { |s| collect_writes_from_stmt(s, names) }
1253
1311
  end
1254
1312
  end
1255
-
1313
+
1256
1314
  def collect_writes_from_stmt_list(stmts, names)
1257
1315
  stmts.each { |s| collect_writes_from_stmt(s, names) }
1258
1316
  end
1259
-
1317
+
1260
1318
  def stmt_sub_stmts(_stmt)
1261
1319
  []
1262
1320
  end
1263
-
1321
+
1264
1322
  def find_borrow_location(stmts, name)
1265
1323
  stmts.each do |stmt|
1266
1324
  location = find_borrow_location_in_stmt(stmt, name)
@@ -1268,7 +1326,7 @@ module MilkTea
1268
1326
  end
1269
1327
  nil
1270
1328
  end
1271
-
1329
+
1272
1330
  def find_borrow_location_in_stmt(stmt, name)
1273
1331
  case stmt
1274
1332
  when AST::LocalDecl
@@ -1320,7 +1378,7 @@ module MilkTea
1320
1378
  nil
1321
1379
  end
1322
1380
  end
1323
-
1381
+
1324
1382
  def find_borrow_location_in_expr(expr, name)
1325
1383
  case expr
1326
1384
  when nil then nil
@@ -783,8 +783,6 @@ module MilkTea
783
783
  @reserved_primitive_name_fixes
784
784
  end
785
785
 
786
- private
787
-
788
786
  include LinterDocTags
789
787
  include LinterFlowRules
790
788
  include LinterImportsPlatform
@@ -31,8 +31,6 @@ module MilkTea
31
31
  )
32
32
  end
33
33
 
34
- private
35
-
36
34
  def validate_target!
37
35
  if File.exist?(@path) && !File.directory?(@path)
38
36
  raise ProjectScaffoldError, "project path is not a directory: #{@path}"
@@ -87,8 +87,6 @@ module MilkTea
87
87
  restore_signal_handlers(previous_handlers) if previous_handlers
88
88
  end
89
89
 
90
- private
91
-
92
90
  def handle_client(socket)
93
91
  request_line = socket.gets
94
92
  return unless request_line
@@ -38,8 +38,6 @@ module MilkTea
38
38
  end
39
39
  end
40
40
 
41
- private
42
-
43
41
  def extract_options!
44
42
  remaining = []
45
43
  until @argv.empty?
data/std/cli.mt CHANGED
@@ -485,11 +485,8 @@ function parse_long_option_impl(
485
485
 
486
486
  match inline_value:
487
487
  Option.some as inline_payload:
488
- match set_option_value(result, spec, inline_payload.value):
489
- Result.failure as set_error:
490
- return Result[ptr_uint, Error].failure(error= set_error.error)
491
- Result.success:
492
- return Result[ptr_uint, Error].success(value= consumed)
488
+ let _ = set_option_value(result, spec, inline_payload.value)?
489
+ return Result[ptr_uint, Error].success(value= consumed)
493
490
  Option.none:
494
491
  pass
495
492
 
@@ -497,12 +494,9 @@ function parse_long_option_impl(
497
494
  let missing = fmt.format(f"--#{spec.long_name}")
498
495
  return Result[ptr_uint, Error].failure(error= require_value_arg(missing.as_str()))
499
496
 
500
- match set_option_value(result, spec, value_arg):
501
- Result.failure as payload:
502
- return Result[ptr_uint, Error].failure(error= payload.error)
503
- Result.success:
504
- consumed = 2z
505
- return Result[ptr_uint, Error].success(value= consumed)
497
+ let _ = set_option_value(result, spec, value_arg)?
498
+ consumed = 2z
499
+ return Result[ptr_uint, Error].success(value= consumed)
506
500
 
507
501
  match inline_value:
508
502
  Option.some:
@@ -510,11 +504,8 @@ function parse_long_option_impl(
510
504
  Option.none:
511
505
  pass
512
506
 
513
- match set_option_flag(result, spec):
514
- Result.failure as payload:
515
- return Result[ptr_uint, Error].failure(error= payload.error)
516
- Result.success:
517
- return Result[ptr_uint, Error].success(value= 1z)
507
+ let _ = set_option_flag(result, spec)?
508
+ return Result[ptr_uint, Error].success(value= 1z)
518
509
 
519
510
 
520
511
  function parse_short_option_impl(
@@ -537,28 +528,18 @@ function parse_short_option_impl(
537
528
 
538
529
  if short_index + 1 < arg.len:
539
530
  let value = arg.slice(short_index + 1, arg.len - short_index - 1)
540
- match set_option_value(result, spec, value):
541
- Result.failure as payload:
542
- return Result[ptr_uint, Error].failure(error= payload.error)
543
- Result.success:
544
- return Result[ptr_uint, Error].success(value= consumed)
531
+ let _ = set_option_value(result, spec, value)?
532
+ return Result[ptr_uint, Error].success(value= consumed)
545
533
 
546
534
  let value_arg = next_arg else:
547
535
  let missing = fmt.format(f"-#{short_name}")
548
536
  return Result[ptr_uint, Error].failure(error= require_value_arg(missing.as_str()))
549
537
 
550
- match set_option_value(result, spec, value_arg):
551
- Result.failure as payload:
552
- return Result[ptr_uint, Error].failure(error= payload.error)
553
- Result.success:
554
- consumed = 2z
555
- return Result[ptr_uint, Error].success(value= consumed)
538
+ let _ = set_option_value(result, spec, value_arg)?
539
+ consumed = 2z
540
+ return Result[ptr_uint, Error].success(value= consumed)
556
541
 
557
- match set_option_flag(result, spec):
558
- Result.failure as payload:
559
- return Result[ptr_uint, Error].failure(error= payload.error)
560
- Result.success:
561
- pass
542
+ let _ = set_option_flag(result, spec)?
562
543
 
563
544
  short_index += 1
564
545
 
data/std/color.mt CHANGED
@@ -50,8 +50,8 @@ function fabs(x: float) -> float:
50
50
  function fmod(x: float, m: float) -> float:
51
51
  # x - floor(x / m) * m
52
52
  let div = x / m
53
- let fl = float<-(int<-(div))
54
- if div < 0.0 and float<-(int<-(div)) != div:
53
+ let fl = float<-(int<-div)
54
+ if div < 0.0 and float<-(int<-div) != div:
55
55
  return x - (fl - 1.0) * m
56
56
  return x - fl * m
57
57
 
@@ -143,9 +143,9 @@ extending Color:
143
143
 
144
144
 
145
145
  public function to_hsl() -> (float, float, float):
146
- let rf = float<-(this.r) / 255.0
147
- let gf = float<-(this.g) / 255.0
148
- let bf = float<-(this.b) / 255.0
146
+ let rf = float<-this.r / 255.0
147
+ let gf = float<-this.g / 255.0
148
+ let bf = float<-this.b / 255.0
149
149
 
150
150
  let mx = fmax(fmax(rf, gf), bf)
151
151
  let mn = fmin(fmin(rf, gf), bf)
@@ -171,9 +171,9 @@ extending Color:
171
171
 
172
172
 
173
173
  public function to_hsv() -> (float, float, float):
174
- let rf = float<-(this.r) / 255.0
175
- let gf = float<-(this.g) / 255.0
176
- let bf = float<-(this.b) / 255.0
174
+ let rf = float<-this.r / 255.0
175
+ let gf = float<-this.g / 255.0
176
+ let bf = float<-this.b / 255.0
177
177
 
178
178
  let mx = fmax(fmax(rf, gf), bf)
179
179
  let mn = fmin(fmin(rf, gf), bf)
@@ -198,11 +198,11 @@ extending Color:
198
198
 
199
199
 
200
200
  public function lerp(target: Color, t: float) -> Color:
201
- let r = float<-(this.r) + (float<-(target.r) - float<-(this.r)) * t
202
- let g = float<-(this.g) + (float<-(target.g) - float<-(this.g)) * t
203
- let b = float<-(this.b) + (float<-(target.b) - float<-(this.b)) * t
204
- let a = float<-(this.a) + (float<-(target.a) - float<-(this.a)) * t
205
- return Color(r = ubyte<-(r), g = ubyte<-(g), b = ubyte<-(b), a = ubyte<-(a))
201
+ let r = float<-this.r + (float<-target.r - float<-this.r) * t
202
+ let g = float<-this.g + (float<-target.g - float<-this.g) * t
203
+ let b = float<-this.b + (float<-target.b - float<-this.b) * t
204
+ let a = float<-this.a + (float<-target.a - float<-this.a) * t
205
+ return Color(r = ubyte<-r, g = ubyte<-g, b = ubyte<-b, a = ubyte<-a)
206
206
 
207
207
 
208
208
  public function alpha_blend(background: Color) -> Color:
@@ -211,33 +211,33 @@ extending Color:
211
211
  if this.a == 255:
212
212
  return this
213
213
 
214
- let sa = float<-(this.a) / 255.0
215
- let da = float<-(background.a) / 255.0
214
+ let sa = float<-this.a / 255.0
215
+ let da = float<-background.a / 255.0
216
216
 
217
- let r_out = float<-(this.r) * sa + float<-(background.r) * da * (1.0 - sa)
218
- let g_out = float<-(this.g) * sa + float<-(background.g) * da * (1.0 - sa)
219
- let b_out = float<-(this.b) * sa + float<-(background.b) * da * (1.0 - sa)
217
+ let r_out = float<-this.r * sa + float<-background.r * da * (1.0 - sa)
218
+ let g_out = float<-this.g * sa + float<-background.g * da * (1.0 - sa)
219
+ let b_out = float<-this.b * sa + float<-background.b * da * (1.0 - sa)
220
220
  let a_out = sa + da * (1.0 - sa)
221
221
 
222
222
  return Color(
223
- r = ubyte<-(r_out),
224
- g = ubyte<-(g_out),
225
- b = ubyte<-(b_out),
223
+ r = ubyte<-r_out,
224
+ g = ubyte<-g_out,
225
+ b = ubyte<-b_out,
226
226
  a = ubyte<-(a_out * 255.0)
227
227
  )
228
228
 
229
229
 
230
230
  public function scale(factor: float) -> Color:
231
- let r = fmin(fmax(float<-(this.r) * factor, 0.0), 255.0)
232
- let g = fmin(fmax(float<-(this.g) * factor, 0.0), 255.0)
233
- let b = fmin(fmax(float<-(this.b) * factor, 0.0), 255.0)
234
- return Color(r = ubyte<-(r), g = ubyte<-(g), b = ubyte<-(b), a = this.a)
231
+ let r = fmin(fmax(float<-this.r * factor, 0.0), 255.0)
232
+ let g = fmin(fmax(float<-this.g * factor, 0.0), 255.0)
233
+ let b = fmin(fmax(float<-this.b * factor, 0.0), 255.0)
234
+ return Color(r = ubyte<-r, g = ubyte<-g, b = ubyte<-b, a = this.a)
235
235
 
236
236
 
237
237
  public function multiply(other: Color) -> Color:
238
- let r = ubyte<-(int<-((int<-(this.r) * int<-(other.r)) / 255))
239
- let g = ubyte<-(int<-((int<-(this.g) * int<-(other.g)) / 255))
240
- let b = ubyte<-(int<-((int<-(this.b) * int<-(other.b)) / 255))
238
+ let r = ubyte<-((int<-this.r * int<-other.r) / 255)
239
+ let g = ubyte<-((int<-this.g * int<-other.g) / 255)
240
+ let b = ubyte<-((int<-this.b * int<-other.b) / 255)
241
241
  return Color(r = r, g = g, b = b, a = this.a)
242
242
 
243
243
 
data/std/encoding.mt CHANGED
@@ -47,15 +47,15 @@ public function decode_utf8_codepoint(text: str, byte_offset: ptr_uint) -> Optio
47
47
  return Option[uint].none
48
48
 
49
49
  if len == 1:
50
- return Option[uint].some(value = uint<-(b0))
50
+ return Option[uint].some(value = uint<-b0)
51
51
 
52
52
  var codepoint: uint = (b0)
53
53
  if len == 2:
54
- codepoint = codepoint & uint<-(0x1F)
54
+ codepoint = codepoint & uint<-0x1F
55
55
  else if len == 3:
56
- codepoint = codepoint & uint<-(0x0F)
56
+ codepoint = codepoint & uint<-0x0F
57
57
  else if len == 4:
58
- codepoint = codepoint & uint<-(0x07)
58
+ codepoint = codepoint & uint<-0x07
59
59
  var i: ptr_uint = 1
60
60
  while i < len:
61
61
  codepoint = (codepoint << 6) | uint<-(text.byte_at(byte_offset + i) & 0x3F)