mt-lang 0.3.39 → 0.3.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/lib/milk_tea/base.rb +1 -1
  3. data/lib/milk_tea/core/ast.rb +2 -2
  4. data/lib/milk_tea/core/bindings/attribute_binding.rb +1 -6
  5. data/lib/milk_tea/core/bindings/module_binding.rb +1 -1
  6. data/lib/milk_tea/core/intrinsics.rb +23 -1
  7. data/lib/milk_tea/core/lowering/functions.rb +3 -0
  8. data/lib/milk_tea/core/lowering/resolve.rb +4 -65
  9. data/lib/milk_tea/core/lowering/utils.rb +0 -17
  10. data/lib/milk_tea/core/lowering.rb +1 -2
  11. data/lib/milk_tea/core/module_binder.rb +4 -15
  12. data/lib/milk_tea/core/module_loader.rb +41 -8
  13. data/lib/milk_tea/core/parser/declarations.rb +24 -17
  14. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +2 -111
  15. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1 -2
  16. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +26 -17
  17. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +26 -42
  18. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +0 -59
  19. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +0 -2
  20. data/lib/milk_tea/core/types/predicates.rb +57 -0
  21. data/lib/milk_tea/core/types/registry.rb +14 -2
  22. data/lib/milk_tea/core/types.rb +0 -4
  23. data/lib/milk_tea/lsp/server/code_actions.rb +13 -7
  24. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +56 -12
  25. data/lib/milk_tea/lsp/server/text_documents.rb +18 -2
  26. data/lib/milk_tea/lsp/workspace/caches.rb +28 -1
  27. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +31 -0
  28. data/lib/milk_tea/lsp/workspace/store.rb +7 -1
  29. data/lib/milk_tea/lsp/workspace.rb +6 -0
  30. data/lib/milk_tea/tooling/formatter.rb +2 -3
  31. data/lib/milk_tea/tooling/linter/fix_engine.rb +46 -0
  32. data/lib/milk_tea/tooling/linter/rules.rb +32 -0
  33. data/lib/milk_tea/tooling/linter.rb +4 -0
  34. data/lib/milk_tea/tooling.rb +0 -1
  35. metadata +2 -3
  36. data/lib/milk_tea/tooling/cst_formatter.rb +0 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5d9af5865fb54b82fbf9169cdf8f8feef652af6a2b84cbd8e63e35f99b33ac08
4
- data.tar.gz: 2c5d57827cc19e8de3ee5fff7e576860aa4ae30fa5f7b588eba80c9e95227e02
3
+ metadata.gz: 91b5015b218da9925b56895b551063fca825ca776fc336fe898ef17e332aae4e
4
+ data.tar.gz: 46e4a2da80edfb7c64fc847ee10844c9f5d36572df35105ab6ed51e2455d347a
5
5
  SHA512:
6
- metadata.gz: 9fc438f51cba2f5a65bd3a8ea3441fb87f26018be991146e060a828b036ba9d5eebde4f5546ce344f276ba68e5f1a99b4fe92b026c839c3f77740b15c108a4eb
7
- data.tar.gz: 232a3879248bbd638c7710135047c09dbe4a140758da63c2fc5434df4602750c6459faa3fa843abca99801c6628b963ec747509be17de4c73f408626882dfa22
6
+ metadata.gz: 0c1dcedaf98ef374c9dd21cded75f8512c755a63e4a3a155e494d9edf098b2d53da3b258edab5d63066a2a9eaa3c2a3883aa69241608509085caf398c1edecc1
7
+ data.tar.gz: a427c7ad0f2475b7dc5cccadcb6cc143ab195f906b7b9b13407a7e754043fd8550495c7dc750f5f27c92e2eca13477d9f6697650bddd515403526812b1dac06b
data/lib/milk_tea/base.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require "pathname"
4
4
 
5
5
  module MilkTea
6
- VERSION = "0.3.39"
6
+ VERSION = "0.3.41"
7
7
 
8
8
  def self.root
9
9
  @root ||= Pathname.new(File.expand_path("../..", __dir__))
@@ -111,8 +111,8 @@ module MilkTea
111
111
  InterfaceDecl = Data.define(:name, :type_params, :methods, :visibility, :line, :column) do
112
112
  def initialize(name:, type_params: [], methods:, visibility:, line: nil, column: nil) = super
