mt-lang 0.3.34 → 0.3.38
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/README.md +2 -2
- data/docs/index.html +5 -5
- data/docs/language-design.md +3 -3
- data/docs/language-manual.md +1 -1
- data/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/bindings/bindgen/type_mapper.rb +12 -2
- data/lib/milk_tea/bindings/bindgen.rb +5 -0
- data/lib/milk_tea/core/c_backend/aggregate_utils.rb +4 -0
- data/lib/milk_tea/core/c_backend/expressions.rb +25 -3
- data/lib/milk_tea/core/c_backend/reinterpret.rb +2 -2
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +15 -2
- data/lib/milk_tea/core/c_backend/type_collectors.rb +39 -2
- data/lib/milk_tea/core/c_backend.rb +12 -0
- data/lib/milk_tea/core/compile_time.rb +109 -74
- data/lib/milk_tea/core/lexer.rb +12 -0
- data/lib/milk_tea/core/lowering/block.rb +6 -7
- data/lib/milk_tea/core/lowering/declarations.rb +40 -1
- data/lib/milk_tea/core/lowering/loops.rb +25 -1
- data/lib/milk_tea/core/lowering/resolve.rb +13 -6
- data/lib/milk_tea/core/lowering/utils.rb +6 -2
- data/lib/milk_tea/core/lowering.rb +12 -0
- data/lib/milk_tea/core/module_loader.rb +59 -37
- data/lib/milk_tea/core/parser/statements.rb +35 -7
- data/lib/milk_tea/core/parser.rb +12 -0
- data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +4 -1
- data/lib/milk_tea/core/semantic_analyzer/attributes.rb +2 -1
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +8 -20
- data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +2 -1
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +12 -2
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +10 -10
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +10 -12
- data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +14 -5
- data/lib/milk_tea/core/semantic_analyzer.rb +6 -31
- data/lib/milk_tea/lsp/server/code_actions.rb +12 -2
- data/lib/milk_tea/lsp/server/formatting.rb +172 -58
- data/lib/milk_tea/lsp/server/inlay_hints.rb +33 -11
- data/lib/milk_tea/lsp/server/selection_range.rb +4 -4
- data/lib/milk_tea/lsp/server/semantic_tokens.rb +9 -5
- data/lib/milk_tea/lsp/server/type_hierarchy.rb +5 -5
- data/lib/milk_tea/packages/manifest.rb +9 -0
- data/lib/milk_tea/tooling/linter/visitors.rb +4 -1
- data/std/box2d.mt +14 -14
- data/std/c/box2d.mt +19 -19
- metadata +3 -3
|
@@ -101,11 +101,21 @@ module MilkTea
|
|
|
101
101
|
|
|
102
102
|
def find_method_by_receiver_name(module_binding, receiver_type, name)
|
|
103
103
|
module_binding.methods.each do |key, methods|
|
|
104
|
-
|
|
104
|
+
next unless key.is_a?(receiver_type.class)
|
|
105
|
+
next unless key.name == receiver_type.name
|
|
106
|
+
next unless same_type_module?(key, receiver_type)
|
|
107
|
+
return methods[name] if methods.key?(name)
|
|
105
108
|
end
|
|
106
109
|
nil
|
|
107
110
|
end
|
|
108
111
|
|
|
112
|
+
def same_type_module?(type_a, type_b)
|
|
113
|
+
module_a = receiver_type_module_name(type_a)
|
|
114
|
+
module_b = receiver_type_module_name(type_b)
|
|
115
|
+
return true if module_a.nil? || module_b.nil?
|
|
116
|
+
module_a == module_b
|
|
117
|
+
end
|
|
118
|
+
|
|
109
119
|
def reachable_module_binding_for_type(receiver_type)
|
|
110
120
|
module_name = receiver_type_module_name(receiver_type)
|
|
111
121
|
return nil unless module_name
|
|
@@ -709,7 +719,7 @@ module MilkTea
|
|
|
709
719
|
|
|
710
720
|
def sized_layout_type?(type)
|
|
711
721
|
case type
|
|
712
|
-
when Types::Primitive, Types::Struct, Types::StructInstance, Types::Union, Types::Enum, Types::Flags, Types::Variant, Types::Span, Types::StringView, Types::Task, Types::Event, Types::Subscription
|
|
722
|
+
when Types::Primitive, Types::Struct, Types::StructInstance, Types::Union, Types::Enum, Types::Flags, Types::Variant, Types::Tuple, Types::Span, Types::StringView, Types::Task, Types::Event, Types::Subscription
|
|
713
723
|
true
|
|
714
724
|
when Types::Nullable
|
|
715
725
|
true
|
|
@@ -34,17 +34,17 @@ module MilkTea
|
|
|
34
34
|
end
|
|
35
35
|
record_local_completion_snapshot(end_line, 1_000_000, nested_scopes)
|
|
36
36
|
rescue SemanticError => e
|
|
37
|
-
if
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
if e.line.nil? && statement.line
|
|
38
|
+
e = SemanticError.new(
|
|
39
|
+
e.message,
|
|
40
|
+
line: statement.line,
|
|
41
|
+
column: source_column(statement),
|
|
42
|
+
length: source_length(statement),
|
|
43
|
+
path: @path,
|
|
44
|
+
)
|
|
40
45
|
end
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
stmt_line = statement.line
|
|
45
|
-
raise e if stmt_line.nil?
|
|
46
|
-
|
|
47
|
-
raise_sema_error(e.message, statement)
|
|
46
|
+
@structural_errors << e
|
|
47
|
+
next
|
|
48
48
|
end
|
|
49
49
|
end
|
|
50
50
|
end
|
|
@@ -18,7 +18,8 @@ module MilkTea
|
|
|
18
18
|
collect_structural_error(e)
|
|
19
19
|
end
|
|
20
20
|
when AST::VarDecl
|
|
21
|
-
binding = @ctx.top_level_values
|
|
21
|
+
binding = @ctx.top_level_values[decl.name]
|
|
22
|
+
next unless binding
|
|
22
23
|
if decl.value
|
|
23
24
|
validate_consuming_foreign_expression!(decl.value, scopes: [], root_allowed: false)
|
|
24
25
|
validate_hoistable_foreign_expression!(decl.value, scopes: [], root_hoistable: false)
|
|
@@ -39,7 +40,8 @@ module MilkTea
|
|
|
39
40
|
end
|
|
40
41
|
|
|
41
42
|
def check_expr_const(decl)
|
|
42
|
-
binding = @ctx.top_level_values
|
|
43
|
+
binding = @ctx.top_level_values[decl.name]
|
|
44
|
+
return unless binding
|
|
43
45
|
validate_consuming_foreign_expression!(decl.value, scopes: [], root_allowed: false)
|
|
44
46
|
validate_hoistable_foreign_expression!(decl.value, scopes: [], root_hoistable: false)
|
|
45
47
|
|
|
@@ -173,7 +175,7 @@ module MilkTea
|
|
|
173
175
|
|
|
174
176
|
if (type_ref = type_ref_from_specialization(callee))
|
|
175
177
|
specialized_type = resolve_type_ref(type_ref)
|
|
176
|
-
return if specialized_type.is_a?(Types::Struct) ||
|
|
178
|
+
return if specialized_type.is_a?(Types::Struct) || task_type?(specialized_type) || specialized_type.is_a?(Types::Vector) || specialized_type.is_a?(Types::Matrix) || specialized_type.is_a?(Types::Quaternion) || specialized_type.is_a?(Types::Simd)
|
|
177
179
|
end
|
|
178
180
|
end
|
|
179
181
|
|
|
@@ -270,9 +272,7 @@ module MilkTea
|
|
|
270
272
|
def evaluate_compile_time_block(statements, scopes: nil)
|
|
271
273
|
ctx = CompileTime::BlockContext.new(self)
|
|
272
274
|
result = ctx.evaluate_block(statements, scopes:)
|
|
273
|
-
result
|
|
274
|
-
rescue CompileTime::ReturnValue => e
|
|
275
|
-
e.value
|
|
275
|
+
result.is_a?(CompileTime::ReturnOutcome) ? result.value : result
|
|
276
276
|
rescue CompileTime::Error => e
|
|
277
277
|
raise_sema_error(e.message)
|
|
278
278
|
end
|
|
@@ -558,9 +558,8 @@ module MilkTea
|
|
|
558
558
|
end
|
|
559
559
|
|
|
560
560
|
ctx = CompileTime::BlockContext.new(self, initial_variables: initial_vars)
|
|
561
|
-
ctx.evaluate_block(func.ast.body, scopes: nil)
|
|
562
|
-
|
|
563
|
-
e.value
|
|
561
|
+
result = ctx.evaluate_block(func.ast.body, scopes: nil)
|
|
562
|
+
result.is_a?(CompileTime::ReturnOutcome) ? result.value : result
|
|
564
563
|
rescue CompileTime::Error => e
|
|
565
564
|
raise_sema_error(e.message)
|
|
566
565
|
end
|
|
@@ -589,9 +588,8 @@ module MilkTea
|
|
|
589
588
|
end
|
|
590
589
|
|
|
591
590
|
ctx = CompileTime::BlockContext.new(self, initial_variables: initial_vars)
|
|
592
|
-
ctx.evaluate_block(func.ast.body, scopes: nil)
|
|
593
|
-
|
|
594
|
-
e.value
|
|
591
|
+
result = ctx.evaluate_block(func.ast.body, scopes: nil)
|
|
592
|
+
result.is_a?(CompileTime::ReturnOutcome) ? result.value : result
|
|
595
593
|
rescue CompileTime::Error => e
|
|
596
594
|
raise_sema_error(e.message)
|
|
597
595
|
end
|
|
@@ -293,9 +293,15 @@ module MilkTea
|
|
|
293
293
|
|
|
294
294
|
constraints = resolve_type_param_constraints(decl.type_params)
|
|
295
295
|
if decl.is_a?(AST::InterfaceDecl)
|
|
296
|
-
|
|
296
|
+
interface_binding = @ctx.interfaces[decl.name]
|
|
297
|
+
next unless interface_binding
|
|
298
|
+
|
|
299
|
+
@ctx.interfaces[decl.name] = interface_binding.with(type_param_constraints: constraints)
|
|
297
300
|
else
|
|
298
|
-
@ctx.types
|
|
301
|
+
type_binding = @ctx.types[decl.name]
|
|
302
|
+
next unless type_binding
|
|
303
|
+
|
|
304
|
+
type_binding.define_type_param_constraints(constraints)
|
|
299
305
|
end
|
|
300
306
|
end
|
|
301
307
|
end
|
|
@@ -455,7 +461,8 @@ module MilkTea
|
|
|
455
461
|
with_error_node(decl) do
|
|
456
462
|
next unless decl.is_a?(AST::StructDecl) || decl.is_a?(AST::UnionDecl)
|
|
457
463
|
|
|
458
|
-
struct_type = @ctx.types
|
|
464
|
+
struct_type = @ctx.types[decl.name]
|
|
465
|
+
next unless struct_type
|
|
459
466
|
struct_type.ast_declaration = decl if struct_type.respond_to?(:ast_declaration=)
|
|
460
467
|
type_params = if struct_type.is_a?(Types::GenericStructDefinition)
|
|
461
468
|
seen = {}
|
|
@@ -581,7 +588,8 @@ module MilkTea
|
|
|
581
588
|
with_error_node(decl) do
|
|
582
589
|
next unless decl.is_a?(AST::EnumDecl) || decl.is_a?(AST::FlagsDecl)
|
|
583
590
|
|
|
584
|
-
enum_type = @ctx.types
|
|
591
|
+
enum_type = @ctx.types[decl.name]
|
|
592
|
+
next unless enum_type
|
|
585
593
|
backing_type = resolve_type_ref(decl.backing_type)
|
|
586
594
|
unless backing_type.is_a?(Types::Primitive) && backing_type.integer?
|
|
587
595
|
raise_sema_error("#{decl.name} backing type must be an integer primitive, got #{backing_type}")
|
|
@@ -641,7 +649,8 @@ module MilkTea
|
|
|
641
649
|
with_error_node(decl) do
|
|
642
650
|
next unless decl.is_a?(AST::VariantDecl)
|
|
643
651
|
|
|
644
|
-
variant_type = @ctx.types
|
|
652
|
+
variant_type = @ctx.types[decl.name]
|
|
653
|
+
next unless variant_type
|
|
645
654
|
type_params = if variant_type.is_a?(Types::GenericVariantDefinition)
|
|
646
655
|
seen = {}
|
|
647
656
|
variant_type.type_params.each_with_object({}) do |name, params|
|
|
@@ -187,31 +187,9 @@ module MilkTea
|
|
|
187
187
|
end
|
|
188
188
|
|
|
189
189
|
def check
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
run_phase(:install_builtin_attributes)
|
|
194
|
-
run_phase(:install_imports)
|
|
195
|
-
run_phase(:install_prelude_types, requires: [:install_imports])
|
|
196
|
-
run_phase(:declare_named_types, requires: [:install_builtin_types, :install_imports, :install_prelude_types])
|
|
197
|
-
run_phase(:resolve_generic_type_param_constraints, requires: [:declare_named_types])
|
|
198
|
-
run_phase(:resolve_type_aliases, requires: [:declare_named_types])
|
|
199
|
-
run_phase(:declare_attributes)
|
|
200
|
-
run_phase(:predeclare_top_level_consts)
|
|
201
|
-
run_phase(:resolve_aggregate_fields, requires: [:resolve_type_aliases, :declare_named_types])
|
|
202
|
-
run_phase(:resolve_enum_members, requires: [:declare_named_types])
|
|
203
|
-
run_phase(:resolve_variant_arms, requires: [:declare_named_types])
|
|
204
|
-
run_phase(:collect_emit_declarations)
|
|
205
|
-
run_phase(:declare_top_level_values, requires: [:resolve_aggregate_fields, :resolve_type_aliases])
|
|
206
|
-
run_phase(:check_attribute_applications, requires: [:declare_attributes])
|
|
207
|
-
run_phase(:declare_functions, requires: [:resolve_aggregate_fields, :resolve_enum_members, :resolve_variant_arms])
|
|
208
|
-
run_phase(:check_interface_conformances, requires: [:declare_functions, :resolve_aggregate_fields])
|
|
209
|
-
run_phase(:check_top_level_values, requires: [:declare_top_level_values])
|
|
210
|
-
run_phase(:finalize_top_level_const_values, requires: [:check_top_level_values])
|
|
211
|
-
run_phase(:check_top_level_static_asserts, requires: [:finalize_top_level_const_values])
|
|
212
|
-
run_phase(:check_functions, requires: [:declare_functions, :resolve_aggregate_fields, :check_interface_conformances])
|
|
213
|
-
|
|
214
|
-
build_analysis
|
|
190
|
+
result = check_collecting_errors
|
|
191
|
+
raise result[:errors].first unless result[:errors].empty?
|
|
192
|
+
result[:analysis]
|
|
215
193
|
end
|
|
216
194
|
|
|
217
195
|
def run_phase(name, requires: [])
|
|
@@ -291,12 +269,11 @@ module MilkTea
|
|
|
291
269
|
end
|
|
292
270
|
end
|
|
293
271
|
|
|
294
|
-
#
|
|
295
|
-
# Structural phases
|
|
296
|
-
#
|
|
272
|
+
# Runs all sema phases and collects every error instead of stopping at
|
|
273
|
+
# the first one. Structural phases collect per-declaration, and
|
|
274
|
+
# function-body phases collect per function/method.
|
|
297
275
|
# Returns { analysis: Analysis, errors: [SemanticError] }.
|
|
298
276
|
def check_collecting_errors
|
|
299
|
-
@collecting_errors = true
|
|
300
277
|
@structural_errors = []
|
|
301
278
|
@completed_phases = Set.new
|
|
302
279
|
|
|
@@ -353,8 +330,6 @@ module MilkTea
|
|
|
353
330
|
end
|
|
354
331
|
|
|
355
332
|
def collect_structural_error(error)
|
|
356
|
-
raise error unless @collecting_errors
|
|
357
|
-
|
|
358
333
|
@structural_errors << error
|
|
359
334
|
end
|
|
360
335
|
end
|
|
@@ -404,6 +404,12 @@ module MilkTea
|
|
|
404
404
|
end
|
|
405
405
|
|
|
406
406
|
def handle_workspace_diagnostic(params)
|
|
407
|
+
progress = nil
|
|
408
|
+
if (work_done_token = params['workDoneToken'])
|
|
409
|
+
progress = create_progress_handle(@protocol, work_done_token)
|
|
410
|
+
progress.report(percentage: 0, message: 'Collecting workspace diagnostics...')
|
|
411
|
+
end
|
|
412
|
+
|
|
407
413
|
previous_ids = params['previousResultIds'] || []
|
|
408
414
|
prev_map = previous_ids.each_with_object({}) do |entry, h|
|
|
409
415
|
h[entry['uri']] = entry['value'] if entry.is_a?(Hash) && entry['uri']
|
|
@@ -420,15 +426,19 @@ module MilkTea
|
|
|
420
426
|
|
|
421
427
|
cached = @workspace_diagnostic_cache[uri]
|
|
422
428
|
if cached && cached[:result_id] == prev_map[uri] && cached[:fingerprint] == fingerprint
|
|
423
|
-
{ uri: uri, kind: 'unchanged', resultId: result_id, items: [] }
|
|
429
|
+
{ uri: uri, kind: 'unchanged', resultId: result_id, items: [], version: nil }
|
|
424
430
|
else
|
|
425
431
|
@workspace_diagnostic_cache[uri] = { result_id: result_id, fingerprint: fingerprint }
|
|
426
|
-
{ uri: uri, kind: 'full', resultId: result_id, items: diagnostics }
|
|
432
|
+
{ uri: uri, kind: 'full', resultId: result_id, items: diagnostics, version: nil }
|
|
427
433
|
end
|
|
428
434
|
end
|
|
429
435
|
|
|
436
|
+
progress&.report(percentage: 100, message: "#{items.length} document#{items.length == 1 ? '' : 's'} checked")
|
|
437
|
+
progress&.done(message: 'Workspace diagnostics ready')
|
|
438
|
+
|
|
430
439
|
{ items: items }
|
|
431
440
|
rescue StandardError => e
|
|
441
|
+
progress&.done(message: 'Workspace diagnostics failed')
|
|
432
442
|
warn "Error in workspace/diagnostic handler: #{e.message}"
|
|
433
443
|
{ items: [] }
|
|
434
444
|
end
|
|
@@ -16,7 +16,8 @@ module MilkTea
|
|
|
16
16
|
# Enrich with hierarchical children from AST
|
|
17
17
|
ast = @workspace.get_ast(uri)
|
|
18
18
|
if ast && result
|
|
19
|
-
|
|
19
|
+
facts = @workspace.get_facts(uri)
|
|
20
|
+
enrich_with_children(result, ast, facts)
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
module_name = resolve_outline_module_name(uri)
|
|
@@ -74,17 +75,42 @@ module MilkTea
|
|
|
74
75
|
children
|
|
75
76
|
end
|
|
76
77
|
|
|
77
|
-
def enrich_with_children(symbols, ast)
|
|
78
|
+
def enrich_with_children(symbols, ast, facts)
|
|
78
79
|
removed_local_names = []
|
|
79
80
|
removed_method_names = []
|
|
80
81
|
removed_nested_type_names = []
|
|
81
82
|
removed_event_names = []
|
|
82
83
|
name_index = symbols.each_with_object(Hash.new { |h, k| h[k] = [] }) { |s, h| h[s[:name]] << s }
|
|
83
84
|
|
|
84
|
-
ast.declarations
|
|
85
|
+
flatten_module_declarations(ast.declarations).each do |decl|
|
|
85
86
|
removed_nested_type_names.concat(collect_nested_type_names(decl)) if decl.is_a?(AST::StructDecl)
|
|
86
87
|
|
|
87
88
|
case decl
|
|
89
|
+
when AST::VarDecl
|
|
90
|
+
parent = name_index[decl.name]&.find { |s| symbol_line(s) == (decl.line || 0) }
|
|
91
|
+
next unless parent
|
|
92
|
+
|
|
93
|
+
detail = decl.type ? type_detail_string(decl.type) : nil
|
|
94
|
+
detail ||= resolved_local_type_detail(decl, facts)
|
|
95
|
+
parent[:detail] = detail if detail
|
|
96
|
+
|
|
97
|
+
when AST::TypeAliasDecl
|
|
98
|
+
parent = name_index[decl.name]&.find { |s| symbol_line(s) == (decl.line || 0) }
|
|
99
|
+
next unless parent
|
|
100
|
+
|
|
101
|
+
if (detail = type_detail_string(decl.target))
|
|
102
|
+
parent[:detail] = "= #{detail}"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
when AST::ExternFunctionDecl, AST::ForeignFunctionDecl
|
|
106
|
+
parent = name_index[decl.name]&.find { |s| symbol_line(s) == (decl.line || 0) }
|
|
107
|
+
next unless parent
|
|
108
|
+
|
|
109
|
+
detail_parts = []
|
|
110
|
+
detail_parts << 'async' if decl.respond_to?(:async) && decl.async
|
|
111
|
+
detail_parts << "-> #{type_detail_string(decl.return_type) || 'void'}"
|
|
112
|
+
parent[:detail] = detail_parts.join(' ')
|
|
113
|
+
|
|
88
114
|
when AST::EventDecl
|
|
89
115
|
parent = name_index[decl.name]&.find { |s| symbol_line(s) == (decl.line || 0) }
|
|
90
116
|
next unless parent
|
|
@@ -100,26 +126,11 @@ module MilkTea
|
|
|
100
126
|
detail_parts = []
|
|
101
127
|
detail_parts << 'const' if decl.respond_to?(:const) && decl.const
|
|
102
128
|
detail_parts << 'async' if decl.respond_to?(:async) && decl.async
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
end
|
|
106
|
-
parent[:detail] = detail_parts.join(' ') unless detail_parts.empty?
|
|
129
|
+
detail_parts << "-> #{type_detail_string(decl.return_type) || 'void'}"
|
|
130
|
+
parent[:detail] = detail_parts.join(' ')
|
|
107
131
|
|
|
108
132
|
locals = collect_local_decls(decl.body)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
parent[:children] ||= []
|
|
112
|
-
parent_children = parent[:children]
|
|
113
|
-
locals.each do |local|
|
|
114
|
-
next unless local.name
|
|
115
|
-
|
|
116
|
-
child = local_decl_symbol(local)
|
|
117
|
-
next unless child
|
|
118
|
-
|
|
119
|
-
parent_children << child unless parent_children.any? { |pc| pc[:name] == child[:name] }
|
|
120
|
-
removed_local_names << local.name
|
|
121
|
-
removed_local_names.concat(descendant_names(child))
|
|
122
|
-
end
|
|
133
|
+
append_local_children(parent, locals, facts, removed_local_names)
|
|
123
134
|
when AST::ExtendingBlock
|
|
124
135
|
type_name_str = decl.type_name.name.parts.join('.')
|
|
125
136
|
|
|
@@ -153,20 +164,7 @@ module MilkTea
|
|
|
153
164
|
removed_method_names << child[:name] if child[:kind] == 6
|
|
154
165
|
|
|
155
166
|
locals = collect_local_decls(method.respond_to?(:body) ? method.body : nil)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
child[:children] ||= []
|
|
159
|
-
child_children = child[:children]
|
|
160
|
-
locals.each do |local|
|
|
161
|
-
next unless local.name
|
|
162
|
-
|
|
163
|
-
local_child = local_decl_symbol(local)
|
|
164
|
-
next unless local_child
|
|
165
|
-
|
|
166
|
-
child_children << local_child unless child_children.any? { |pc| pc[:name] == local_child[:name] }
|
|
167
|
-
removed_local_names << local.name
|
|
168
|
-
removed_local_names.concat(descendant_names(local_child))
|
|
169
|
-
end
|
|
167
|
+
append_local_children(child, locals, facts, removed_local_names)
|
|
170
168
|
end
|
|
171
169
|
when AST::ConstDecl
|
|
172
170
|
parent = name_index[decl.name]&.find { |s| symbol_line(s) == (decl.line || 0) }
|
|
@@ -179,25 +177,13 @@ module MilkTea
|
|
|
179
177
|
next unless decl.block_body
|
|
180
178
|
|
|
181
179
|
locals = collect_local_decls(decl.block_body)
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
parent[:children] ||= []
|
|
185
|
-
parent_children = parent[:children]
|
|
186
|
-
locals.each do |local|
|
|
187
|
-
next unless local.name
|
|
188
|
-
|
|
189
|
-
child = local_decl_symbol(local)
|
|
190
|
-
next unless child
|
|
191
|
-
|
|
192
|
-
parent_children << child unless parent_children.any? { |pc| pc[:name] == child[:name] }
|
|
193
|
-
removed_local_names << local.name
|
|
194
|
-
removed_local_names.concat(descendant_names(child))
|
|
195
|
-
end
|
|
180
|
+
append_local_children(parent, locals, facts, removed_local_names)
|
|
196
181
|
else
|
|
197
182
|
parent_name = child_parent_name(decl)
|
|
198
183
|
parent = parent_name ? name_index[parent_name]&.find { |s| symbol_line(s) == (decl.line || 0) } : nil
|
|
199
184
|
next unless parent
|
|
200
185
|
|
|
186
|
+
detail_parts = []
|
|
201
187
|
if decl.is_a?(AST::StructDecl) && decl.implements&.any?
|
|
202
188
|
ifaces = decl.implements.map { |i|
|
|
203
189
|
base = i.respond_to?(:parts) ? i.parts.join('.') : i.name.parts.join('.')
|
|
@@ -210,9 +196,19 @@ module MilkTea
|
|
|
210
196
|
end
|
|
211
197
|
"#{base}#{args}"
|
|
212
198
|
}.join(', ')
|
|
213
|
-
|
|
199
|
+
detail_parts << "(#{ifaces})"
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
if decl.respond_to?(:backing_type) && decl.backing_type && (bt = type_detail_string(decl.backing_type))
|
|
203
|
+
detail_parts << ": #{bt}"
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
if (generic = generic_signature_detail(decl))
|
|
207
|
+
detail_parts << generic
|
|
214
208
|
end
|
|
215
209
|
|
|
210
|
+
parent[:detail] = detail_parts.join(' ') unless detail_parts.empty?
|
|
211
|
+
|
|
216
212
|
type_params = expand_generic_type_params(decl)
|
|
217
213
|
if type_params&.any?
|
|
218
214
|
parent[:children] ||= []
|
|
@@ -374,14 +370,78 @@ module MilkTea
|
|
|
374
370
|
line = (a.line) ? a.line : default_line
|
|
375
371
|
return nil unless a.respond_to?(:name) && a.name && line
|
|
376
372
|
col = a.column ? a.column : 1
|
|
373
|
+
|
|
374
|
+
detail = nil
|
|
375
|
+
if a.respond_to?(:fields) && a.fields&.any?
|
|
376
|
+
fields = a.fields.map { |f| "#{f.name}: #{type_detail_string(f.type)}" }.join(', ')
|
|
377
|
+
detail = "(#{fields})"
|
|
378
|
+
end
|
|
379
|
+
|
|
377
380
|
{
|
|
378
381
|
name: a.name, kind: 22,
|
|
382
|
+
detail: detail,
|
|
379
383
|
range: { start: { line: line - 1, character: 0 }, end: { line: line, character: 0 } },
|
|
380
384
|
selectionRange: {
|
|
381
385
|
start: { line: line - 1, character: col - 1 },
|
|
382
386
|
end: { line: line - 1, character: col - 1 + a.name.length },
|
|
383
387
|
},
|
|
384
|
-
}
|
|
388
|
+
}.compact
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
# Renders the generic parameter clause of a type declaration, e.g.
|
|
392
|
+
# `[A, B]` for `struct Pair[A, B]` or `[@a]` for `struct Buffer[@a]`.
|
|
393
|
+
def generic_signature_detail(decl)
|
|
394
|
+
parts = []
|
|
395
|
+
if decl.respond_to?(:lifetime_params) && decl.lifetime_params&.any?
|
|
396
|
+
parts.concat(decl.lifetime_params.map(&:to_s))
|
|
397
|
+
end
|
|
398
|
+
if decl.respond_to?(:type_params) && decl.type_params&.any?
|
|
399
|
+
parts.concat(decl.type_params.map { |tp| tp.respond_to?(:name) ? tp.name : tp.to_s })
|
|
400
|
+
end
|
|
401
|
+
parts.empty? ? nil : "[#{parts.join(', ')}]"
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
# Module-level `when` branches are compile-time conditionals; the token
|
|
405
|
+
# symbol scan lists their declarations, so flatten the branch bodies so
|
|
406
|
+
# the enrichment below can type them like ordinary top-level decls.
|
|
407
|
+
def flatten_module_declarations(declarations)
|
|
408
|
+
(declarations || []).flat_map do |decl|
|
|
409
|
+
if decl.is_a?(AST::WhenStmt)
|
|
410
|
+
(decl.branches || []).flat_map { |b| flatten_module_declarations(b.body) } +
|
|
411
|
+
flatten_module_declarations(decl.else_body)
|
|
412
|
+
else
|
|
413
|
+
[decl]
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
# Adds local declaration children to an outline symbol. Destructure
|
|
419
|
+
# locals (`let Vec2(x, y) = ...`) introduce a spurious flat variable
|
|
420
|
+
# symbol named after the destructure type; that symbol is collected for
|
|
421
|
+
# removal instead of being shown as a typed child.
|
|
422
|
+
def append_local_children(container, locals, facts, removed_local_names)
|
|
423
|
+
return unless locals&.any?
|
|
424
|
+
|
|
425
|
+
container[:children] ||= []
|
|
426
|
+
children = container[:children]
|
|
427
|
+
locals.each do |local|
|
|
428
|
+
if local.respond_to?(:destructure_bindings) && local.destructure_bindings
|
|
429
|
+
type_name = local.destructure_type_name
|
|
430
|
+
if type_name
|
|
431
|
+
name = type_name.is_a?(Array) ? type_name.join('.') : type_name.to_s
|
|
432
|
+
removed_local_names << name unless removed_local_names.include?(name)
|
|
433
|
+
end
|
|
434
|
+
next
|
|
435
|
+
end
|
|
436
|
+
next unless local.name
|
|
437
|
+
|
|
438
|
+
child = local_decl_symbol(local, facts:)
|
|
439
|
+
next unless child
|
|
440
|
+
|
|
441
|
+
children << child unless children.any? { |pc| pc[:name] == child[:name] }
|
|
442
|
+
removed_local_names << local.name
|
|
443
|
+
removed_local_names.concat(descendant_names(child))
|
|
444
|
+
end
|
|
385
445
|
end
|
|
386
446
|
|
|
387
447
|
def collect_local_decls(body)
|
|
@@ -417,7 +477,7 @@ module MilkTea
|
|
|
417
477
|
end
|
|
418
478
|
end
|
|
419
479
|
|
|
420
|
-
def local_decl_symbol(decl)
|
|
480
|
+
def local_decl_symbol(decl, facts: nil)
|
|
421
481
|
return nil unless decl.name
|
|
422
482
|
return nil if decl.name == '_'
|
|
423
483
|
|
|
@@ -429,8 +489,9 @@ module MilkTea
|
|
|
429
489
|
if decl.respond_to?(:value) && decl.value.is_a?(AST::ProcExpr)
|
|
430
490
|
detail ||= proc_signature_detail(decl.value)
|
|
431
491
|
proc_locals = collect_local_decls(decl.value.body)
|
|
432
|
-
children = proc_locals.filter_map { |l| local_decl_symbol(l) }
|
|
492
|
+
children = proc_locals.filter_map { |l| local_decl_symbol(l, facts:) }
|
|
433
493
|
end
|
|
494
|
+
detail ||= resolved_local_type_detail(decl, facts)
|
|
434
495
|
|
|
435
496
|
{
|
|
436
497
|
name: decl.name, kind: 13,
|
|
@@ -441,6 +502,60 @@ module MilkTea
|
|
|
441
502
|
}.compact
|
|
442
503
|
end
|
|
443
504
|
|
|
505
|
+
# Resolves the declared type of an inferred local from semantic facts.
|
|
506
|
+
# Prefers the sema binding type (which reflects let/var ... else: and
|
|
507
|
+
# other flow refinement), falling back to the initializer expression
|
|
508
|
+
# type recorded during checking. Returns nil when facts are unavailable
|
|
509
|
+
# or the binding is missing.
|
|
510
|
+
def resolved_local_type_detail(decl, facts)
|
|
511
|
+
return nil unless facts
|
|
512
|
+
return nil unless facts.respond_to?(:binding_resolution) && facts.binding_resolution
|
|
513
|
+
|
|
514
|
+
binding_id = facts.binding_resolution.declaration_binding_ids[decl.object_id]
|
|
515
|
+
if binding_id
|
|
516
|
+
type = facts.binding_resolution.binding_types[binding_id]
|
|
517
|
+
return nil if type.is_a?(Types::Error)
|
|
518
|
+
return short_type_detail(type) if type
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
return nil unless decl.respond_to?(:value) && decl.value
|
|
522
|
+
return nil unless facts.respond_to?(:resolved_expr_types)
|
|
523
|
+
|
|
524
|
+
node_id = facts.respond_to?(:ast) && facts.ast ? facts.ast.node_ids[decl.value.object_id] : nil
|
|
525
|
+
return nil unless node_id
|
|
526
|
+
|
|
527
|
+
type = facts.resolved_expr_types[node_id]
|
|
528
|
+
return nil if type.is_a?(Types::Error)
|
|
529
|
+
|
|
530
|
+
short_type_detail(type)
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
# Renders a resolved semantic type for outline display without module
|
|
534
|
+
# qualifiers (e.g. std.deque.Deque[int] as Deque[int]). Falls back to
|
|
535
|
+
# the canonical #to_s when a type has no usable short form.
|
|
536
|
+
def short_type_detail(type)
|
|
537
|
+
return nil unless type
|
|
538
|
+
|
|
539
|
+
case type
|
|
540
|
+
when Types::Nullable
|
|
541
|
+
"#{short_type_detail(type.base)}?"
|
|
542
|
+
when Types::Span
|
|
543
|
+
"span[#{short_type_detail(type.element_type)}]"
|
|
544
|
+
when Types::SoA
|
|
545
|
+
"SoA[#{short_type_detail(type.element_type)}, #{type.count}]"
|
|
546
|
+
when Types::StructInstance, Types::VariantInstance, Types::GenericInstance
|
|
547
|
+
args = type.arguments.map { |arg| short_type_arg(arg) }.join(', ')
|
|
548
|
+
args.empty? ? type.name : "#{type.name}[#{args}]"
|
|
549
|
+
else
|
|
550
|
+
name = type.respond_to?(:name) ? type.name.to_s : ''
|
|
551
|
+
name.empty? ? type.to_s : name
|
|
552
|
+
end
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
def short_type_arg(arg)
|
|
556
|
+
arg.is_a?(Types::LiteralTypeArg) ? arg.value.to_s : short_type_detail(arg)
|
|
557
|
+
end
|
|
558
|
+
|
|
444
559
|
def descendant_names(symbol)
|
|
445
560
|
return [] unless symbol[:children]
|
|
446
561
|
|
|
@@ -487,9 +602,7 @@ module MilkTea
|
|
|
487
602
|
detail_parts << 'mut' if m.respond_to?(:kind) && m.kind == :editable_function
|
|
488
603
|
detail_parts << 'static' if m.respond_to?(:kind) && m.kind == :static_function
|
|
489
604
|
detail_parts << 'async' if m.respond_to?(:async) && m.async
|
|
490
|
-
|
|
491
|
-
detail_parts << "-> #{ret}"
|
|
492
|
-
end
|
|
605
|
+
detail_parts << "-> #{type_detail_string(m.return_type) || 'void'}"
|
|
493
606
|
{
|
|
494
607
|
name: m.name, kind: 6,
|
|
495
608
|
range: { start: { line: m.line - 1, character: 0 }, end: { line: (m.respond_to?(:end_line) && m.end_line ? m.end_line : m.line), character: 0 } },
|
|
@@ -507,10 +620,11 @@ module MilkTea
|
|
|
507
620
|
case type
|
|
508
621
|
when AST::TypeRef
|
|
509
622
|
type.to_s
|
|
510
|
-
when AST::ProcType
|
|
623
|
+
when AST::FunctionType, AST::ProcType
|
|
511
624
|
params = (type.params || []).map { |p| type_detail_string(p.type) }.join(', ')
|
|
512
625
|
ret = type_detail_string(type.return_type) || 'void'
|
|
513
|
-
|
|
626
|
+
keyword = type.is_a?(AST::FunctionType) ? 'fn' : 'proc'
|
|
627
|
+
"#{keyword}(#{params}) -> #{ret}"
|
|
514
628
|
when AST::TupleType
|
|
515
629
|
base = "(#{(type.element_types || []).map { |t| type_detail_string(t) }.join(', ')})"
|
|
516
630
|
type.nullable ? "#{base}?" : base
|