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.
- checksums.yaml +4 -4
- data/lib/milk_tea/base.rb +1 -2
- data/lib/milk_tea/bindings/bindgen/ast_parser.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/declaration.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/emitter.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/overrides.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/type_mapper.rb +51 -53
- data/lib/milk_tea/bindings/imported_bindings/generator.rb +7 -9
- data/lib/milk_tea/bindings/imported_bindings/method_source.rb +0 -2
- data/lib/milk_tea/bindings/imported_bindings/naming.rb +6 -8
- data/lib/milk_tea/bindings/imported_bindings.rb +0 -2
- data/lib/milk_tea/bindings/opengl_registry.rb +3 -7
- data/lib/milk_tea/bindings/steamworks.rb +12 -16
- data/lib/milk_tea/bindings/upstream_sources.rb +10 -14
- data/lib/milk_tea/bindings/vendored_box2d.rb +10 -12
- data/lib/milk_tea/bindings/vendored_cjson.rb +6 -8
- data/lib/milk_tea/bindings/vendored_flecs.rb +10 -12
- data/lib/milk_tea/bindings/vendored_glfw.rb +11 -13
- data/lib/milk_tea/bindings/vendored_libuv.rb +11 -13
- data/lib/milk_tea/bindings/vendored_pcre2.rb +11 -13
- data/lib/milk_tea/bindings/vendored_raylib.rb +6 -10
- data/lib/milk_tea/bindings/vendored_sdl3.rb +11 -13
- data/lib/milk_tea/bindings/vendored_steamworks.rb +1 -5
- data/lib/milk_tea/core/ast.rb +103 -45
- data/lib/milk_tea/core/async_runtime_installer.rb +0 -2
- data/lib/milk_tea/core/bindings/attribute_binding.rb +1 -3
- data/lib/milk_tea/core/c_backend/aggregate_utils.rb +147 -84
- data/lib/milk_tea/core/c_backend/control_flow_emission.rb +394 -396
- data/lib/milk_tea/core/c_backend/expressions.rb +489 -476
- data/lib/milk_tea/core/c_backend/feature_detection.rb +446 -437
- data/lib/milk_tea/core/c_backend/format_helpers.rb +0 -2
- data/lib/milk_tea/core/c_backend/reachability.rb +339 -341
- data/lib/milk_tea/core/c_backend/reinterpret.rb +231 -217
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +531 -526
- data/lib/milk_tea/core/c_backend/statements.rb +528 -530
- data/lib/milk_tea/core/c_backend/type_collectors.rb +1344 -1346
- data/lib/milk_tea/core/c_backend/type_declaration.rb +215 -213
- data/lib/milk_tea/core/c_backend/type_system.rb +347 -349
- data/lib/milk_tea/core/c_backend.rb +17 -9
- data/lib/milk_tea/core/compatibility_helpers.rb +0 -2
- data/lib/milk_tea/core/compile_time/const_eval.rb +7 -12
- data/lib/milk_tea/core/compile_time.rb +32 -38
- data/lib/milk_tea/core/control_flow/builder.rb +3 -5
- data/lib/milk_tea/core/control_flow/constant_propagation.rb +14 -17
- data/lib/milk_tea/core/control_flow/definite_assignment.rb +0 -1
- data/lib/milk_tea/core/ir.rb +10 -10
- data/lib/milk_tea/core/lexer/character_classes.rb +5 -7
- data/lib/milk_tea/core/lexer/format_strings.rb +7 -9
- data/lib/milk_tea/core/lexer/heredocs.rb +22 -22
- data/lib/milk_tea/core/lexer/indentation.rb +0 -2
- data/lib/milk_tea/core/lexer/numbers.rb +0 -2
- data/lib/milk_tea/core/lexer/recovery.rb +0 -2
- data/lib/milk_tea/core/lexer/strings.rb +0 -2
- data/lib/milk_tea/core/lexer/symbols.rb +0 -2
- data/lib/milk_tea/core/lexer/trivia.rb +0 -2
- data/lib/milk_tea/core/lexer.rb +0 -2
- data/lib/milk_tea/core/lowering/async/analysis.rb +0 -2
- data/lib/milk_tea/core/lowering/async/lowering.rb +1264 -1265
- data/lib/milk_tea/core/lowering/async/normalization.rb +0 -2
- data/lib/milk_tea/core/lowering/async.rb +627 -630
- data/lib/milk_tea/core/lowering/block.rb +413 -416
- data/lib/milk_tea/core/lowering/calls.rb +1381 -1384
- data/lib/milk_tea/core/lowering/declarations.rb +180 -183
- data/lib/milk_tea/core/lowering/dyn.rb +48 -50
- data/lib/milk_tea/core/lowering/events.rb +983 -985
- data/lib/milk_tea/core/lowering/expressions.rb +1513 -1518
- data/lib/milk_tea/core/lowering/foreign_cstr.rb +181 -184
- data/lib/milk_tea/core/lowering/functions.rb +185 -188
- data/lib/milk_tea/core/lowering/loops.rb +1028 -1031
- data/lib/milk_tea/core/lowering/proc.rb +363 -366
- data/lib/milk_tea/core/lowering/resolve.rb +2133 -2135
- data/lib/milk_tea/core/lowering/scans.rb +179 -181
- data/lib/milk_tea/core/lowering/str_buffer.rb +102 -105
- data/lib/milk_tea/core/lowering/utils.rb +1040 -1044
- data/lib/milk_tea/core/lowering.rb +5 -7
- data/lib/milk_tea/core/module_binder.rb +0 -2
- data/lib/milk_tea/core/module_loader/errors.rb +4 -2
- data/lib/milk_tea/core/module_loader.rb +113 -20
- data/lib/milk_tea/core/module_path_resolver.rb +0 -2
- data/lib/milk_tea/core/module_roots.rb +6 -8
- data/lib/milk_tea/core/parser/attributes.rb +0 -2
- data/lib/milk_tea/core/parser/blocks.rb +0 -2
- data/lib/milk_tea/core/parser/declarations.rb +0 -2
- data/lib/milk_tea/core/parser/expressions.rb +31 -14
- data/lib/milk_tea/core/parser/recovery.rb +0 -2
- data/lib/milk_tea/core/parser/statements.rb +0 -2
- data/lib/milk_tea/core/parser/types.rb +8 -10
- data/lib/milk_tea/core/parser.rb +1 -3
- data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +31 -33
- data/lib/milk_tea/core/pretty_printer/base_formatter.rb +0 -2
- data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/attributes.rb +14 -16
- data/lib/milk_tea/core/semantic_analyzer/calls.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1 -14
- data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +18 -21
- data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +0 -3
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +12 -15
- data/lib/milk_tea/core/semantic_analyzer/generics.rb +57 -59
- data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +5 -9
- data/lib/milk_tea/core/semantic_analyzer/nullability.rb +0 -3
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +35 -40
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +10 -12
- data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +0 -6
- data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +102 -105
- data/lib/milk_tea/core/semantic_analyzer.rb +17 -8
- data/lib/milk_tea/core/types/layout.rb +21 -22
- data/lib/milk_tea/core/types/predicates.rb +0 -2
- data/lib/milk_tea/core/types/registry.rb +16 -18
- data/lib/milk_tea/core/types/visitor.rb +0 -4
- data/lib/milk_tea/dap/backends/lldb_dap.rb +0 -2
- data/lib/milk_tea/dap/protocol.rb +1 -1
- data/lib/milk_tea/dap/server/breakpoints.rb +0 -2
- data/lib/milk_tea/dap/server/debug_map.rb +0 -2
- data/lib/milk_tea/dap/server/handlers.rb +0 -2
- data/lib/milk_tea/dap/server/launch.rb +0 -2
- data/lib/milk_tea/dap/server/lldb_backend.rb +8 -10
- data/lib/milk_tea/dap/server/pause_diagnostics.rb +14 -16
- data/lib/milk_tea/dap/server/utilities.rb +0 -2
- data/lib/milk_tea/dap/server/wire.rb +0 -2
- data/lib/milk_tea/dap/server.rb +0 -2
- data/lib/milk_tea/lsp/diagnostics.rb +16 -16
- data/lib/milk_tea/lsp/server/call_hierarchy.rb +11 -12
- data/lib/milk_tea/lsp/server/code_actions.rb +355 -357
- data/lib/milk_tea/lsp/server/code_lens.rb +23 -25
- data/lib/milk_tea/lsp/server/completion.rb +873 -875
- data/lib/milk_tea/lsp/server/configuration.rb +129 -131
- data/lib/milk_tea/lsp/server/debug_info.rb +0 -2
- data/lib/milk_tea/lsp/server/definition.rb +564 -565
- data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +191 -193
- data/lib/milk_tea/lsp/server/execute_command.rb +0 -2
- data/lib/milk_tea/lsp/server/folding_range.rb +0 -4
- data/lib/milk_tea/lsp/server/formatting.rb +478 -480
- data/lib/milk_tea/lsp/server/hover.rb +1282 -1284
- data/lib/milk_tea/lsp/server/inlay_hints.rb +179 -181
- data/lib/milk_tea/lsp/server/lifecycle.rb +192 -194
- data/lib/milk_tea/lsp/server/linked_editing_range.rb +0 -2
- data/lib/milk_tea/lsp/server/on_type_formatting.rb +0 -2
- data/lib/milk_tea/lsp/server/progress.rb +0 -2
- data/lib/milk_tea/lsp/server/references.rb +316 -318
- data/lib/milk_tea/lsp/server/rename.rb +445 -447
- data/lib/milk_tea/lsp/server/selection_range.rb +0 -4
- data/lib/milk_tea/lsp/server/semantic_tokens.rb +1318 -1320
- data/lib/milk_tea/lsp/server/signature_help.rb +146 -148
- data/lib/milk_tea/lsp/server/text_documents.rb +99 -101
- data/lib/milk_tea/lsp/server/type_hierarchy.rb +2 -4
- data/lib/milk_tea/lsp/server/utilities.rb +397 -399
- data/lib/milk_tea/lsp/server.rb +0 -3
- data/lib/milk_tea/lsp/workspace/analysis.rb +40 -42
- data/lib/milk_tea/lsp/workspace/dependency_graph.rb +4 -10
- data/lib/milk_tea/lsp/workspace/utilities.rb +19 -19
- data/lib/milk_tea/packages/atomic_write.rb +4 -5
- data/lib/milk_tea/packages/dependency_solver.rb +15 -15
- data/lib/milk_tea/packages/lock.rb +4 -4
- data/lib/milk_tea/packages/manager_cli.rb +1 -1
- data/lib/milk_tea/packages/manifest.rb +3 -3
- data/lib/milk_tea/packages/registry_store.rb +4 -6
- data/lib/milk_tea/packages/services.rb +16 -16
- data/lib/milk_tea/packages/source_cache.rb +4 -6
- data/lib/milk_tea/packages/source_resolver.rb +44 -44
- data/lib/milk_tea/packages/version.rb +8 -8
- data/lib/milk_tea/tooling/build.rb +3 -7
- data/lib/milk_tea/tooling/build_cache.rb +0 -2
- data/lib/milk_tea/tooling/debug_info_formatter.rb +31 -33
- data/lib/milk_tea/tooling/debug_map.rb +8 -10
- data/lib/milk_tea/tooling/docs_app.rb +0 -2
- data/lib/milk_tea/tooling/linter/doc_tags.rb +25 -27
- data/lib/milk_tea/tooling/linter/fix_engine.rb +87 -24
- data/lib/milk_tea/tooling/linter/flow_rules.rb +44 -46
- data/lib/milk_tea/tooling/linter/imports_platform.rb +14 -16
- data/lib/milk_tea/tooling/linter/release_rules.rb +3 -5
- data/lib/milk_tea/tooling/linter/reserved_names.rb +23 -25
- data/lib/milk_tea/tooling/linter/rules.rb +87 -89
- data/lib/milk_tea/tooling/linter/source_helpers.rb +40 -42
- data/lib/milk_tea/tooling/linter/trailing_comma.rb +17 -19
- data/lib/milk_tea/tooling/linter/visitors.rb +162 -50
- data/lib/milk_tea/tooling/linter.rb +0 -2
- data/lib/milk_tea/tooling/project_scaffold.rb +0 -2
- data/lib/milk_tea/tooling/run.rb +0 -2
- data/lib/milk_tea/tooling/toolchain_cli.rb +0 -2
- data/std/cli.mt +13 -32
- data/std/color.mt +28 -28
- data/std/encoding.mt +4 -4
- data/std/graph.mt +1 -2
- data/std/hash.mt +14 -14
- data/std/http/server.mt +3 -3
- data/std/input.mt +1 -1
- data/std/json.mt +1 -1
- data/std/linear_algebra.mt +6 -6
- data/std/net/mux.mt +1 -1
- data/std/noise.mt +23 -23
- data/std/parse.mt +2 -2
- data/std/process.mt +3 -4
- data/std/sparse_set.mt +1 -1
- data/std/steering.mt +5 -5
- data/std/str.mt +1 -1
- data/std/tween.mt +4 -4
- data/std/utility.mt +3 -3
- 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
|
|
@@ -197,14 +195,20 @@ module MilkTea
|
|
|
197
195
|
with_scope { visit_statement_list(statement.body) }
|
|
198
196
|
@unsafe_depth -= 1
|
|
199
197
|
when AST::ForStmt
|
|
198
|
+
@loop_depth ||= 0
|
|
199
|
+
@loop_depth += 1
|
|
200
200
|
visit_expression(statement.iterable)
|
|
201
201
|
with_scope do
|
|
202
202
|
declare_local(statement.name, statement.line, column: statement.column, var: false) if statement.name
|
|
203
203
|
visit_statement_list(statement.body)
|
|
204
204
|
end
|
|
205
|
+
@loop_depth -= 1
|
|
205
206
|
when AST::WhileStmt
|
|
207
|
+
@loop_depth ||= 0
|
|
208
|
+
@loop_depth += 1
|
|
206
209
|
visit_expression(statement.condition)
|
|
207
210
|
with_scope { visit_statement_list(statement.body) }
|
|
211
|
+
@loop_depth -= 1
|
|
208
212
|
when AST::ReturnStmt
|
|
209
213
|
visit_expression(statement.value) if statement.value
|
|
210
214
|
flag_redundant_widening_cast(statement.value) if statement.value
|
|
@@ -293,7 +297,7 @@ module MilkTea
|
|
|
293
297
|
@unsafe_depth -= 1
|
|
294
298
|
when AST::ProcExpr
|
|
295
299
|
with_scope do
|
|
296
|
-
fallback_line = expression.
|
|
300
|
+
fallback_line = expression.line
|
|
297
301
|
expression.params.each do |param|
|
|
298
302
|
declare_param(
|
|
299
303
|
param.name,
|
|
@@ -319,7 +323,7 @@ module MilkTea
|
|
|
319
323
|
when AST::FormatString
|
|
320
324
|
expression.parts.each do |part|
|
|
321
325
|
next unless part.is_a?(AST::FormatExprPart)
|
|
322
|
-
|
|
326
|
+
|
|
323
327
|
visit_expression(part.expression)
|
|
324
328
|
end
|
|
325
329
|
when AST::IntegerLiteral, AST::FloatLiteral, AST::StringLiteral, AST::BooleanLiteral, AST::NullLiteral
|
|
@@ -331,6 +335,7 @@ module MilkTea
|
|
|
331
335
|
when AST::PrefixCast
|
|
332
336
|
visit_expression(expression.expression)
|
|
333
337
|
check_redundant_cast(expression)
|
|
338
|
+
check_redundant_cast_parens(expression)
|
|
334
339
|
else
|
|
335
340
|
nil
|
|
336
341
|
end
|
|
@@ -363,28 +368,28 @@ module MilkTea
|
|
|
363
368
|
end
|
|
364
369
|
def mark_assignment_target_reads(target, operator)
|
|
365
370
|
return if operator == "="
|
|
366
|
-
|
|
371
|
+
|
|
367
372
|
mark_used(target.name, identifier: target) if target.is_a?(AST::Identifier)
|
|
368
373
|
end
|
|
369
374
|
def mark_mutated(target)
|
|
370
375
|
return unless target.is_a?(AST::Identifier) || target.is_a?(AST::IndexAccess) || target.is_a?(AST::MemberAccess)
|
|
371
|
-
|
|
376
|
+
|
|
372
377
|
# For direct identifier assignment (e.g., x = value), mark x as mutated.
|
|
373
378
|
if target.is_a?(AST::Identifier)
|
|
374
379
|
@scopes.reverse_each do |scope|
|
|
375
380
|
binding = scope[target.name]
|
|
376
381
|
next unless binding
|
|
377
|
-
|
|
382
|
+
|
|
378
383
|
binding.mutated = true
|
|
379
384
|
return
|
|
380
385
|
end
|
|
381
386
|
end
|
|
382
|
-
|
|
387
|
+
|
|
383
388
|
# For index assignment (e.g., array[0] = value), mark the array as mutated.
|
|
384
389
|
if target.is_a?(AST::IndexAccess)
|
|
385
390
|
mark_mutated(target.receiver)
|
|
386
391
|
end
|
|
387
|
-
|
|
392
|
+
|
|
388
393
|
# For field assignment (e.g., rect.w = value), mark the struct variable as mutated.
|
|
389
394
|
if target.is_a?(AST::MemberAccess)
|
|
390
395
|
mark_mutated(target.receiver)
|
|
@@ -395,12 +400,12 @@ module MilkTea
|
|
|
395
400
|
mark_mutated(expression)
|
|
396
401
|
return
|
|
397
402
|
end
|
|
398
|
-
|
|
403
|
+
|
|
399
404
|
if expression.is_a?(AST::UnaryOp) && %w[out inout].include?(expression.operator)
|
|
400
405
|
mark_mutated(expression.operand)
|
|
401
406
|
return
|
|
402
407
|
end
|
|
403
|
-
|
|
408
|
+
|
|
404
409
|
# ref_of(x) and ptr_of(x) can expose writable aliases — treat as potential
|
|
405
410
|
# mutation since callees may write through them (common C-FFI out-param pattern).
|
|
406
411
|
if expression.is_a?(AST::Call) &&
|
|
@@ -412,10 +417,10 @@ module MilkTea
|
|
|
412
417
|
end
|
|
413
418
|
def mutating_argument_identifier?(expression)
|
|
414
419
|
return false unless expression.is_a?(AST::Identifier)
|
|
415
|
-
|
|
420
|
+
|
|
416
421
|
binding_resolution = @sema_facts&.binding_resolution
|
|
417
422
|
return false unless binding_resolution
|
|
418
|
-
|
|
423
|
+
|
|
419
424
|
binding_resolution.mutating_argument_identifier_ids&.key?(expression.object_id) ||
|
|
420
425
|
binding_resolution.mutable_lvalue_argument_identifier_ids&.key?(expression.object_id)
|
|
421
426
|
end
|
|
@@ -423,7 +428,7 @@ module MilkTea
|
|
|
423
428
|
return unless expression.is_a?(AST::Call)
|
|
424
429
|
return unless expression.callee.is_a?(AST::MemberAccess)
|
|
425
430
|
return unless editable_receiver_expression?(expression.callee.receiver)
|
|
426
|
-
|
|
431
|
+
|
|
427
432
|
mark_mutated(expression.callee.receiver)
|
|
428
433
|
end
|
|
429
434
|
def mark_alias_source_mutated(expression)
|
|
@@ -431,12 +436,12 @@ module MilkTea
|
|
|
431
436
|
return unless expression.callee.is_a?(AST::Identifier)
|
|
432
437
|
return unless %w[ref_of ptr_of].include?(expression.callee.name)
|
|
433
438
|
return unless expression.arguments.length == 1
|
|
434
|
-
|
|
439
|
+
|
|
435
440
|
mark_mutated(expression.arguments.first.value)
|
|
436
441
|
end
|
|
437
442
|
def editable_receiver_expression?(expression)
|
|
438
443
|
return true unless @sema_facts
|
|
439
|
-
|
|
444
|
+
|
|
440
445
|
@sema_facts&.binding_resolution&.editable_receiver_expression_ids&.key?(expression.object_id)
|
|
441
446
|
end
|
|
442
447
|
def with_scope
|
|
@@ -447,9 +452,9 @@ module MilkTea
|
|
|
447
452
|
end
|
|
448
453
|
def declare_local(name, line, column: nil, var: false)
|
|
449
454
|
return if ignored_binding_name?(name)
|
|
450
|
-
|
|
455
|
+
|
|
451
456
|
resolve_reserved_primitive_name_conflicts!(name)
|
|
452
|
-
|
|
457
|
+
|
|
453
458
|
replacement_name = nil
|
|
454
459
|
replacement_base_name = nil
|
|
455
460
|
if RESERVED_VALUE_TYPE_NAMES.include?(name)
|
|
@@ -459,7 +464,7 @@ module MilkTea
|
|
|
459
464
|
visible_binding_names(excluding_name: name),
|
|
460
465
|
)
|
|
461
466
|
end
|
|
462
|
-
|
|
467
|
+
|
|
463
468
|
# shadow: check whether any outer scope already has a binding for this name
|
|
464
469
|
if @scopes.length > 1
|
|
465
470
|
@scopes[0..-2].each do |outer_scope|
|
|
@@ -473,7 +478,7 @@ module MilkTea
|
|
|
473
478
|
end
|
|
474
479
|
end
|
|
475
480
|
end
|
|
476
|
-
|
|
481
|
+
|
|
477
482
|
@scopes.last[name] = Binding.new(
|
|
478
483
|
name:, line:, column:, used: false,
|
|
479
484
|
binding_kind: :local,
|
|
@@ -486,9 +491,9 @@ module MilkTea
|
|
|
486
491
|
end
|
|
487
492
|
def declare_param(name, line: nil, column: nil)
|
|
488
493
|
return if ignored_binding_name?(name)
|
|
489
|
-
|
|
494
|
+
|
|
490
495
|
resolve_reserved_primitive_name_conflicts!(name)
|
|
491
|
-
|
|
496
|
+
|
|
492
497
|
replacement_name = nil
|
|
493
498
|
replacement_base_name = nil
|
|
494
499
|
if RESERVED_VALUE_TYPE_NAMES.include?(name)
|
|
@@ -498,7 +503,7 @@ module MilkTea
|
|
|
498
503
|
visible_binding_names(excluding_name: name),
|
|
499
504
|
)
|
|
500
505
|
end
|
|
501
|
-
|
|
506
|
+
|
|
502
507
|
@scopes.last[name] = Binding.new(
|
|
503
508
|
name:, line:, column:, used: false,
|
|
504
509
|
binding_kind: :param,
|
|
@@ -776,6 +781,7 @@ module MilkTea
|
|
|
776
781
|
return unless stmts.all? { |s| inline_simple_statement?(s) }
|
|
777
782
|
|
|
778
783
|
return if visually_inline_if?(statement, stmts)
|
|
784
|
+
return if inline_if_too_wide?(statement, stmts)
|
|
779
785
|
|
|
780
786
|
emit_conciseness_hint(
|
|
781
787
|
"prefer-inline-if",
|
|
@@ -785,12 +791,57 @@ module MilkTea
|
|
|
785
791
|
)
|
|
786
792
|
end
|
|
787
793
|
|
|
794
|
+
def inline_if_too_wide?(statement, stmts)
|
|
795
|
+
return false if statement.branches.empty?
|
|
796
|
+
|
|
797
|
+
text = +""
|
|
798
|
+
|
|
799
|
+
statement.branches.each_with_index do |b, i|
|
|
800
|
+
cond_src = source_line_from(b.line, b.column)
|
|
801
|
+
return false unless cond_src
|
|
802
|
+
|
|
803
|
+
stmt_col = stmts[i]&.column
|
|
804
|
+
stmt_line = stmts[i]&.line
|
|
805
|
+
body_src = source_line_from(stmt_line, stmt_col)
|
|
806
|
+
return false unless body_src
|
|
807
|
+
|
|
808
|
+
text << cond_src
|
|
809
|
+
text << " "
|
|
810
|
+
text << body_src
|
|
811
|
+
text << " "
|
|
812
|
+
end
|
|
813
|
+
|
|
814
|
+
if statement.else_line
|
|
815
|
+
else_src = source_line_from(statement.else_line, statement.else_column)
|
|
816
|
+
return false unless else_src
|
|
817
|
+
text << else_src
|
|
818
|
+
text << " "
|
|
819
|
+
end
|
|
820
|
+
|
|
821
|
+
stmt_col = stmts.last&.column
|
|
822
|
+
stmt_line = stmts.last&.line
|
|
823
|
+
else_body = source_line_from(stmt_line, stmt_col)
|
|
824
|
+
return false unless else_body
|
|
825
|
+
text << else_body
|
|
826
|
+
|
|
827
|
+
text.strip.length > 120
|
|
828
|
+
end
|
|
829
|
+
|
|
830
|
+
def source_line_from(line, column)
|
|
831
|
+
return nil unless line
|
|
832
|
+
line_idx = Integer(line) - 1
|
|
833
|
+
return nil if line_idx < 0 || line_idx >= @source_lines.length
|
|
834
|
+
raw = @source_lines[line_idx].to_s
|
|
835
|
+
return raw.strip if column.nil?
|
|
836
|
+
raw[Integer(column) - 1..]&.strip
|
|
837
|
+
end
|
|
838
|
+
|
|
788
839
|
def visually_inline_if?(statement, stmts)
|
|
789
840
|
n = statement.branches.length
|
|
790
841
|
statement.branches.each_with_index.all? { |b, i|
|
|
791
|
-
stmts[i].
|
|
842
|
+
stmts[i].line == b.line
|
|
792
843
|
} && (
|
|
793
|
-
stmts[n].
|
|
844
|
+
stmts[n].line && statement.else_line && stmts[n].line == statement.else_line
|
|
794
845
|
)
|
|
795
846
|
end
|
|
796
847
|
|
|
@@ -801,6 +852,7 @@ module MilkTea
|
|
|
801
852
|
return unless statement.else_body
|
|
802
853
|
|
|
803
854
|
stmts = (statement.branches.map(&:body) + [statement.else_body]).map { |b| single_statement_body(b) }
|
|
855
|
+
return if inline_if_too_wide?(statement, stmts)
|
|
804
856
|
report_conditional_expression(stmts, line: statement.line, column: statement.branches.first&.column, kind: "if")
|
|
805
857
|
end
|
|
806
858
|
|
|
@@ -814,6 +866,7 @@ module MilkTea
|
|
|
814
866
|
|
|
815
867
|
def report_conditional_expression(stmts, line:, column:, kind:)
|
|
816
868
|
return if stmts.empty? || stmts.any?(&:nil?)
|
|
869
|
+
return if @loop_depth && @loop_depth > 0
|
|
817
870
|
|
|
818
871
|
if stmts.all? { |s| s.is_a?(AST::ReturnStmt) && s.value }
|
|
819
872
|
emit_conciseness_hint(
|
|
@@ -843,8 +896,8 @@ module MilkTea
|
|
|
843
896
|
next unless node_fingerprint(body_of.call(first)) == node_fingerprint(body_of.call(second))
|
|
844
897
|
|
|
845
898
|
pattern = second.pattern
|
|
846
|
-
pattern_line = pattern.
|
|
847
|
-
pattern_column = pattern.
|
|
899
|
+
pattern_line = pattern.line || second.binding_line
|
|
900
|
+
pattern_column = pattern.column || second.binding_column
|
|
848
901
|
emit_conciseness_hint(
|
|
849
902
|
"prefer-or-pattern",
|
|
850
903
|
line: pattern_line,
|
|
@@ -881,8 +934,8 @@ module MilkTea
|
|
|
881
934
|
source_text = expr_source_name(copied.first.value.receiver)
|
|
882
935
|
emit_conciseness_hint(
|
|
883
936
|
"prefer-struct-with",
|
|
884
|
-
line: call.callee.
|
|
885
|
-
column: call.callee.
|
|
937
|
+
line: call.callee.line,
|
|
938
|
+
column: call.callee.column,
|
|
886
939
|
message: "copies #{copied.size} field(s) from `#{source_text}`; use `#{source_text}.with(#{changed_names.join(", ")} = …)`",
|
|
887
940
|
)
|
|
888
941
|
end
|
|
@@ -914,8 +967,8 @@ module MilkTea
|
|
|
914
967
|
|
|
915
968
|
emit_conciseness_hint(
|
|
916
969
|
"prefer-try",
|
|
917
|
-
line: scrutinee.
|
|
918
|
-
column: scrutinee.
|
|
970
|
+
line: scrutinee.line,
|
|
971
|
+
column: scrutinee.column,
|
|
919
972
|
message: "this #{base} match only propagates the failure branch; consider `expr?`",
|
|
920
973
|
)
|
|
921
974
|
end
|
|
@@ -991,6 +1044,65 @@ module MilkTea
|
|
|
991
1044
|
# check_boundary_widening_cast at the specific boundary sites.
|
|
992
1045
|
end
|
|
993
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
|
+
|
|
994
1106
|
# Reports a widening cast as redundant only when it sits *directly* at a
|
|
995
1107
|
# coercion slot: the RHS of a typed `let`/`var`, a `return` value, or the
|
|
996
1108
|
# RHS of a plain `=` assignment. At those positions the slot type is
|
|
@@ -1034,8 +1146,8 @@ module MilkTea
|
|
|
1034
1146
|
end
|
|
1035
1147
|
|
|
1036
1148
|
def emit_redundant_cast(cast_expr, target_name, reason)
|
|
1037
|
-
line = cast_expr.
|
|
1038
|
-
column = cast_expr.
|
|
1149
|
+
line = cast_expr.line || expression_line(cast_expr)
|
|
1150
|
+
column = cast_expr.column || expression_column(cast_expr)
|
|
1039
1151
|
|
|
1040
1152
|
@warnings << Warning.new(
|
|
1041
1153
|
path: @path,
|
|
@@ -1092,15 +1204,15 @@ module MilkTea
|
|
|
1092
1204
|
end
|
|
1093
1205
|
end
|
|
1094
1206
|
# ── borrow facts helpers ──────────────────────────────────────────────────
|
|
1095
|
-
|
|
1207
|
+
|
|
1096
1208
|
BORROW_CALL_NAMES = %w[ref_of ptr_of].freeze
|
|
1097
|
-
|
|
1209
|
+
|
|
1098
1210
|
def collect_borrowed_names(stmts)
|
|
1099
1211
|
names = Set.new
|
|
1100
1212
|
stmts.each { |s| collect_borrows_from_stmt(s, names) }
|
|
1101
1213
|
names
|
|
1102
1214
|
end
|
|
1103
|
-
|
|
1215
|
+
|
|
1104
1216
|
def collect_borrows_from_stmt(stmt, names)
|
|
1105
1217
|
case stmt
|
|
1106
1218
|
when AST::LocalDecl
|
|
@@ -1136,7 +1248,7 @@ module MilkTea
|
|
|
1136
1248
|
stmt.else_body&.each { |s| collect_borrows_from_stmt(s, names) }
|
|
1137
1249
|
end
|
|
1138
1250
|
end
|
|
1139
|
-
|
|
1251
|
+
|
|
1140
1252
|
def collect_borrows_from_expr(expr, names, inside_call_argument: false)
|
|
1141
1253
|
case expr
|
|
1142
1254
|
when nil then nil
|
|
@@ -1168,13 +1280,13 @@ module MilkTea
|
|
|
1168
1280
|
collect_borrows_from_expr(expr.index, names, inside_call_argument:)
|
|
1169
1281
|
end
|
|
1170
1282
|
end
|
|
1171
|
-
|
|
1283
|
+
|
|
1172
1284
|
def collect_written_names(stmts)
|
|
1173
1285
|
names = Set.new
|
|
1174
1286
|
stmts.each { |s| collect_writes_from_stmt(s, names) }
|
|
1175
1287
|
names
|
|
1176
1288
|
end
|
|
1177
|
-
|
|
1289
|
+
|
|
1178
1290
|
def collect_writes_from_stmt(stmt, names)
|
|
1179
1291
|
case stmt
|
|
1180
1292
|
when AST::Assignment
|
|
@@ -1198,15 +1310,15 @@ module MilkTea
|
|
|
1198
1310
|
stmt.body&.each { |s| collect_writes_from_stmt(s, names) }
|
|
1199
1311
|
end
|
|
1200
1312
|
end
|
|
1201
|
-
|
|
1313
|
+
|
|
1202
1314
|
def collect_writes_from_stmt_list(stmts, names)
|
|
1203
1315
|
stmts.each { |s| collect_writes_from_stmt(s, names) }
|
|
1204
1316
|
end
|
|
1205
|
-
|
|
1317
|
+
|
|
1206
1318
|
def stmt_sub_stmts(_stmt)
|
|
1207
1319
|
[]
|
|
1208
1320
|
end
|
|
1209
|
-
|
|
1321
|
+
|
|
1210
1322
|
def find_borrow_location(stmts, name)
|
|
1211
1323
|
stmts.each do |stmt|
|
|
1212
1324
|
location = find_borrow_location_in_stmt(stmt, name)
|
|
@@ -1214,7 +1326,7 @@ module MilkTea
|
|
|
1214
1326
|
end
|
|
1215
1327
|
nil
|
|
1216
1328
|
end
|
|
1217
|
-
|
|
1329
|
+
|
|
1218
1330
|
def find_borrow_location_in_stmt(stmt, name)
|
|
1219
1331
|
case stmt
|
|
1220
1332
|
when AST::LocalDecl
|
|
@@ -1266,7 +1378,7 @@ module MilkTea
|
|
|
1266
1378
|
nil
|
|
1267
1379
|
end
|
|
1268
1380
|
end
|
|
1269
|
-
|
|
1381
|
+
|
|
1270
1382
|
def find_borrow_location_in_expr(expr, name)
|
|
1271
1383
|
case expr
|
|
1272
1384
|
when nil then nil
|
data/lib/milk_tea/tooling/run.rb
CHANGED
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
|
-
|
|
489
|
-
|
|
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
|
-
|
|
501
|
-
|
|
502
|
-
|
|
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
|
-
|
|
514
|
-
|
|
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
|
-
|
|
541
|
-
|
|
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
|
-
|
|
551
|
-
|
|
552
|
-
|
|
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
|
-
|
|
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
|
|