113
113
  end
114
- ExtendingBlock = Data.define(:type_name, :methods, :line, :column) do
115
- def initialize(type_name:, methods:, line: nil, column: nil) = super
114
+ ExtendingBlock = Data.define(:type_name, :methods, :line, :column, :inline) do
115
+ def initialize(type_name:, methods:, line: nil, column: nil, inline: false) = super
116
116
  end
117
117
  InterfaceMethodDecl = Data.define(:name, :params, :return_type, :kind, :async, :attributes, :line, :column) do
118
118
  def initialize(name:, params:, return_type:, kind:, async:, attributes: [], line: nil, column: nil) = super
@@ -2,7 +2,7 @@
2
2
 
3
3
  module MilkTea
4
4
  module Bindings
5
- AttributeBinding = Data.define(:name, :targets, :params, :module_name, :builtin, :ast)
5
+ AttributeBinding = Data.define(:name, :targets, :params, :module_name, :builtin)
6
6
  BUILTIN_ATTRIBUTE_NAMES = %w[packed align deprecated test expect_fatal].freeze
7
7
 
8
8
  def self.builtin_attribute_binding(name, types)
@@ -14,7 +14,6 @@ module MilkTea
14
14
  params: [].freeze,
15
15
  module_name: nil,
16
16
  builtin: true,
17
- ast: nil,
18
17
  )
19
18
  when "align"
20
19
  AttributeBinding.new(
@@ -23,7 +22,6 @@ module MilkTea
23
22
  params: [Types::Registry.parameter("bytes", types.fetch("ptr_uint"))].freeze,
24
23
  module_name: nil,
25
24
  builtin: true,
26
- ast: nil,
27
25
  )
28
26
  when "deprecated"
29
27
  AttributeBinding.new(
@@ -32,7 +30,6 @@ module MilkTea
32
30
  params: [Types::Registry.parameter("message", types.fetch("str"))].freeze,
33
31
  module_name: nil,
34
32
  builtin: true,
35
- ast: nil,
36
33
  )
37
34
  when "test"
38
35
  AttributeBinding.new(
@@ -41,7 +38,6 @@ module MilkTea
41
38
  params: [].freeze,
42
39
  module_name: nil,
43
40
  builtin: true,
44
- ast: nil,
45
41
  )
46
42
  when "expect_fatal"
47
43
  AttributeBinding.new(
@@ -50,7 +46,6 @@ module MilkTea
50
46
  params: [].freeze,
51
47
  module_name: nil,
52
48
  builtin: true,
53
- ast: nil,
54
49
  )
55
50
  end
56
51
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module MilkTea
4
4
  module Bindings
5
- ModuleBinding = Data.define(:name, :types, :type_declarations, :interfaces, :attributes, :attribute_applications, :values, :functions, :methods, :implemented_interfaces, :imports, :private_types, :private_interfaces, :private_attributes, :private_values, :private_functions, :private_methods, :private_implemented_interfaces) do
5
+ ModuleBinding = Data.define(:name, :types, :type_declarations, :interfaces, :attributes, :attribute_applications, :values, :functions, :methods, :implemented_interfaces, :imports, :private_types, :private_interfaces, :private_attributes, :private_values, :private_functions, :private_methods) do
6
6
  def private_type?(name)
7
7
  private_types.key?(name)
8
8
  end
@@ -84,5 +84,27 @@ module MilkTea
84
84
  def string_literal_cstr_compatibility?(expression, expected_type)
85
85
  expression.is_a?(AST::StringLiteral) && !expression.cstring && expected_type == BUILTIN_CSTR
86
86
  end
87
+
88
+ def current_nested_types
89
+ @current_nested_types
90
+ end
91
+
92
+ # Resolves the receiver type's short name (self-reference) and its own
93
+ # nested types as a bare-name scope for method bodies and signatures.
94
+ def method_receiver_nested_scope(receiver_type)
95
+ return nil unless receiver_type.respond_to?(:name)
96
+
97
+ nested = receiver_type.respond_to?(:nested_types) ? receiver_type.nested_types : nil
98
+ scope = { receiver_type.name => receiver_type }
99
+ scope = scope.merge(nested) if nested && !nested.empty?
100
+ scope
101
+ end
102
+
103
+ # Looks up a bare type name, consulting the active method-receiver nested
104
+ # scope before the module type namespace so nested types self-reference
105
+ # by their short name inside their own methods.
106
+ def lookup_named_type(name)
107
+ (@current_nested_types && @current_nested_types[name]) || @ctx.types[name]
108
+ end
87
109
  end
