mt-lang 0.2.15 → 0.2.17
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 -1
- data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +37 -0
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +156 -0
- data/lib/milk_tea/lsp/server/completion.rb +14 -4
- data/lib/milk_tea/lsp/server/signature_help.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a2b1f14318677bf9232d4163ad6aac3905650056716de05bf89762f08e1aa7d5
|
|
4
|
+
data.tar.gz: af90e63fd1c9fd21f6af721c1c039b61159d931d20eaa3eaca3b41369da053c1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cef8b0d7f55c4d9496f41d02b0ace0df87f233ff8c24892768543ff3518e68271a2e4613a7654cef661506d42835d1ea8baa9b3d84225614aae420c0d105a0a4
|
|
7
|
+
data.tar.gz: b55024abb91b7fdf2694b4be82bf972f1410aa01b949952d30fb49be517fddd501467ebc55fcf3a6dc1a1eb01334709aa74c26a231348026e3197cc5b163b74c
|
data/lib/milk_tea/base.rb
CHANGED
|
@@ -122,6 +122,15 @@ module MilkTea
|
|
|
122
122
|
start_line = [binding.ast.respond_to?(:line) ? binding.ast.line : nil, snapshots.first.line].compact.min
|
|
123
123
|
end_line = snapshots.last.line
|
|
124
124
|
|
|
125
|
+
# Generic function bodies are not analysed during structural checking,
|
|
126
|
+
# so the frame only has the initial snapshot at the declaration line.
|
|
127
|
+
# Extend end_line to cover the actual body so that completion and hover
|
|
128
|
+
# lookups for parameters resolve correctly inside the body.
|
|
129
|
+
if snapshots.length == 1 && binding.ast.respond_to?(:body) && binding.ast.body
|
|
130
|
+
body_end = last_ast_line(binding.ast.body)
|
|
131
|
+
end_line = body_end if body_end && body_end > end_line
|
|
132
|
+
end
|
|
133
|
+
|
|
125
134
|
@local_completion_frames << LocalCompletionFrame.new(
|
|
126
135
|
start_line:,
|
|
127
136
|
end_line:,
|
|
@@ -350,6 +359,34 @@ module MilkTea
|
|
|
350
359
|
end
|
|
351
360
|
end
|
|
352
361
|
|
|
362
|
+
# Returns the highest line number across a list of AST statement nodes,
|
|
363
|
+
# recursing into nested bodies (if/else/match/while/for/defer/unsafe).
|
|
364
|
+
def last_ast_line(statements)
|
|
365
|
+
return nil if statements.nil? || statements.empty?
|
|
366
|
+
|
|
367
|
+
statements.filter_map do |stmt|
|
|
368
|
+
case stmt
|
|
369
|
+
when AST::IfStmt
|
|
370
|
+
branch_lines = stmt.branches.filter_map { |b| last_ast_line(b.body) }
|
|
371
|
+
else_lines = last_ast_line(stmt.else_body)
|
|
372
|
+
[stmt.line, *branch_lines, else_lines].compact.max
|
|
373
|
+
when AST::WhileStmt, AST::ForStmt, AST::UnsafeStmt, AST::ErrorBlockStmt
|
|
374
|
+
[stmt.respond_to?(:line) ? stmt.line : nil, last_ast_line(stmt.body)].compact.max
|
|
375
|
+
when AST::MatchStmt
|
|
376
|
+
arm_lines = stmt.arms.filter_map { |arm| last_ast_line(arm.body) }
|
|
377
|
+
[stmt.line, *arm_lines].compact.max
|
|
378
|
+
when AST::DeferStmt
|
|
379
|
+
[stmt.respond_to?(:line) ? stmt.line : nil, last_ast_line(stmt.body)].compact.max
|
|
380
|
+
when AST::WhenStmt
|
|
381
|
+
branch_lines = stmt.branches.filter_map { |b| last_ast_line(b.body) }
|
|
382
|
+
else_lines = last_ast_line(stmt.else_body)
|
|
383
|
+
[stmt.line, *branch_lines, else_lines].compact.max
|
|
384
|
+
else
|
|
385
|
+
stmt.respond_to?(:line) ? stmt.line : nil
|
|
386
|
+
end
|
|
387
|
+
end.compact.max
|
|
388
|
+
end
|
|
389
|
+
|
|
353
390
|
end
|
|
354
391
|
end
|
|
355
392
|
end
|
|
@@ -302,6 +302,161 @@ module MilkTea
|
|
|
302
302
|
end
|
|
303
303
|
end
|
|
304
304
|
|
|
305
|
+
# Names recognised as builtin callable / type-constructor identifiers by
|
|
306
|
+
# resolve_callable and the compile-time evaluation path. We allow these
|
|
307
|
+
# so the generic-name check does not flag them as unknown before the full
|
|
308
|
+
# type checker has a chance to handle the surrounding Specialization /
|
|
309
|
+
# Call context.
|
|
310
|
+
BUILTIN_CALLABLE_NAMES = %w[
|
|
311
|
+
fatal ref_of const_ptr_of read ptr_of
|
|
312
|
+
field_of fields_of callable_of attribute_of has_attribute
|
|
313
|
+
members_of attributes_of get
|
|
314
|
+
reinterpret array span zero default hash equal order
|
|
315
|
+
adapt attribute_arg
|
|
316
|
+
Task Option Result SoA str_buffer atomic
|
|
317
|
+
].to_set.freeze
|
|
318
|
+
|
|
319
|
+
# Verifies that every bare identifier used as a value expression inside
|
|
320
|
+
# a generic method body resolves to a known name (parameter, local,
|
|
321
|
+
# top-level value, function, type, import, or type parameter). This
|
|
322
|
+
# catches misspelled variables and undeclared names without touching any
|
|
323
|
+
# type-dependent logic, so it is safe to run during structural analysis
|
|
324
|
+
# before concrete types are substituted.
|
|
325
|
+
def check_generic_method_names(binding, scopes)
|
|
326
|
+
names = Set.new
|
|
327
|
+
binding.ast.body&.each do |statement|
|
|
328
|
+
check_stmt_names(statement, scopes, binding, names)
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def check_stmt_names(stmt, scopes, binding, names)
|
|
333
|
+
case stmt
|
|
334
|
+
when AST::LocalDecl
|
|
335
|
+
check_expr_names(stmt.value, scopes, binding, names) if stmt.value
|
|
336
|
+
names.add(stmt.name) if stmt.name
|
|
337
|
+
when AST::Assignment
|
|
338
|
+
check_expr_names(stmt.target, scopes, binding, names)
|
|
339
|
+
check_expr_names(stmt.value, scopes, binding, names)
|
|
340
|
+
when AST::ExpressionStmt
|
|
341
|
+
check_expr_names(stmt.expression, scopes, binding, names)
|
|
342
|
+
when AST::ReturnStmt
|
|
343
|
+
check_expr_names(stmt.value, scopes, binding, names) if stmt.value
|
|
344
|
+
when AST::IfStmt
|
|
345
|
+
stmt.branches.each do |b|
|
|
346
|
+
check_expr_names(b.condition, scopes, binding, names)
|
|
347
|
+
branch_names = names.dup
|
|
348
|
+
b.body&.each { |s| check_stmt_names(s, scopes, binding, branch_names) }
|
|
349
|
+
end
|
|
350
|
+
else_names = names.dup
|
|
351
|
+
stmt.else_body&.each { |s| check_stmt_names(s, scopes, binding, else_names) }
|
|
352
|
+
when AST::WhileStmt
|
|
353
|
+
check_expr_names(stmt.condition, scopes, binding, names)
|
|
354
|
+
body_names = names.dup
|
|
355
|
+
stmt.body&.each { |s| check_stmt_names(s, scopes, binding, body_names) }
|
|
356
|
+
when AST::ForStmt
|
|
357
|
+
Array(stmt.iterables).each { |i| check_expr_names(i, scopes, binding, names) }
|
|
358
|
+
body_names = names.dup
|
|
359
|
+
Array(stmt.bindings).each { |b| body_names.add(b.respond_to?(:name) ? b.name : b) }
|
|
360
|
+
stmt.body&.each { |s| check_stmt_names(s, scopes, binding, body_names) }
|
|
361
|
+
when AST::MatchStmt
|
|
362
|
+
check_expr_names(stmt.expression, scopes, binding, names)
|
|
363
|
+
stmt.arms.each do |arm|
|
|
364
|
+
arm_names = names.dup
|
|
365
|
+
arm_names.add(arm.binding_name) if arm.binding_name
|
|
366
|
+
arm.body&.each { |s| check_stmt_names(s, scopes, binding, arm_names) }
|
|
367
|
+
end
|
|
368
|
+
when AST::UnsafeStmt, AST::ParallelBlockStmt
|
|
369
|
+
body_names = names.dup
|
|
370
|
+
stmt.body&.each { |s| check_stmt_names(s, scopes, binding, body_names) }
|
|
371
|
+
when AST::DeferStmt
|
|
372
|
+
check_expr_names(stmt.expression, scopes, binding, names) if stmt.expression
|
|
373
|
+
body_names = names.dup
|
|
374
|
+
stmt.body&.each { |s| check_stmt_names(s, scopes, binding, body_names) }
|
|
375
|
+
when AST::WhenStmt
|
|
376
|
+
check_expr_names(stmt.discriminant, scopes, binding, names)
|
|
377
|
+
stmt.branches.each do |b|
|
|
378
|
+
branch_names = names.dup
|
|
379
|
+
b.body&.each { |s| check_stmt_names(s, scopes, binding, branch_names) }
|
|
380
|
+
end
|
|
381
|
+
else_names = names.dup
|
|
382
|
+
stmt.else_body&.each { |s| check_stmt_names(s, scopes, binding, else_names) }
|
|
383
|
+
when AST::ErrorBlockStmt
|
|
384
|
+
body_names = names.dup
|
|
385
|
+
stmt.body&.each { |s| check_stmt_names(s, scopes, binding, body_names) }
|
|
386
|
+
when AST::BreakStmt, AST::ContinueStmt, AST::PassStmt, AST::StaticAssert, AST::EmitStmt
|
|
387
|
+
nil
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def check_expr_names(expr, scopes, binding, names)
|
|
392
|
+
case expr
|
|
393
|
+
when AST::Identifier
|
|
394
|
+
check_generic_name(expr.name, scopes, binding, expr, names)
|
|
395
|
+
when AST::MemberAccess
|
|
396
|
+
check_expr_names(expr.receiver, scopes, binding, names)
|
|
397
|
+
when AST::Call
|
|
398
|
+
check_expr_names(expr.callee, scopes, binding, names)
|
|
399
|
+
expr.arguments.each { |arg| check_expr_names(arg.value, scopes, binding, names) }
|
|
400
|
+
when AST::BinaryOp
|
|
401
|
+
check_expr_names(expr.left, scopes, binding, names)
|
|
402
|
+
check_expr_names(expr.right, scopes, binding, names)
|
|
403
|
+
when AST::UnaryOp
|
|
404
|
+
check_expr_names(expr.operand, scopes, binding, names)
|
|
405
|
+
when AST::IfExpr
|
|
406
|
+
check_expr_names(expr.condition, scopes, binding, names)
|
|
407
|
+
check_expr_names(expr.then_expression, scopes, binding, names)
|
|
408
|
+
check_expr_names(expr.else_expression, scopes, binding, names)
|
|
409
|
+
when AST::MatchExpr
|
|
410
|
+
check_expr_names(expr.expression, scopes, binding, names)
|
|
411
|
+
expr.arms.each do |arm|
|
|
412
|
+
arm_names = names.dup
|
|
413
|
+
arm_names.add(arm.binding_name) if arm.binding_name
|
|
414
|
+
check_expr_names(arm.value, scopes, binding, arm_names)
|
|
415
|
+
end
|
|
416
|
+
when AST::IndexAccess
|
|
417
|
+
check_expr_names(expr.receiver, scopes, binding, names)
|
|
418
|
+
check_expr_names(expr.index, scopes, binding, names)
|
|
419
|
+
when AST::Specialization
|
|
420
|
+
check_expr_names(expr.callee, scopes, binding, names)
|
|
421
|
+
when AST::PrefixCast
|
|
422
|
+
check_expr_names(expr.expression, scopes, binding, names)
|
|
423
|
+
when AST::AwaitExpr
|
|
424
|
+
check_expr_names(expr.expression, scopes, binding, names)
|
|
425
|
+
when AST::UnsafeExpr
|
|
426
|
+
check_expr_names(expr.expression, scopes, binding, names)
|
|
427
|
+
when AST::RangeExpr
|
|
428
|
+
check_expr_names(expr.start_expr, scopes, binding, names) if expr.start_expr
|
|
429
|
+
check_expr_names(expr.end_expr, scopes, binding, names) if expr.end_expr
|
|
430
|
+
when AST::ExpressionList
|
|
431
|
+
expr.elements.each { |elem| check_expr_names(elem, scopes, binding, names) }
|
|
432
|
+
when AST::ProcExpr
|
|
433
|
+
body_names = names.dup
|
|
434
|
+
Array(expr.params).each { |p| body_names.add(p.name) if p.respond_to?(:name) }
|
|
435
|
+
expr.body&.each { |s| check_stmt_names(s, scopes, binding, body_names) }
|
|
436
|
+
when AST::DetachExpr
|
|
437
|
+
check_expr_names(expr.body, scopes, binding, names)
|
|
438
|
+
when AST::FormatExprPart
|
|
439
|
+
check_expr_names(expr.expression, scopes, binding, names)
|
|
440
|
+
when AST::IntegerLiteral, AST::FloatLiteral, AST::StringLiteral,
|
|
441
|
+
AST::BooleanLiteral, AST::NullLiteral, AST::CharLiteral,
|
|
442
|
+
AST::FormatString, AST::FormatTextPart
|
|
443
|
+
nil
|
|
444
|
+
end
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
def check_generic_name(name, scopes, binding, node, names)
|
|
448
|
+
return if name == "_" || name == "this"
|
|
449
|
+
return if names.include?(name)
|
|
450
|
+
return if BUILTIN_CALLABLE_NAMES.include?(name)
|
|
451
|
+
return if lookup_value(name, scopes)
|
|
452
|
+
return if @ctx.top_level_functions.key?(name)
|
|
453
|
+
return if @ctx.types.key?(name)
|
|
454
|
+
return if @ctx.imports.key?(name)
|
|
455
|
+
return if binding.type_params.include?(name)
|
|
456
|
+
|
|
457
|
+
raise_sema_error("unknown name #{name}", node)
|
|
458
|
+
end
|
|
459
|
+
|
|
305
460
|
# Scans generic method bodies for assignments to this through a
|
|
306
461
|
# non-editable receiver. Full body checking is deferred to call-site
|
|
307
462
|
# specialization, but immutable-this violations are type-independent.
|
|
@@ -337,6 +492,7 @@ module MilkTea
|
|
|
337
492
|
start_local_completion_frame(binding, scopes)
|
|
338
493
|
if binding.type_params.any?
|
|
339
494
|
check_generic_method_immutable_this(binding, scopes)
|
|
495
|
+
check_generic_method_names(binding, scopes)
|
|
340
496
|
return
|
|
341
497
|
end
|
|
342
498
|
|
|
@@ -725,6 +725,18 @@ module MilkTea
|
|
|
725
725
|
dispatch_receiver_type = method_dispatch_receiver_type_for_completion(receiver_type)
|
|
726
726
|
receiver_candidates << dispatch_receiver_type if dispatch_receiver_type != receiver_type
|
|
727
727
|
|
|
728
|
+
# For GenericInstance or Nullable-wrapped GenericInstance, also try the
|
|
729
|
+
# struct definition from facts.types — method registration keys are
|
|
730
|
+
# always the GenericStructDefinition, not a GenericInstance.
|
|
731
|
+
[dispatch_receiver_type, receiver_type].each do |candidate|
|
|
732
|
+
base = candidate
|
|
733
|
+
base = base.base while base.is_a?(Types::Nullable)
|
|
734
|
+
next unless base.is_a?(Types::GenericInstance)
|
|
735
|
+
|
|
736
|
+
definition = facts.types[base.name]
|
|
737
|
+
receiver_candidates << definition if definition && !receiver_candidates.include?(definition)
|
|
738
|
+
end
|
|
739
|
+
|
|
728
740
|
receiver_candidates.each do |candidate|
|
|
729
741
|
facts.methods.fetch(candidate, {}).each do |name, binding|
|
|
730
742
|
methods[name] ||= binding
|
|
@@ -745,10 +757,7 @@ module MilkTea
|
|
|
745
757
|
return receiver_type.definition if receiver_type.is_a?(Types::StructInstance) || receiver_type.is_a?(Types::VariantInstance)
|
|
746
758
|
|
|
747
759
|
if receiver_type.is_a?(Types::Nullable)
|
|
748
|
-
|
|
749
|
-
return receiver_type if dispatch_base_type == receiver_type.base
|
|
750
|
-
|
|
751
|
-
return Types::Registry.nullable(dispatch_base_type)
|
|
760
|
+
return method_dispatch_receiver_type_for_completion(receiver_type.base)
|
|
752
761
|
end
|
|
753
762
|
|
|
754
763
|
return receiver_type unless receiver_type.is_a?(Types::GenericInstance)
|
|
@@ -764,6 +773,7 @@ module MilkTea
|
|
|
764
773
|
end
|
|
765
774
|
|
|
766
775
|
def project_field_receiver_type_for_completion(type, facts = nil)
|
|
776
|
+
type = type.base while type.is_a?(Types::Nullable)
|
|
767
777
|
type = project_receiver_type_for_completion(type)
|
|
768
778
|
return type.definition if type.is_a?(Types::StructInstance) || type.is_a?(Types::VariantInstance)
|
|
769
779
|
return field_type_from_struct_definition(type, facts) if facts
|
|
@@ -100,7 +100,7 @@ module MilkTea
|
|
|
100
100
|
extending = nil
|
|
101
101
|
each_ast_node(ast) do |node|
|
|
102
102
|
next unless node.is_a?(AST::ExtendingBlock)
|
|
103
|
-
if node.line &&
|
|
103
|
+
if node.line && lsp_line + 1 >= node.line && (!node.respond_to?(:end_line) || !node.end_line || lsp_line + 1 <= node.end_line)
|
|
104
104
|
extending = node
|
|
105
105
|
end
|
|
106
106
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mt-lang
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.17
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Long (Teefan) Tran
|
|
@@ -583,7 +583,7 @@ metadata:
|
|
|
583
583
|
homepage_uri: https://teefan.github.io/mt-lang/
|
|
584
584
|
source_code_uri: https://github.com/teefan/mt-lang
|
|
585
585
|
post_install_message: |
|
|
586
|
-
Milk Tea 0.2.
|
|
586
|
+
Milk Tea 0.2.17 installed!
|
|
587
587
|
|
|
588
588
|
System requirements:
|
|
589
589
|
- A C compiler (gcc or clang) must be available on PATH
|