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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c3e9c0ffde0323f887700bd299b2007572ab902f5de1395d01b536c5df275be
4
- data.tar.gz: ffc7e3e56abf8661cce5b6d638590fe8fe0a2828c989e74f7781de27492d6f4f
3
+ metadata.gz: a2b1f14318677bf9232d4163ad6aac3905650056716de05bf89762f08e1aa7d5
4
+ data.tar.gz: af90e63fd1c9fd21f6af721c1c039b61159d931d20eaa3eaca3b41369da053c1
5
5
  SHA512:
6
- metadata.gz: 178c78214fd02f7da6b38c6edbce4e1dec6b2c9469ed68a7ac83abd80899e3a016aba741868f7e5d88c21d01c974305739b0df5a14eee55a297d33b31d8bf3c4
7
- data.tar.gz: 6f0c2c1b54dc604322b51888635ee6a1049656f7a1da3572ccb58581cd107602f3791c6d4135fd24fec425e8ad81e2db9ed77e83e896545b23b1b8d80b7ea956
6
+ metadata.gz: cef8b0d7f55c4d9496f41d02b0ace0df87f233ff8c24892768543ff3518e68271a2e4613a7654cef661506d42835d1ea8baa9b3d84225614aae420c0d105a0a4
7
+ data.tar.gz: b55024abb91b7fdf2694b4be82bf972f1410aa01b949952d30fb49be517fddd501467ebc55fcf3a6dc1a1eb01334709aa74c26a231348026e3197cc5b163b74c
data/lib/milk_tea/base.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require "pathname"
4
4
 
5
5
  module MilkTea
6
- VERSION = "0.2.15"
6
+ VERSION = "0.2.17"
7
7
 
8
8
  def self.root
9
9
  @root ||= Pathname.new(File.expand_path("../..", __dir__))
@@ -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
- dispatch_base_type = method_dispatch_receiver_type_for_completion(receiver_type.base)
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 && node.end_line && lsp_line + 1 >= node.line && lsp_line + 1 <= node.end_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.15
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.15 installed!
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