88
- end
110
+ end
@@ -118,8 +118,10 @@ module MilkTea
118
118
  parameter_setup = []
119
119
  previous_type_substitutions = @ctx.current_type_substitutions
120
120
  previous_value_type_params = @ctx.current_value_type_params
121
+ previous_nested_types = @current_nested_types
121
122
  @ctx.current_type_substitutions = binding.type_substitutions
122
123
  @ctx.current_value_type_params = resolve_value_type_params(decl.type_params)
124
+ @current_nested_types = method_receiver_nested_scope(binding.declared_receiver_type)
123
125
 
124
126
  return lower_async_function_decl(binding, receiver_type:) if binding.async
125
127
 
@@ -179,6 +181,7 @@ module MilkTea
179
181
  ensure
180
182
  @ctx.current_type_substitutions = previous_type_substitutions
181
183
  @ctx.current_value_type_params = previous_value_type_params
184
+ @current_nested_types = previous_nested_types
182
185
  end
183
186
 
184
187
  def resolve_value_type_params(type_params)
@@ -444,7 +444,7 @@ module MilkTea
444
444
  return [:compile_time_builtin, callee.name, nil, compile_time_builtin_function_type(callee.name, arguments, env)]
445
445
  end
446
446
 
447
- type = @ctx.types[callee.name]
447
+ type = lookup_named_type(callee.name)
448
448
  if type.is_a?(Types::Struct) || type.is_a?(Types::StringView) || task_type?(type) || type.is_a?(Types::Vector) || type.is_a?(Types::Matrix) || type.is_a?(Types::Quaternion)
449
449
  return [:struct_literal, nil, nil, type]
450
450
  end
@@ -606,7 +606,6 @@ module MilkTea
606
606
  return [:array_as_span, nil, callee.receiver, Types::Registry.span(array_element_type(field_receiver_type))]
607
607
  end
608
608
 
609
- member_type = field_receiver_type.respond_to?(:field) ? field_receiver_type.field(callee.member) : nil
610
609
  member_type = field_receiver_type.respond_to?(:field) ? field_receiver_type.field(callee.member) : nil
611
610
  return [:callable_value, nil, nil, member_type, nil] if callable_type?(member_type)
612
611
 
@@ -1055,7 +1054,7 @@ module MilkTea
1055
1054
  end
1056
1055
  end
1057
1056
 
1058
- def type_contains_array_storage?(type, visited = Set.new)
1057
+ def type_contains_array_storage?(type)
1059
1058
  visitor = Types::ContainsArrayStorageVisitor.new
1060
1059
  visitor.visit(type)
1061
1060
  visitor.found?
@@ -1118,62 +1117,6 @@ module MilkTea
1118
1117
  expected_type || null_type
1119
1118
  end
1120
1119
 
