mt-lang 0.2.0

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 (258) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/AUTHORS +3 -0
  4. data/Gemfile +19 -0
  5. data/Gemfile.lock +82 -0
  6. data/LICENSE +21 -0
  7. data/README.md +1208 -0
  8. data/Rakefile +332 -0
  9. data/bin/mtc +8 -0
  10. data/bin/profile-mtc-checks +133 -0
  11. data/bin/tracy-profiler +0 -0
  12. data/docs/build-guide.md +536 -0
  13. data/docs/index.html +2778 -0
  14. data/docs/language-design.md +1519 -0
  15. data/docs/language-manual.md +1534 -0
  16. data/lib/milk_tea/base.rb +49 -0
  17. data/lib/milk_tea/bindings/bindgen/ast_parser.rb +398 -0
  18. data/lib/milk_tea/bindings/bindgen/declaration.rb +319 -0
  19. data/lib/milk_tea/bindings/bindgen/emitter.rb +387 -0
  20. data/lib/milk_tea/bindings/bindgen/overrides.rb +134 -0
  21. data/lib/milk_tea/bindings/bindgen/type_mapper.rb +622 -0
  22. data/lib/milk_tea/bindings/bindgen.rb +207 -0
  23. data/lib/milk_tea/bindings/cli.rb +121 -0
  24. data/lib/milk_tea/bindings/imported_bindings/defaults.rb +210 -0
  25. data/lib/milk_tea/bindings/imported_bindings/generator.rb +1114 -0
  26. data/lib/milk_tea/bindings/imported_bindings/method_source.rb +190 -0
  27. data/lib/milk_tea/bindings/imported_bindings/naming.rb +286 -0
  28. data/lib/milk_tea/bindings/imported_bindings.rb +215 -0
  29. data/lib/milk_tea/bindings/opengl_registry.rb +547 -0
  30. data/lib/milk_tea/bindings/raw_bindings/defaults.rb +1338 -0
  31. data/lib/milk_tea/bindings/raw_bindings.rb +269 -0
  32. data/lib/milk_tea/bindings/steamworks.rb +629 -0
  33. data/lib/milk_tea/bindings/upstream_sources.rb +352 -0
  34. data/lib/milk_tea/bindings/vendored_box2d.rb +79 -0
  35. data/lib/milk_tea/bindings/vendored_c_library.rb +322 -0
  36. data/lib/milk_tea/bindings/vendored_cjson.rb +52 -0
  37. data/lib/milk_tea/bindings/vendored_flecs.rb +78 -0
  38. data/lib/milk_tea/bindings/vendored_glfw.rb +77 -0
  39. data/lib/milk_tea/bindings/vendored_libuv.rb +75 -0
  40. data/lib/milk_tea/bindings/vendored_pcre2.rb +84 -0
  41. data/lib/milk_tea/bindings/vendored_raylib.rb +131 -0
  42. data/lib/milk_tea/bindings/vendored_sdl3.rb +78 -0
  43. data/lib/milk_tea/bindings/vendored_steamworks.rb +71 -0
  44. data/lib/milk_tea/bindings/vendored_tool.rb +71 -0
  45. data/lib/milk_tea/bindings/vendored_tools.rb +23 -0
  46. data/lib/milk_tea/bindings/vendored_tracy.rb +37 -0
  47. data/lib/milk_tea/bindings.rb +23 -0
  48. data/lib/milk_tea/core/ast.rb +311 -0
  49. data/lib/milk_tea/core/async_runtime_installer.rb +28 -0
  50. data/lib/milk_tea/core/binding_types.rb +18 -0
  51. data/lib/milk_tea/core/bindings/attribute_binding.rb +58 -0
  52. data/lib/milk_tea/core/bindings/function_binding.rb +5 -0
  53. data/lib/milk_tea/core/bindings/module_binding.rb +58 -0
  54. data/lib/milk_tea/core/bindings/value_binding.rb +21 -0
  55. data/lib/milk_tea/core/c_backend/aggregate_utils.rb +103 -0
  56. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +412 -0
  57. data/lib/milk_tea/core/c_backend/expressions.rb +468 -0
  58. data/lib/milk_tea/core/c_backend/feature_detection.rb +483 -0
  59. data/lib/milk_tea/core/c_backend/reachability.rb +398 -0
  60. data/lib/milk_tea/core/c_backend/reinterpret.rb +226 -0
  61. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1080 -0
  62. data/lib/milk_tea/core/c_backend/statements.rb +563 -0
  63. data/lib/milk_tea/core/c_backend/type_collectors.rb +1545 -0
  64. data/lib/milk_tea/core/c_backend/type_declaration.rb +223 -0
  65. data/lib/milk_tea/core/c_backend/type_system.rb +345 -0
  66. data/lib/milk_tea/core/c_backend.rb +287 -0
  67. data/lib/milk_tea/core/compatibility_helpers.rb +79 -0
  68. data/lib/milk_tea/core/compile_time/const_eval.rb +187 -0
  69. data/lib/milk_tea/core/compile_time.rb +329 -0
  70. data/lib/milk_tea/core/control_flow/builder.rb +582 -0
  71. data/lib/milk_tea/core/control_flow/constant_propagation.rb +143 -0
  72. data/lib/milk_tea/core/control_flow/dataflow.rb +74 -0
  73. data/lib/milk_tea/core/control_flow/definite_assignment.rb +79 -0
  74. data/lib/milk_tea/core/control_flow/graph.rb +90 -0
  75. data/lib/milk_tea/core/control_flow/liveness.rb +24 -0
  76. data/lib/milk_tea/core/control_flow/nullability_flow.rb +43 -0
  77. data/lib/milk_tea/core/control_flow/reachability.rb +22 -0
  78. data/lib/milk_tea/core/control_flow/termination.rb +31 -0
  79. data/lib/milk_tea/core/control_flow.rb +34 -0
  80. data/lib/milk_tea/core/cst.rb +48 -0
  81. data/lib/milk_tea/core/cst_builder.rb +19 -0
  82. data/lib/milk_tea/core/ir.rb +85 -0
  83. data/lib/milk_tea/core/keywords.rb +97 -0
  84. data/lib/milk_tea/core/lexer/character_classes.rb +60 -0
  85. data/lib/milk_tea/core/lexer/format_strings.rb +225 -0
  86. data/lib/milk_tea/core/lexer/heredocs.rb +199 -0
  87. data/lib/milk_tea/core/lexer/indentation.rb +62 -0
  88. data/lib/milk_tea/core/lexer/numbers.rb +96 -0
  89. data/lib/milk_tea/core/lexer/recovery.rb +32 -0
  90. data/lib/milk_tea/core/lexer/strings.rb +167 -0
  91. data/lib/milk_tea/core/lexer/symbols.rb +71 -0
  92. data/lib/milk_tea/core/lexer/trivia.rb +53 -0
  93. data/lib/milk_tea/core/lexer.rb +430 -0
  94. data/lib/milk_tea/core/lowering/artifacts.rb +26 -0
  95. data/lib/milk_tea/core/lowering/async/analysis.rb +245 -0
  96. data/lib/milk_tea/core/lowering/async/lowering.rb +1399 -0
  97. data/lib/milk_tea/core/lowering/async/normalization.rb +459 -0
  98. data/lib/milk_tea/core/lowering/async.rb +714 -0
  99. data/lib/milk_tea/core/lowering/block.rb +1052 -0
  100. data/lib/milk_tea/core/lowering/calls.rb +1565 -0
  101. data/lib/milk_tea/core/lowering/declarations.rb +214 -0
  102. data/lib/milk_tea/core/lowering/dyn.rb +206 -0
  103. data/lib/milk_tea/core/lowering/events.rb +1054 -0
  104. data/lib/milk_tea/core/lowering/expressions.rb +1645 -0
  105. data/lib/milk_tea/core/lowering/foreign_cstr.rb +206 -0
  106. data/lib/milk_tea/core/lowering/functions.rb +242 -0
  107. data/lib/milk_tea/core/lowering/loops.rb +1087 -0
  108. data/lib/milk_tea/core/lowering/lowering_context.rb +80 -0
  109. data/lib/milk_tea/core/lowering/proc.rb +419 -0
  110. data/lib/milk_tea/core/lowering/resolve.rb +2516 -0
  111. data/lib/milk_tea/core/lowering/scans.rb +220 -0
  112. data/lib/milk_tea/core/lowering/str_buffer.rb +125 -0
  113. data/lib/milk_tea/core/lowering/utils.rb +1453 -0
  114. data/lib/milk_tea/core/lowering.rb +378 -0
  115. data/lib/milk_tea/core/module_binder.rb +181 -0
  116. data/lib/milk_tea/core/module_loader/errors.rb +21 -0
  117. data/lib/milk_tea/core/module_loader.rb +479 -0
  118. data/lib/milk_tea/core/module_path_resolver.rb +153 -0
  119. data/lib/milk_tea/core/module_roots.rb +88 -0
  120. data/lib/milk_tea/core/parser/attributes.rb +71 -0
  121. data/lib/milk_tea/core/parser/blocks.rb +119 -0
  122. data/lib/milk_tea/core/parser/declarations.rb +749 -0
  123. data/lib/milk_tea/core/parser/expressions.rb +624 -0
  124. data/lib/milk_tea/core/parser/recovery.rb +131 -0
  125. data/lib/milk_tea/core/parser/statements.rb +756 -0
  126. data/lib/milk_tea/core/parser/types.rb +271 -0
  127. data/lib/milk_tea/core/parser.rb +400 -0
  128. data/lib/milk_tea/core/prelude_installer.rb +29 -0
  129. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +917 -0
  130. data/lib/milk_tea/core/pretty_printer/base_formatter.rb +87 -0
  131. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +300 -0
  132. data/lib/milk_tea/core/pretty_printer.rb +17 -0
  133. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +314 -0
  134. data/lib/milk_tea/core/semantic_analyzer/attributes.rb +184 -0
  135. data/lib/milk_tea/core/semantic_analyzer/calls.rb +992 -0
  136. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1735 -0
  137. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +355 -0
  138. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +155 -0
  139. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +354 -0
  140. data/lib/milk_tea/core/semantic_analyzer/generics.rb +383 -0
  141. data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +78 -0
  142. data/lib/milk_tea/core/semantic_analyzer/module_context.rb +35 -0
  143. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +1438 -0
  144. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +421 -0
  145. data/lib/milk_tea/core/semantic_analyzer/statements.rb +1308 -0
  146. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +588 -0
  147. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +307 -0
  148. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +851 -0
  149. data/lib/milk_tea/core/semantic_analyzer.rb +327 -0
  150. data/lib/milk_tea/core/token.rb +26 -0
  151. data/lib/milk_tea/core/token_stream.rb +30 -0
  152. data/lib/milk_tea/core/types/layout.rb +243 -0
  153. data/lib/milk_tea/core/types/predicates.rb +609 -0
  154. data/lib/milk_tea/core/types/registry.rb +83 -0
  155. data/lib/milk_tea/core/types/types.rb +1696 -0
  156. data/lib/milk_tea/core/types/visitor.rb +442 -0
  157. data/lib/milk_tea/core.rb +25 -0
  158. data/lib/milk_tea/dap/backends/lldb_dap.rb +158 -0
  159. data/lib/milk_tea/dap/protocol.rb +58 -0
  160. data/lib/milk_tea/dap/server/breakpoints.rb +66 -0
  161. data/lib/milk_tea/dap/server/debug_map.rb +291 -0
  162. data/lib/milk_tea/dap/server/handlers.rb +374 -0
  163. data/lib/milk_tea/dap/server/launch.rb +261 -0
  164. data/lib/milk_tea/dap/server/lldb_backend.rb +380 -0
  165. data/lib/milk_tea/dap/server/pause_diagnostics.rb +109 -0
  166. data/lib/milk_tea/dap/server/utilities.rb +160 -0
  167. data/lib/milk_tea/dap/server/wire.rb +55 -0
  168. data/lib/milk_tea/dap/server.rb +131 -0
  169. data/lib/milk_tea/dap/session.rb +152 -0
  170. data/lib/milk_tea/dap.rb +6 -0
  171. data/lib/milk_tea/lsp/dependency_resolution.rb +52 -0
  172. data/lib/milk_tea/lsp/diagnostics.rb +611 -0
  173. data/lib/milk_tea/lsp/protocol.rb +104 -0
  174. data/lib/milk_tea/lsp/server/call_hierarchy.rb +274 -0
  175. data/lib/milk_tea/lsp/server/code_actions.rb +446 -0
  176. data/lib/milk_tea/lsp/server/code_lens.rb +97 -0
  177. data/lib/milk_tea/lsp/server/completion.rb +1099 -0
  178. data/lib/milk_tea/lsp/server/configuration.rb +167 -0
  179. data/lib/milk_tea/lsp/server/debug_info.rb +45 -0
  180. data/lib/milk_tea/lsp/server/definition.rb +779 -0
  181. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +239 -0
  182. data/lib/milk_tea/lsp/server/execute_command.rb +25 -0
  183. data/lib/milk_tea/lsp/server/folding_range.rb +153 -0
  184. data/lib/milk_tea/lsp/server/formatting.rb +575 -0
  185. data/lib/milk_tea/lsp/server/hover.rb +1465 -0
  186. data/lib/milk_tea/lsp/server/inlay_hints.rb +204 -0
  187. data/lib/milk_tea/lsp/server/lifecycle.rb +234 -0
  188. data/lib/milk_tea/lsp/server/linked_editing_range.rb +43 -0
  189. data/lib/milk_tea/lsp/server/on_type_formatting.rb +73 -0
  190. data/lib/milk_tea/lsp/server/progress.rb +47 -0
  191. data/lib/milk_tea/lsp/server/references.rb +433 -0
  192. data/lib/milk_tea/lsp/server/rename.rb +598 -0
  193. data/lib/milk_tea/lsp/server/selection_range.rb +130 -0
  194. data/lib/milk_tea/lsp/server/semantic_tokens.rb +1745 -0
  195. data/lib/milk_tea/lsp/server/signature_help.rb +200 -0
  196. data/lib/milk_tea/lsp/server/text_documents.rb +125 -0
  197. data/lib/milk_tea/lsp/server/type_hierarchy.rb +167 -0
  198. data/lib/milk_tea/lsp/server/utilities.rb +520 -0
  199. data/lib/milk_tea/lsp/server.rb +415 -0
  200. data/lib/milk_tea/lsp/workspace/analysis.rb +209 -0
  201. data/lib/milk_tea/lsp/workspace/caches.rb +260 -0
  202. data/lib/milk_tea/lsp/workspace/collection.rb +98 -0
  203. data/lib/milk_tea/lsp/workspace/definition_index.rb +184 -0
  204. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +282 -0
  205. data/lib/milk_tea/lsp/workspace/store.rb +154 -0
  206. data/lib/milk_tea/lsp/workspace/utilities.rb +490 -0
  207. data/lib/milk_tea/lsp/workspace.rb +127 -0
  208. data/lib/milk_tea/lsp.rb +13 -0
  209. data/lib/milk_tea/packages/atomic_write.rb +63 -0
  210. data/lib/milk_tea/packages/dependency_solver.rb +194 -0
  211. data/lib/milk_tea/packages/graph.rb +139 -0
  212. data/lib/milk_tea/packages/lock.rb +421 -0
  213. data/lib/milk_tea/packages/manager_cli.rb +485 -0
  214. data/lib/milk_tea/packages/manifest.rb +356 -0
  215. data/lib/milk_tea/packages/manifest_editor.rb +134 -0
  216. data/lib/milk_tea/packages/registry_metadata_provider.rb +34 -0
  217. data/lib/milk_tea/packages/registry_store.rb +420 -0
  218. data/lib/milk_tea/packages/services.rb +41 -0
  219. data/lib/milk_tea/packages/source_cache.rb +71 -0
  220. data/lib/milk_tea/packages/source_fetcher.rb +124 -0
  221. data/lib/milk_tea/packages/source_resolver.rb +389 -0
  222. data/lib/milk_tea/packages/version.rb +164 -0
  223. data/lib/milk_tea/packages.rb +16 -0
  224. data/lib/milk_tea/tooling/asset_pack.rb +141 -0
  225. data/lib/milk_tea/tooling/build.rb +1124 -0
  226. data/lib/milk_tea/tooling/build_cache.rb +240 -0
  227. data/lib/milk_tea/tooling/cli.rb +2688 -0
  228. data/lib/milk_tea/tooling/cst_formatter.rb +13 -0
  229. data/lib/milk_tea/tooling/debug_info_formatter.rb +456 -0
  230. data/lib/milk_tea/tooling/debug_map.rb +248 -0
  231. data/lib/milk_tea/tooling/docs_app.rb +369 -0
  232. data/lib/milk_tea/tooling/error_formatter.rb +86 -0
  233. data/lib/milk_tea/tooling/formatter.rb +787 -0
  234. data/lib/milk_tea/tooling/linter/doc_tags.rb +212 -0
  235. data/lib/milk_tea/tooling/linter/fix_engine.rb +237 -0
  236. data/lib/milk_tea/tooling/linter/flow_rules.rb +380 -0
  237. data/lib/milk_tea/tooling/linter/imports_platform.rb +841 -0
  238. data/lib/milk_tea/tooling/linter/release_rules.rb +744 -0
  239. data/lib/milk_tea/tooling/linter/reserved_names.rb +199 -0
  240. data/lib/milk_tea/tooling/linter/rules.rb +593 -0
  241. data/lib/milk_tea/tooling/linter/source_helpers.rb +407 -0
  242. data/lib/milk_tea/tooling/linter/trailing_comma.rb +139 -0
  243. data/lib/milk_tea/tooling/linter/visitors.rb +1286 -0
  244. data/lib/milk_tea/tooling/linter.rb +798 -0
  245. data/lib/milk_tea/tooling/project_scaffold.rb +82 -0
  246. data/lib/milk_tea/tooling/public/css/docs.css +166 -0
  247. data/lib/milk_tea/tooling/public/js/docs.js +94 -0
  248. data/lib/milk_tea/tooling/run.rb +408 -0
  249. data/lib/milk_tea/tooling/templates/wasm_shell.html +48 -0
  250. data/lib/milk_tea/tooling/toolchain_cli.rb +157 -0
  251. data/lib/milk_tea/tooling/views/404.erb +7 -0
  252. data/lib/milk_tea/tooling/views/index.erb +87 -0
  253. data/lib/milk_tea/tooling/views/layout.erb +69 -0
  254. data/lib/milk_tea/tooling/views/module.erb +97 -0
  255. data/lib/milk_tea/tooling/views/stdlib.erb +21 -0
  256. data/lib/milk_tea/tooling.rb +20 -0
  257. data/lib/milk_tea.rb +8 -0
  258. metadata +426 -0