1121
- def common_numeric_type(left_type, right_type)
1122
- left_type = left_type.backing_type if left_type.is_a?(Types::EnumBase)
1123
- right_type = right_type.backing_type if right_type.is_a?(Types::EnumBase)
1124
- return left_type if left_type == right_type
1125
- return unless left_type.is_a?(Types::Primitive) && right_type.is_a?(Types::Primitive)
1126
- return unless left_type.numeric? && right_type.numeric?
1127
-
1128
- return common_integer_type(left_type, right_type) if left_type.integer? && right_type.integer?
1129
- return wider_float_type(left_type, right_type) if left_type.float? && right_type.float?
1130
-
1131
- float_type, integer_type = left_type.float? ? [left_type, right_type] : [right_type, left_type]
1132
- return unless integer_type.integer? && integer_type.fixed_width_integer?
1133
-
1134
- float_type
1135
- end
1136
-
1137
- def common_integer_type(left_type, right_type)
1138
- left_type = left_type.backing_type if left_type.is_a?(Types::EnumBase)
1139
- right_type = right_type.backing_type if right_type.is_a?(Types::EnumBase)
1140
- return left_type if left_type == right_type
1141
- return unless left_type.is_a?(Types::Primitive) && right_type.is_a?(Types::Primitive)
1142
- return unless left_type.integer? && right_type.integer?
1143
- return unless left_type.fixed_width_integer? && right_type.fixed_width_integer?
1144
-
1145
- # Mirrors the semantic analyzer's rule: same signedness picks the wider
1146
- # type; mixed signed/unsigned promotes to the narrowest signed type that
1147
- # holds both operands' full ranges (a strictly-wider signed type covers
1148
- # an unsigned operand, otherwise widen to the next signed width). Mixing
1149
- # with a 64-bit unsigned type has no safe signed common type.
1150
- if left_type.signed_integer? == right_type.signed_integer?
1151
- return left_type.integer_width >= right_type.integer_width ? left_type : right_type
1152
- end
1153
-
1154
- signed_type, unsigned_type = if left_type.signed_integer?
1155
- [left_type, right_type]
1156
- else
1157
- [right_type, left_type]
1158
- end
1159
-
1160
- return signed_type if signed_type.integer_width > unsigned_type.integer_width
1161
-
1162
- signed_type_above_width(unsigned_type.integer_width)
1163
- end
1164
-
1165
- def signed_type_above_width(width)
1166
- case width
1167
- when 8 then @ctx.types.fetch("short")
1168
- when 16 then @ctx.types.fetch("int")
1169
- when 32 then @ctx.types.fetch("long")
1170
- end
1171
- end
1172
-
1173
- def wider_float_type(left_type, right_type)
1174
- left_type.float_width >= right_type.float_width ? left_type : right_type
1175
- end
1176
-
1177
1120
  def aggregate_arithmetic_result_type(operator, left_type, right_type)
1178
1121
  if left_type.is_a?(Types::Vector) && right_type.is_a?(Types::Vector) && left_type.name == right_type.name
1179
1122
  return left_type
@@ -1217,7 +1160,7 @@ module MilkTea
1217
1160
  when AST::Identifier
1218
1161
  return current_type_params[expression.name] if current_type_params.key?(expression.name)
1219
1162
 
1220
- @ctx.types[expression.name]
1163
+ lookup_named_type(expression.name)
1221
1164
  when AST::MemberAccess
1222
1165
  return nil unless expression.receiver.is_a?(AST::Identifier)
1223
1166
 
@@ -1870,10 +1813,6 @@ module MilkTea
1870
1813
  def top_level_function(name)
1871
1814
  @lowerer.instance_variable_get(:@ctx).functions&.[](name)
1872
1815
  end
1873
-
1874
- def raise_sema_error(message)
1875
- raise CompileTime::Error, message
1876
- end
1877
1816
  end
1878
1817
 
1879
1818
  def evaluate_reflection_target_argument(expression, env:)
@@ -2532,7 +2471,7 @@ module MilkTea
2532
2471
  elsif parts.length == 1 && type_params.key?(parts.first)
2533
2472
  type_params.fetch(parts.first)
2534
2473
  elsif parts.length == 1
2535
- type = @ctx.types[parts.first]
2474
+ type = lookup_named_type(parts.first)
2536
2475
  raise LoweringError.new("unknown type #{parts.first}", line: 0, column: 0, path: @ctx.current_analysis_path) unless type
2537
2476
  raise LoweringError.new("generic type #{parts.first} requires type arguments", line: 0, column: 0, path: @ctx.current_analysis_path) if type.is_a?(Types::GenericStructDefinition) || type.is_a?(Types::GenericVariantDefinition)
2538
2477
 
@@ -155,18 +155,6 @@ module MilkTea
155
155
  receiver_type
156
156
  end
157
157
 
158
- def collection_loop_type(type)
159
- super
160
- end
161
-
162
- def collection_loop_binding_type(iterable_type, element_type)
163
- super
164
- end
165
-
166
- def collection_loop_ref_element_type?(type)
167
- super
168
- end
169
-
170
158
  def collection_loop_item_value(iterable_ref, iterable_type, index_ref, element_type)
171
159
  if array_type?(iterable_type)
172
160
  IR::Index.new(receiver: iterable_ref, index: index_ref, type: element_type)
@@ -248,11 +236,6 @@ module MilkTea
248
236
  raise LoweringError.new("cannot index #{receiver_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
249
237
  end
250
238
 
251
- def contains_type_var?(type)
252
- super
253
- end
254
-
255
-
256
239
  def stored_ref_supported_type?(type, visited = {})
257
240
  return true unless type
258
241
 
@@ -100,7 +100,6 @@ module MilkTea
100
100
  include Intrinsics
101
101
 
102
102
  attr_accessor :bypass_sema_type_cache
103
- attr_reader :recorded_expr_types
104
103
 
105
104
  include Lowering::Scans
106
105
  include Lowering::Declarations
@@ -122,6 +121,7 @@ module MilkTea
122
121
  @program = program
123
122
  @ctx = LoweringContext.new
124
123
  @artifacts = Artifacts.new
124
+ @error_type = Types::Error.new
125
125
  @synthetic_proc_counter = 0
126
126
  @parallel_for_counter = 0
127
127
  @async_binding_counter = 0
@@ -130,7 +130,6 @@ module MilkTea
130
130
  end
131
131
 
132
132
  def lower
133
- @recorded_expr_types = {} if @bypass_sema_type_cache
134
133
  ir_program, _modules, _synths = lower_and_assemble
135
134
  ir_program
136
135
  end
@@ -42,7 +42,7 @@ module MilkTea
42
42
  end
43
43
 
44
44
  methods, private_methods = exported_methods(analysis, types)
45
- implemented_interfaces, private_implemented_interfaces = exported_interface_implementations(analysis, types, interfaces)
45
+ implemented_interfaces = exported_interface_implementations(analysis, types, interfaces)
46
46
 
47
47
  Bindings::ModuleBinding.new(
48
48
  name: analysis.module_name,
@@ -62,7 +62,6 @@ module MilkTea
62
62
  private_values:,
63
63
  private_functions:,
64
64
  private_methods:,
65
- private_implemented_interfaces:,
66
65
  )
67
66
  end
68
67
 
@@ -102,28 +101,18 @@ module MilkTea
102
101
 
103
102
  def exported_interface_implementations(analysis, exported_types, exported_interfaces)
104
103
  implemented_interfaces = {}
105
- private_implemented_interfaces = {}
106
104
 
107
105
  analysis.implemented_interfaces.each do |receiver_type, interfaces|
108
- public_interfaces = []
109
- hidden_interfaces = []
110
-
111
- interfaces.each do |interface|
112
- visible = exported_method_receiver?(receiver_type, analysis, exported_types) &&
106
+ public_interfaces = interfaces.select do |interface|
107
+ exported_method_receiver?(receiver_type, analysis, exported_types) &&
113
108
  exported_interface_binding?(interface, analysis, exported_interfaces) &&
114
109
  exported_interface_methods?(receiver_type, interface, analysis, exported_types)
115
- if visible
116
- public_interfaces << interface
117
- else
118
- hidden_interfaces << interface
119
- end
120
110
  end
121
111
 
122
112
  implemented_interfaces[receiver_type] = public_interfaces.freeze unless public_interfaces.empty?
123
- private_implemented_interfaces[receiver_type] = hidden_interfaces.freeze unless hidden_interfaces.empty?
124
113
  end
125
114
 
126
- [implemented_interfaces.freeze, private_implemented_interfaces.freeze]
115
+ implemented_interfaces.freeze
127
116
  end
128
117
 
129
118
  def exported_interface_methods?(receiver_type, interface, analysis, exported_types)
@@ -158,7 +158,30 @@ module MilkTea
158
158
 
159
159
  def load_file(path)
160
160
  resolved_path = self.class.resolve_source_path(path, platform: @platform, error_class: ModuleLoadError)
161
- @ast_cache[resolved_path] ||= parse_file(resolved_path)
161
+ @ast_cache[resolved_path] ||= cached_or_parse_file(resolved_path)
162
+ end
163
+
164
+ # Parse +resolved_path+, reusing the shared-cache parsed AST when the source
165
+ # is disk-backed and unchanged. ASTs are immutable Data, so sharing them
166
+ # across loaders (and worker threads) is safe; node_ids are assigned at
167
+ # parse time and analyses keep their own per-node result hashes. Source
168
+ # files covered by source_overrides are always re-parsed so live editor
169
+ # buffers win.
170
+ def cached_or_parse_file(resolved_path)
171
+ if use_shared_cache_for?(resolved_path)
172
+ mtime = source_mtime(resolved_path)
173
+ if mtime
174
+ entry = @shared_cache[[:ast, resolved_path]]
175
+ return entry[:ast] if entry && entry[:mtime] == mtime
176
+ end
177
+ end
178
+
179
+ ast = parse_file(resolved_path)
180
+ if use_shared_cache_for?(resolved_path)
181
+ mtime = source_mtime(resolved_path)
182
+ @shared_cache[[:ast, resolved_path]] = { mtime:, ast: } if mtime
183
+ end
184
+ ast
162
185
  end
163
186
 
164
187
  def check_file(path)
@@ -166,7 +189,14 @@ module MilkTea
166
189
  end
167
190
 
168
191
  def with_check_context(path, &block)
169
- Types::Registry.reset!
192
+ # NOTE: Types::Registry.reset! is intentionally NOT called here. The LSP
193
+ # runs check_program_collecting concurrently on diagnostics workers and
194
+ # the request thread; a reset would clear the global intern pool while
195
+ # another thread is mid-analysis, causing threads to intern duplicate type
196
+ # instances. Interning is idempotent and the pool stays small (bounded by
197
+ # the type universe), so it is safe to let it grow for the process
198
+ # lifetime. Single-threaded entry points that want a clean slate (Build#build,
199
+ # server re-initialize) reset explicitly.
170
200
  requested_path = File.expand_path(path)
171
201
  previous_platform = @platform
172
202
  @platform ||= self.class.platform_suffix_for_path(requested_path)
@@ -541,7 +571,7 @@ module MilkTea
541
571
  return [resolved_path, nil, @analysis_cache[resolved_path]] if @analysis_cache.key?(resolved_path)
542
572
  return [resolved_path, nil, extra_cache[resolved_path]] if extra_cache&.key?(resolved_path)
543
573
 
544
- if use_shared_cache?
574
+ if use_shared_cache_for?(resolved_path)
545
575
  entry = @shared_cache[resolved_path]
546
576
  if entry
547
577
  mtime = source_mtime(resolved_path)
@@ -566,7 +596,7 @@ module MilkTea
566
596
  end
567
597
 
568
598
  def update_shared_cache(resolved_path, analysis)
569
- return unless use_shared_cache?
599
+ return unless use_shared_cache_for?(resolved_path)
570
600
 
571
601
  mtime = source_mtime(resolved_path)
572
602
  @shared_cache[resolved_path] = { mtime:, analysis: } if mtime
@@ -691,7 +721,6 @@ module MilkTea
691
721
  private_values: full_binding.private_values,
692
722
  private_functions: full_binding.private_functions,
693
723
  private_methods: full_binding.private_methods,
694
- private_implemented_interfaces: full_binding.private_implemented_interfaces,
695
724
  )