@@ -0,0 +1,407 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ class Linter
5
+ module LinterSourceHelpers
6
+ private
7
+
8
+ def emit_line_too_long_warnings
9
+ return unless @max_line_length.positive?
10
+ return if @source.empty?
11
+
12
+ @source_lines.each_with_index do |line, index|
13
+ next if line.empty?
14
+ next if external_or_foreign_function_header_line?(line)
15
+ next unless line.length > @max_line_length
16
+ next unless Formatter.wrappable_long_line_candidate_text?(line)
17
+
18
+ fix = Formatter.build_long_line_wrap_fix(
19
+ @source,
20
+ index,
21
+ max_line_length: @max_line_length,
22
+ path: @path,
23
+ tokens: @tokens,
24
+ tokens_by_line: @tokens_by_line,
25
+ validate: false,
26
+ )
27
+ message = "line exceeds max length of #{@max_line_length} columns (#{line.length})"
28
+ message << "; wrap the expression" if fix
29
+ @warnings << Warning.new(
30
+ path: @path,
31
+ line: index + 1,
32
+ column: @max_line_length + 1,
33
+ length: line.length - @max_line_length,
34
+ code: "line-too-long",
35
+ message:,
36
+ severity: :warning,
37
+ )
38
+ end
39
+ end
40
+ def external_or_foreign_function_header_line?(line)
41
+ line.strip.match?(/\A(?:[A-Za-z_]\w*\s+)*(?:external|foreign)\s+function\b/)
42
+ end
43
+ def emit_event_capacity_warnings(source_file)
44
+ each_event_declaration(source_file) do |event_decl, owner_name|
45
+ next unless event_decl.capacity >= EVENT_STACK_SNAPSHOT_WARNING_THRESHOLD
46
+
47
+ warn_large_event_capacity(event_decl, owner_name:)
48
+ end
49
+ end
50
+
51
+ def each_event_declaration(source_file)
52
+ source_file.declarations.each do |declaration|
53
+ case declaration
54
+ when AST::EventDecl
55
+ yield declaration, nil
56
+ when AST::StructDecl
57
+ declaration.events.each { |event_decl| yield event_decl, declaration.name }
58
+ each_nested_struct_event_declaration(declaration, owner_name: declaration.name) { |ev, owner| yield ev, owner }
59
+ end
60
+ end
61
+ end
62
+
63
+ def each_nested_struct_event_declaration(struct_decl, owner_name:)
64
+ struct_decl.nested_types.each do |nested|
65
+ next unless nested.is_a?(AST::StructDecl)
66
+
67
+ nested_owner = "#{owner_name}.#{nested.name}"
68
+ nested.events.each { |event_decl| yield event_decl, nested_owner }
69
+ each_nested_struct_event_declaration(nested, owner_name: nested_owner) { |ev, owner| yield ev, owner }
70
+ end
71
+ end
72
+ def profile_phase(name)
73
+ return yield unless @profile
74
+
75
+ @profile.measure(name) { yield }
76
+ end
77
+ def param_line(param, fallback: nil)
78
+ line = param.respond_to?(:line) ? param.line : nil
79
+ line || fallback
80
+ end
81
+
82
+ def param_column(param)
83
+ param.respond_to?(:column) ? param.column : nil
84
+ end
85
+
86
+ def declaration_column(declaration)
87
+ declaration.respond_to?(:column) ? declaration.column : nil
88
+ end
89
+ def source_line_text(line)
90
+ return nil unless line && line >= 1 && line <= @source_lines.length
91
+
92
+ @source_lines[line - 1]
93
+ end
94
+
95
+ def source_code_line(line)
96
+ text = source_line_text(line)
97
+ return nil unless text
98
+
99
+ text.rstrip
100
+ end
101
+
102
+ def source_statement_span(line)
103
+ text = source_code_line(line)
104
+ return nil unless text
105
+
106
+ start_index = text.index(/\S/)
107
+ return nil unless start_index
108
+
109
+ [start_index + 1, text.length - start_index]
110
+ end
111
+ def source_condition_span(line, keyword_pattern:)
112
+ text = source_code_line(line)
113
+ return nil unless text
114
+
115
+ match = text.match(/\A\s*(?:#{keyword_pattern})\s+(.*?)\s*:/)
116
+ return nil unless match
117
+
118
+ condition = match[1].rstrip
119
+ return nil if condition.empty?
120
+
121
+ [match.begin(1) + 1, condition.length]
122
+ end
123
+ def expression_line(expr)
124
+ return nil unless expr
125
+
126
+ return expr.line if expr.respond_to?(:line) && expr.line
127
+
128
+ case expr
129
+ when AST::BinaryOp
130
+ expression_line(expr.left) || expression_line(expr.right)
131
+ when AST::UnaryOp
132
+ expression_line(expr.operand)
133
+ when AST::Specialization
134
+ expression_line(expr.callee)
135
+ when AST::Call
136
+ expression_line(expr.callee) || expr.arguments.filter_map { |argument| expression_line(argument.value) }.first
137
+ when AST::IndexAccess
138
+ expression_line(expr.receiver) || expression_line(expr.index)
139
+ when AST::MemberAccess
140
+ expression_line(expr.receiver)
141
+ when AST::RangeExpr
142
+ expression_line(expr.start_expr) || expression_line(expr.end_expr)
143
+ when AST::ExpressionList
144
+ expr.elements.filter_map { |element| expression_line(element) }.first
145
+ when AST::IfExpr
146
+ expression_line(expr.condition) || expression_line(expr.then_expression) || expression_line(expr.else_expression)
147
+ when AST::AwaitExpr
148
+ expression_line(expr.expression)
149
+ when AST::UnsafeExpr
150
+ expression_line(expr.expression)
151
+ when AST::FormatString
152
+ expr.parts.filter_map do |part|
153
+ expression_line(part.expression) if part.is_a?(AST::FormatExprPart)
154
+ end.first
155
+ else
156
+ nil
157
+ end
158
+ end
159
+
160
+ def expression_column(expr)
161
+ return nil unless expr
162
+
163
+ if expr.respond_to?(:column) && expr.column
164
+ return expr.column
165
+ end
166
+
167
+ case expr
168
+ when AST::BinaryOp
169
+ expression_column(expr.left) || expression_column(expr.right)
170
+ when AST::UnaryOp
171
+ expression_column(expr.operand)
172
+ when AST::Specialization
173
+ expression_column(expr.callee)
174
+ when AST::Call
175
+ expression_column(expr.callee)
176
+ when AST::IndexAccess
177
+ expression_column(expr.receiver)
178
+ when AST::MemberAccess
179
+ expression_column(expr.receiver)
180
+ when AST::RangeExpr
181
+ expression_column(expr.start_expr) || expression_column(expr.end_expr)
182
+ when AST::ExpressionList
183
+ expr.elements.filter_map { |element| expression_column(element) }.first
184
+ when AST::IfExpr
185
+ expression_column(expr.condition) || expression_column(expr.then_expression) || expression_column(expr.else_expression)
186
+ when AST::AwaitExpr
187
+ expression_column(expr.expression)
188
+ when AST::UnsafeExpr
189
+ expression_column(expr.expression)
190
+ else
191
+ nil
192
+ end
193
+ end
194
+
195
+ def expression_length(expr)
196
+ return nil unless expr
197
+
198
+ case expr
199
+ when AST::Identifier
200
+ expr.name.length
201
+ when AST::BooleanLiteral
202
+ expr.value ? 4 : 5
203
+ when AST::NullLiteral
204
+ 4
205
+ when AST::IntegerLiteral, AST::FloatLiteral, AST::StringLiteral
206
+ expr.lexeme.length
207
+ when AST::BinaryOp
208
+ left_column = expression_column(expr.left)
209
+ right_column = expression_column(expr.right)
210
+ right_length = expression_length(expr.right)
211
+ if left_column && right_column && right_length
212
+ (right_column + right_length) - left_column
213
+ end
214
+ when AST::UnaryOp
215
+ expression_length(expr.operand)
216
+ when AST::Specialization
217
+ expression_length(expr.callee)
218
+ when AST::Call
219
+ expression_length(expr.callee)
220
+ when AST::AwaitExpr
221
+ expression_length(expr.expression)
222
+ when AST::UnsafeExpr
223
+ expression_length(expr.expression)
224
+ else
225
+ 1
226
+ end
227
+ end
228
+
229
+ def statement_column(statement)
230
+ return nil unless statement
231
+
232
+ if statement.respond_to?(:column) && statement.column
233
+ return statement.column
234
+ end
235
+
236
+ case statement
237
+ when AST::IfStmt
238
+ statement.branches.first&.column || statement.else_column
239
+ when AST::WhileStmt
240
+ expression_column(statement.condition)
241
+ when AST::MatchStmt
242
+ expression_column(statement.expression)
243
+ when AST::ReturnStmt
244
+ expression_column(statement.value)
245
+ when AST::DeferStmt
246
+ expression_column(statement.expression)
247
+ when AST::ExpressionStmt
248
+ expression_column(statement.expression) || source_statement_span(statement.line)&.first
249
+ else
250
+ nil
251
+ end
252
+ end
253
+
254
+ def statement_length(statement)
255
+ return nil unless statement
256
+
257
+ if statement.respond_to?(:length) && statement.length
258
+ return statement.length
259
+ end
260
+
261
+ case statement
262
+ when AST::LocalDecl, AST::ForStmt
263
+ statement.name.length
264
+ when AST::IfStmt
265
+ statement.branches.first&.length || (statement.else_column ? 4 : nil)
266
+ when AST::WhileStmt
267
+ expression_length(statement.condition)
268
+ when AST::MatchStmt
269
+ expression_length(statement.expression)
270
+ when AST::ReturnStmt
271
+ expression_length(statement.value)
272
+ when AST::DeferStmt
273
+ expression_length(statement.expression)
274
+ when AST::ExpressionStmt
275
+ expression_length(statement.expression) || source_statement_span(statement.line)&.last
276
+ else
277
+ nil
278
+ end
279
+ end
280
+
281
+ def condition_span(expr, line:, keyword_pattern:)
282
+ source_span = source_condition_span(line, keyword_pattern:)
283
+ if source_span
284
+ [line, source_span.first, source_span.last]
285
+ else
286
+ [expression_line(expr) || line, expression_column(expr), expression_length(expr)]
287
+ end
288
+ end
289
+
290
+ def condition_symbol_name(expr)
291
+ case expr
292
+ when AST::Identifier
293
+ expr.name
294
+ when AST::BooleanLiteral
295
+ expr.value ? "true" : "false"
296
+ when AST::BinaryOp
297
+ condition_symbol_name(expr.left) || condition_symbol_name(expr.right)
298
+ when AST::UnaryOp
299
+ condition_symbol_name(expr.operand)
300
+ when AST::Call
301
+ condition_symbol_name(expr.callee)
302
+ when AST::IndexAccess
303
+ condition_symbol_name(expr.receiver)
304
+ when AST::MemberAccess
305
+ condition_symbol_name(expr.receiver)
306
+ when AST::RangeExpr
307
+ condition_symbol_name(expr.start_expr) || condition_symbol_name(expr.end_expr)
308
+ else
309
+ nil
310
+ end
311
+ end
312
+ def each_statement_expression(statement, &block)
313
+ case statement
314
+ when AST::LocalDecl
315
+ walk_expression_tree(statement.value, &block)
316
+ when AST::Assignment
317
+ walk_expression_tree(statement.target, &block)
318
+ walk_expression_tree(statement.value, &block)
319
+ when AST::IfBranch
320
+ walk_expression_tree(statement.condition, &block)
321
+ when AST::IfStmt
322
+ statement.branches.each { |branch| walk_expression_tree(branch.condition, &block) }
323
+ when AST::WhileStmt
324
+ walk_expression_tree(statement.condition, &block)
325
+ when AST::ForStmt
326
+ statement.iterables.each { |iterable| walk_expression_tree(iterable, &block) }
327
+ when AST::MatchStmt
328
+ walk_expression_tree(statement.expression, &block)
329
+ when AST::ReturnStmt
330
+ walk_expression_tree(statement.value, &block)
331
+ when AST::DeferStmt
332
+ walk_expression_tree(statement.expression, &block)
333
+ when AST::ExpressionStmt
334
+ walk_expression_tree(statement.expression, &block)
335
+ when AST::StaticAssert
336
+ walk_expression_tree(statement.condition, &block)
337
+ when AST::WhenStmt
338
+ walk_expression_tree(statement.discriminant, &block)
339
+ statement.branches.each { |branch| branch.body.each { |s| each_statement_expression(s, &block) } }
340
+ statement.else_body&.each { |s| each_statement_expression(s, &block) }
341
+ when AST::UnsafeStmt
342
+ statement.body.each { |s| each_statement_expression(s, &block) }
343
+ when AST::ErrorBlockStmt
344
+ statement.body.each { |s| each_statement_expression(s, &block) }
345
+ end
346
+ end
347
+
348
+ def walk_expression_tree(expression, &block)
349
+ return unless expression
350
+
351
+ yield expression
352
+ case expression
353
+ when AST::MemberAccess
354
+ walk_expression_tree(expression.receiver, &block)
355
+ when AST::IndexAccess
356
+ walk_expression_tree(expression.receiver, &block)
357
+ walk_expression_tree(expression.index, &block)
358
+ when AST::Specialization
359
+ walk_expression_tree(expression.callee, &block)
360
+ when AST::Call
361
+ walk_expression_tree(expression.callee, &block)
362
+ expression.arguments.each { |argument| walk_expression_tree(argument.value, &block) }
363
+ when AST::UnaryOp
364
+ walk_expression_tree(expression.operand, &block)
365
+ when AST::BinaryOp
366
+ walk_expression_tree(expression.left, &block)
367
+ walk_expression_tree(expression.right, &block)
368
+ when AST::RangeExpr
369
+ walk_expression_tree(expression.start_expr, &block)
370
+ walk_expression_tree(expression.end_expr, &block)
371
+ when AST::ExpressionList
372
+ expression.elements.each { |element| walk_expression_tree(element, &block) }
373
+ when AST::IfExpr
374
+ walk_expression_tree(expression.condition, &block)
375
+ walk_expression_tree(expression.then_expression, &block)
376
+ walk_expression_tree(expression.else_expression, &block)
377
+ when AST::AwaitExpr
378
+ walk_expression_tree(expression.expression, &block)
379
+ when AST::UnsafeExpr
380
+ walk_expression_tree(expression.expression, &block)
381
+ when AST::FormatString
382
+ expression.parts.each do |part|
383
+ walk_expression_tree(part.expression, &block) if part.is_a?(AST::FormatExprPart)
384
+ end
385
+ end
386
+ end
387
+ def walk_statement_lists(stmts, &block)
388
+ return if stmts.nil? || stmts.empty?
389
+
390
+ yield stmts
391
+ stmts.each do |statement|
392
+ case statement
393
+ when AST::IfStmt
394
+ statement.branches.each { |branch| walk_statement_lists(branch.body, &block) }
395
+ walk_statement_lists(statement.else_body, &block) if statement.else_body
396
+ when AST::MatchStmt
397
+ statement.arms.each { |arm| walk_statement_lists(arm.body, &block) }
398
+ when AST::UnsafeStmt, AST::ForStmt, AST::WhileStmt
399
+ walk_statement_lists(statement.body, &block)
400
+ when AST::DeferStmt
401
+ walk_statement_lists(statement.body, &block) if statement.body
402
+ end
403
+ end
404
+ end
405
+ end
406
+ end
407
+ end
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ class Linter
5
+ module LinterTrailingComma
6
+ private
7
+
8
+ def emit_trailing_list_comma_warnings(source_file)
9
+ return if @tokens.nil? || @tokens.empty?
10
+
11
+ warned_sites = Set.new
12
+ each_call_argument_list_candidate(source_file) do |call_expression, symbol_name|
13
+ site = trailing_call_argument_comma_site(call_expression)
14
+ next unless site
15
+ next if warned_sites.include?(site)
16
+
17
+ warned_sites << site
18
+ @warnings << Warning.new(
19
+ path: @path,
20
+ line: site[0],
21
+ column: site[1],
22
+ length: 1,
23
+ code: "trailing-list-comma",
24
+ message: "trailing comma in call argument list is redundant",
25
+ severity: :hint,
26
+ symbol_name:,
27
+ )
28
+ end
29
+ end
30
+
31
+ def each_call_argument_list_candidate(source_file, &block)
32
+ source_file.declarations.each do |declaration|
33
+ case declaration
34
+ when AST::ConstDecl, AST::VarDecl
35
+ each_call_in_expression(declaration.value, &block)
36
+ when AST::FunctionDef, AST::MethodDef
37
+ each_call_in_statement_list(declaration.body, &block)
38
+ when AST::ExtendingBlock
39
+ declaration.methods.each { |method| each_call_in_statement_list(method.body, &block) }
40
+ end
41
+ end
42
+ end
43
+
44
+ def each_call_in_statement_list(stmts, &block)
45
+ walk_statement_lists(stmts) do |statement_list|
46
+ statement_list.each do |statement|
47
+ each_statement_expression(statement) do |expression|
48
+ next unless expression.is_a?(AST::Call)
49
+ next if expression.arguments.nil? || expression.arguments.empty?
50
+
51
+ block.call(expression, call_symbol_name(expression.callee))
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ def each_call_in_expression(expression, &block)
58
+ walk_expression_tree(expression) do |node|
59
+ next unless node.is_a?(AST::Call)
60
+ next if node.arguments.nil? || node.arguments.empty?
61
+
62
+ block.call(node, call_symbol_name(node.callee))
63
+ end
64
+ end
65
+
66
+ def call_symbol_name(callee)
67
+ case callee
68
+ when AST::Identifier
69
+ callee.name
70
+ when AST::MemberAccess
71
+ callee.member
72
+ when AST::Specialization
73
+ call_symbol_name(callee.callee)
74
+ else
75
+ nil
76
+ end
77
+ end
78
+
79
+ def trailing_call_argument_comma_site(call_expression)
80
+ return nil unless call_expression.is_a?(AST::Call)
81
+ return nil if call_expression.arguments.nil? || call_expression.arguments.empty?
82
+
83
+ callee_line = expression_line(call_expression.callee)
84
+ callee_column = expression_column(call_expression.callee)
85
+ return nil unless callee_line && callee_column
86
+
87
+ callee_token_idx = @token_index_by_location[[callee_line, callee_column]]
88
+ return nil unless callee_token_idx
89
+
90
+ lparen_idx = nil
91
+ cursor = callee_token_idx
92
+ while cursor < @tokens.length
93
+ token = @tokens[cursor]
94
+ if token.type == :lparen
95
+ lparen_idx = cursor
96
+ break
97
+ end
98
+ break if token.type == :newline && token.line > callee_line
99
+
100
+ cursor += 1
101
+ end
102
+ return nil unless lparen_idx
103
+
104
+ paren_depth = 0
105
+ bracket_depth = 0
106
+ comma_token = nil
107
+ cursor = lparen_idx + 1
108
+
109
+ while cursor < @tokens.length
110
+ token = @tokens[cursor]
111
+ case token.type
112
+ when :lparen
113
+ paren_depth += 1
114
+ when :rparen
115
+ if paren_depth.zero? && bracket_depth.zero?
116
+ return [comma_token.line, comma_token.column] if comma_token
117
+
118
+ return nil
119
+ end
120
+ paren_depth -= 1 if paren_depth.positive?
121
+ when :lbracket
122
+ bracket_depth += 1
123
+ when :rbracket
124
+ bracket_depth -= 1 if bracket_depth.positive?
125
+ when :comma
126
+ comma_token = token if paren_depth.zero? && bracket_depth.zero?
127
+ else
128
+ if paren_depth.zero? && bracket_depth.zero? && !%i[newline indent dedent eof].include?(token.type)
129
+ comma_token = nil
130
+ end
131
+ end
132
+ cursor += 1
133
+ end
134
+
135
+ nil
136
+ end
137
+ end
138
+ end
139
+ end