696
725
  end
697
726
  end
@@ -728,12 +757,16 @@ module MilkTea
728
757
  implemented_interfaces: {}, imports: {},
729
758
  private_types: {}, private_interfaces: {}, private_attributes: {},
730
759
  private_values: {}, private_functions: {}, private_methods: {},
731
- private_implemented_interfaces: {},
732
760
  )
733
761
  end
734
762
 
735
- def use_shared_cache?
736
- @shared_cache && @source_overrides.empty?
763
+ # The shared cache stores per-module analyses keyed by resolved path. It is
764
+ # consulted only for paths NOT covered by source_overrides: source-overridden
765
+ # files reflect live editor buffers (or in-memory sources) that never match
766
+ # disk, so their entries would be stale by construction. This lets a single
767
+ # open document invalidate only itself while imported modules stay cached.
768
+ def use_shared_cache_for?(resolved_path)
769
+ @shared_cache && !@source_overrides.key?(resolved_path)
737
770
  end
738
771
  end
739
772
  end
@@ -369,10 +369,11 @@ module MilkTea
369
369
  AST::AttributeDecl.new(name: name_token.lexeme, targets:, params:, visibility:, line:, column: name_token.column)
370
370
  end
371
371
 
372
- def parse_struct_decl(packed: false, alignment: nil, visibility: :private, attributes: [], inline_methods: true)
372
+ def parse_struct_decl(packed: false, alignment: nil, visibility: :private, attributes: [], qualified_parts: [])
373
373
  line = previous.line
374
374
  name_token = consume_name("expected struct name")
375
375
  name = name_token.lexeme
376
+ qualified_name = qualified_parts + [name]
376
377
  lifetime_params, type_params = parse_struct_decl_params
377
378
  implements = parse_implements_clause
378
379
  c_name = parse_optional_c_name
@@ -380,29 +381,33 @@ module MilkTea
380
381
  receiver_type_param_names = type_params.map(&:name)
381
382
  members = with_type_param_names(receiver_type_param_names) do
382
383
  parse_recoverable_block do
383
- parse_struct_member
384
+ parse_struct_member(qualified_name)
384
385
  end
385
386
  end
386
387
  fields = members.filter_map { |kind, member| member if kind == :field }
387
388
  events = members.filter_map { |kind, member| member if kind == :event }
388
389
  nested_types = members.filter_map { |kind, member| member if kind == :nested_type }
389
390
  methods = members.filter_map { |kind, member| member if kind == :method }
391
+ nested_extending_blocks = members.filter_map { |kind, _, blocks| blocks if kind == :nested_type }.flatten(1)
390
392
  struct_decl = AST::StructDecl.new(name:, type_params:, implements:, c_name:, fields:, events:, nested_types:, attributes:, packed:, alignment:, visibility:, lifetime_params:, line:, column: name_token.column)
391
393
 
392
- if inline_methods && methods.any?
393
- type_ref_args = type_params.map do |tp|
394
- AST::TypeArgument.new(
395
- value: AST::TypeRef.new(name: AST::QualifiedName.new(parts: [tp.name]), arguments: [], nullable: false, line: tp.line, column: tp.column),
396
- line: tp.line,
397
- column: tp.column,
398
- )
399
- end
400
- type_ref = AST::TypeRef.new(name: AST::QualifiedName.new(parts: [name]), arguments: type_ref_args, nullable: false, line: name_token.line, column: name_token.column)
401
- extending_block = AST::ExtendingBlock.new(type_name: type_ref, methods:, line: name_token.line, column: name_token.column)
402
- [struct_decl, extending_block]
403
- else
404
- struct_decl
394
+ extending_blocks = []
395
+ extending_blocks << inline_methods_extending_block(qualified_name, type_params, methods, name_token) if methods.any?
396
+ extending_blocks.concat(nested_extending_blocks)
397
+
398
+ extending_blocks.empty? ? struct_decl : [struct_decl, *extending_blocks]
399
+ end
400
+
401
+ def inline_methods_extending_block(qualified_name, type_params, methods, name_token)
402
+ type_ref_args = type_params.map do |tp|
403
+ AST::TypeArgument.new(
404
+ value: AST::TypeRef.new(name: AST::QualifiedName.new(parts: [tp.name]), arguments: [], nullable: false, line: tp.line, column: tp.column),
405
+ line: tp.line,
406
+ column: tp.column,
407
+ )
405
408
  end
409
+ type_ref = AST::TypeRef.new(name: AST::QualifiedName.new(parts: qualified_name), arguments: type_ref_args, nullable: false, line: name_token.line, column: name_token.column)
410
+ AST::ExtendingBlock.new(type_name: type_ref, methods:, line: name_token.line, column: name_token.column, inline: true)
406
411
  end
407
412
 
408
413
  def parse_struct_decl_params
@@ -459,7 +464,7 @@ module MilkTea
459
464
  result
460
465
  end
461
466
 
462
- def parse_struct_member
467
+ def parse_struct_member(qualified_name)
463
468
  field_attributes = parse_attribute_applications
464
469
 
465
470
  if check_method_start?
@@ -475,7 +480,9 @@ module MilkTea
475
480
 
476
481
  if check(:struct) && !check_next(:colon)
477
482
  advance
478
- return [:nested_type, parse_struct_decl(visibility:, attributes: field_attributes, inline_methods: false)]
483
+ result = parse_struct_decl(visibility:, attributes: field_attributes, qualified_parts: qualified_name)
484
+ nested_decl, *nested_blocks = result.is_a?(Array) ? result : [result]
485
+ return [:nested_type, nested_decl, nested_blocks]
479
486
  end
480
487
 
481
488
  raise error(visibility_token, "public is only allowed on struct events") if visibility == :public