mt-lang 0.2.6 → 0.2.8

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: 10511367134e7b86901ee81dd22007c1e902139f3dc3e738edcbbab52ce6f978
4
- data.tar.gz: bbb13727c9cbecc0dc890e6d2f66620c1e28a9e0c9d924600c34ee29b19bb1f8
3
+ metadata.gz: fcc2383591d95c6043caebcb271aba6b70650528dafbb1a3cd8b906e74a5a6b6
4
+ data.tar.gz: 2e264acc4e563b29caf921062ca9156f92869d101b1f16f6516f9b412061a1c9
5
5
  SHA512:
6
- metadata.gz: f3c5d9fb56448090e0576193a8a892f88fa1f8185c49d11f42e6946cecf6545cef4027da911e1ed7714aad46b740796e872788a936fda47a4276fd02df3adfae
7
- data.tar.gz: 97dd1426ab931fb7776a910767d7182113490bd936cc534868ed80046f368c17034099305ab4ee1dd8be09fd6c4acd0cf89d1061527617b583e578250eafd324
6
+ metadata.gz: 17ef473e87741d685323aa279e434ce56a24b7c30cc4b48f61cbb7d4b7c49d47300c7cbad1e4211740fc35ecc29bf36ee941fa0b4bb18ac0fc19d04aee69d7ab
7
+ data.tar.gz: 9952dc4f2a306b443cbd57c4840965c8c7e870f8f9f976643b063635c64e371c0f8e903ea3446b124cd0b7e9ddf6754807c924e90426c1bfe789124bc08d6d44
data/README.md CHANGED
@@ -269,6 +269,7 @@ struct Rectangle:
269
269
  - Nested structs may themselves contain nested structs to arbitrary depth.
270
270
  - The generated C name uses underscore-separated path components (e.g., `module_Rectangle_Edge`).
271
271
 
272
+ ```mt
272
273
  union Number:
273
274
  i: int
274
275
  f: float
@@ -276,8 +277,10 @@ union Number:
276
277
  enum State: ubyte
277
278
  idle = 0
278
279
  running = 1
280
+ ```
279
281
 
280
- # Backing type defaults to int; values auto-increment from 0:
282
+ Backing type defaults to int; values auto-increment from 0:
283
+ ```mt
281
284
  enum Color:
282
285
  red
283
286
  green
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.6"
6
+ VERSION = "0.2.8"
7
7
 
8
8
  def self.root
9
9
  @root ||= Pathname.new(File.expand_path("../..", __dir__))
@@ -80,7 +80,7 @@ module MilkTea
80
80
  def resolve_extending_receiver_type(analysis, type_name)
81
81
  if type_name.is_a?(AST::TypeRef)
82
82
  generic_type = resolve_named_generic_type_for_analysis(analysis, type_name.name.parts)
83
- if generic_type.is_a?(Types::GenericStructDefinition)
83
+ if generic_type.is_a?(Types::GenericStructDefinition) || generic_type.is_a?(Types::GenericVariantDefinition)
84
84
  validate_methods_receiver_type_arguments!(type_name, generic_type)
85
85
  return generic_type
86
86
  end
@@ -430,6 +430,8 @@ module MilkTea
430
430
  [:get, nil, nil, nil]
431
431
  elsif (type = @ctx.types[callee.name]).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)
432
432
  [ :struct_literal, nil, nil, type ]
433
+ elsif (type = @ctx.types[callee.name]).is_a?(Types::GenericStructDefinition) || type.is_a?(Types::GenericVariantDefinition)
434
+ raise LoweringError, "generic type #{callee.name} requires type arguments"
433
435
  else
434
436
  emit_fn = @artifacts.emitted_declarations.find { |d| d.is_a?(IR::Function) && d.name == callee.name }
435
437
  if emit_fn
@@ -452,6 +454,10 @@ module MilkTea
452
454
  return [:function, external_function_c_name(binding), nil, binding.type, binding]
453
455
  end
454
456
  imported_type = imported_module.types[callee.member]
457
+ if imported_type.is_a?(Types::GenericStructDefinition) || imported_type.is_a?(Types::GenericVariantDefinition)
458
+ raise LoweringError, "generic type #{callee.receiver.name}.#{callee.member} requires type arguments"
459
+ end
460
+
455
461
  if imported_type.is_a?(Types::Struct) || imported_type.is_a?(Types::StringView) || task_type?(imported_type) || imported_type.is_a?(Types::Vector) || imported_type.is_a?(Types::Matrix) || imported_type.is_a?(Types::Quaternion)
456
462
  return [:struct_literal, nil, nil, imported_module.types.fetch(callee.member)]
457
463
  end
@@ -1005,7 +1005,7 @@ module MilkTea
1005
1005
  )
1006
1006
 
1007
1007
  check_function_call(callable, expression.arguments, scopes:)
1008
- callable.owner.send(:check_function, callable) unless callable.type_arguments.empty?
1008
+ validate_specialized_function_body(callable) unless callable.type_arguments.empty?
1009
1009
  callable.type.return_type
1010
1010
  when :method
1011
1011
  callable = specialize_function_binding(
@@ -1018,7 +1018,7 @@ module MilkTea
1018
1018
  raise_sema_error("cannot call editable method #{callable.name} on an immutable receiver") if callable.type.receiver_editable && !assignable_receiver?(receiver, scopes)
1019
1019
 
1020
1020
  check_function_call(callable, expression.arguments, scopes:)
1021
- callable.owner.send(:check_function, callable) unless callable.type_arguments.empty?
1021
+ validate_specialized_function_body(callable) unless callable.type_arguments.empty?
1022
1022
  callable.type.return_type
1023
1023
  when :callable_value
1024
1024
  check_callable_value_call(callable, expression.arguments, scopes:, callee_expression: expression.callee)
@@ -1342,7 +1342,11 @@ module MilkTea
1342
1342
  imported_module = @ctx.imports.fetch(callee.receiver.name)
1343
1343
  return [:function, imported_module.functions.fetch(callee.member), nil] if imported_module.functions.key?(callee.member)
1344
1344
  imported_type = imported_module.types[callee.member]
1345
- if imported_type.is_a?(Types::Struct) || imported_type.is_a?(Types::GenericStructDefinition) || imported_type.is_a?(Types::StringView) || task_type?(imported_type) || imported_type.is_a?(Types::Vector) || imported_type.is_a?(Types::Matrix) || imported_type.is_a?(Types::Quaternion)
1345
+ if imported_type.is_a?(Types::GenericStructDefinition) || imported_type.is_a?(Types::GenericVariantDefinition)
1346
+ raise_sema_error("generic type #{callee.receiver.name}.#{callee.member} requires type arguments", callee)
1347
+ end
1348
+
1349
+ if imported_type.is_a?(Types::Struct) || imported_type.is_a?(Types::StringView) || task_type?(imported_type) || imported_type.is_a?(Types::Vector) || imported_type.is_a?(Types::Matrix) || imported_type.is_a?(Types::Quaternion)
1346
1350
  return [:struct, imported_module.types.fetch(callee.member), nil]
1347
1351
  end
1348
1352
 
@@ -256,6 +256,22 @@ module MilkTea
256
256
  end
257
257
  end
258
258
 
259
+ # Validates the body of a specialized (instantiated) function or method
260
+ # binding. The owner checker may be in collecting-errors mode, which
261
+ # would silently swallow body errors into @structural_errors. We
262
+ # temporarily disable collect mode on the owner so the caller receives
263
+ # the SemanticError directly.
264
+ def validate_specialized_function_body(binding)
265
+ owner = binding.owner
266
+ prev_collecting = owner.instance_variable_get(:@collecting_errors)
267
+ owner.instance_variable_set(:@collecting_errors, false)
268
+ owner.send(:check_function, binding)
269
+ rescue SemanticError => e
270
+ raise unless e.message.include?("cannot assign through immutable")
271
+ ensure
272
+ owner.instance_variable_set(:@collecting_errors, prev_collecting) if owner
273
+ end
274
+
259
275
  # Per-function error collection used by check_collecting_errors.
260
276
  # Continues past individual function failures, accumulating SemanticErrors.
261
277
  def check_functions_collecting(errors)
@@ -286,13 +302,29 @@ module MilkTea
286
302
  end
287
303
  end
288
304
 
305
+ # Scans generic method bodies for assignments to this through a
306
+ # non-editable receiver. Full body checking is deferred to call-site
307
+ # specialization, but immutable-this violations are type-independent.
308
+ def check_generic_method_immutable_this(binding, scopes)
309
+ this_binding = scopes.first&.values&.find { |v| v.name == "this" }
310
+ return unless this_binding
311
+ return if this_binding.mutable
312
+
313
+ binding.ast.body&.each do |statement|
314
+ next unless statement.is_a?(AST::Assignment) && statement.operator == "="
315
+ next unless statement.target.is_a?(AST::MemberAccess) && statement.target.receiver.is_a?(AST::Identifier) && statement.target.receiver.name == "this"
316
+
317
+ raise_sema_error("cannot assign through immutable this", statement.target)
318
+ end
319
+ end
320
+
289
321
  def check_function(binding)
290
322
  @local_completion_frames = @local_completion_frames.dup if @local_completion_frames.frozen?
291
323
 
292
324
  previous_type_substitutions = @current_type_substitutions
293
325
  previous_specialization_owner = @current_specialization_owner
294
326
  started_check = false
295
- return if binding.external || binding.type_params.any?
327
+ return if binding.external
296
328
  return if @checked_function_bindings[binding.object_id]
297
329
  return if @checking_function_bindings[binding.object_id]
298
330
 
@@ -303,6 +335,11 @@ module MilkTea
303
335
  with_error_node(binding.ast) do
304
336
  with_scope(binding.body_params) do |scopes|
305
337
  start_local_completion_frame(binding, scopes)
338
+ if binding.type_params.any?
339
+ check_generic_method_immutable_this(binding, scopes)
340
+ return
341
+ end
342
+
306
343
  if binding.ast.is_a?(AST::ForeignFunctionDecl)
307
344
  record_callable_value_expression_site(binding.ast.mapping) unless binding.ast.mapping.is_a?(AST::Call)
308
345
  expression = foreign_mapping_expression(binding.ast)
@@ -337,10 +374,10 @@ module MilkTea
337
374
  end
338
375
  end
339
376
  end
340
- @checked_function_bindings[binding.object_id] = true
341
377
  ensure
342
378
  return unless started_check
343
379
 
380
+ @checked_function_bindings[binding.object_id] = true
344
381
  finish_local_completion_frame(binding)
345
382
  @preassigned_local_binding_ids = {}
346
383
  @nullability_flow_result = nil
@@ -304,10 +304,17 @@ module MilkTea
304
304
  end
305
305
  display_name = type_name.is_a?(Array) ? type_name.join(".") : type_name
306
306
  raise_sema_error("unknown type #{display_name} for struct destructure") unless struct_type
307
- raise_sema_error("#{display_name} is not a struct") unless struct_type.is_a?(Types::Struct) || struct_type.is_a?(Types::Tuple)
308
- ensure_assignable!(value_type, struct_type, "cannot destructure #{value_type} as #{display_name}")
309
-
310
- fields = struct_type.fields
307
+ if struct_type.is_a?(Types::GenericStructDefinition)
308
+ if value_type.is_a?(Types::StructInstance) && value_type.definition == struct_type
309
+ fields = value_type.fields
310
+ else
311
+ raise_sema_error("generic type #{display_name} requires type arguments")
312
+ end
313
+ else
314
+ raise_sema_error("#{display_name} is not a struct") unless struct_type.is_a?(Types::Struct) || struct_type.is_a?(Types::Tuple)
315
+ ensure_assignable!(value_type, struct_type, "cannot destructure #{value_type} as #{display_name}")
316
+ fields = struct_type.fields
317
+ end
311
318
  raise_sema_error("destructure pattern has #{statement.destructure_bindings.length} bindings but #{display_name} has #{fields.length} fields") unless statement.destructure_bindings.length == fields.length
312
319
 
313
320
  current_scope = current_actual_scope(scopes)
@@ -36,7 +36,7 @@ module MilkTea
36
36
 
37
37
  def to_diagnostic(path: nil)
38
38
  Diagnostic.new(
39
- path:,
39
+ path: @path || path,
40
40
  line: @line,
41
41
  column: @column,
42
42
  length: @length,
@@ -87,8 +87,17 @@ module MilkTea
87
87
  sema_start = total_start ? monotonic_time : nil
88
88
  sema_snapshot ||= SemanticAnalyzer.tooling_snapshot(ast, imported_modules: imported_modules.fetch(:modules), path: path, allow_missing_imports: true)
89
89
  sema_facts = sema_snapshot.facts
90
+ cross_by_uri = {}
91
+ target_path = path.is_a?(String) ? File.expand_path(path) : nil
90
92
  sema_snapshot.diagnostics.reject { |diagnostic| redundant_unknown_import_diagnostic?(diagnostic, unresolved_import_paths) }
91
- .each { |diagnostic| diagnostics << format_tooling_diagnostic(diagnostic, stage: 'sema') }
93
+ .each do |diagnostic|
94
+ if target_path && diagnostic.path && File.expand_path(diagnostic.path) != target_path
95
+ target_uri = path_to_uri(diagnostic.path)
96
+ (cross_by_uri[target_uri] ||= []) << format_tooling_diagnostic(diagnostic, stage: 'sema')
97
+ else
98
+ diagnostics << format_tooling_diagnostic(diagnostic, stage: 'sema')
99
+ end
100
+ end
92
101
  sema_ms = elapsed_ms(sema_start) if sema_start
93
102
  rescue StandardError => e
94
103
  log_diagnostics_warning("Error collecting diagnostics: #{e.message}")
@@ -142,7 +151,7 @@ module MilkTea
142
151
  log_diagnostics_warning("Error collecting lint diagnostics: #{e.message}")
143
152
  end
144
153
 
145
- { diagnostics: diagnostics, facts: sema_facts, sema_snapshot: sema_snapshot }
154
+ { diagnostics: diagnostics, cross_file_diagnostics: cross_by_uri, facts: sema_facts, sema_snapshot: sema_snapshot }
146
155
  ensure
147
156
  if total_start
148
157
  lint_breakdown = lint_profile&.summary(limit: 12)
@@ -196,7 +205,15 @@ module MilkTea
196
205
  def self.redundant_unknown_import_diagnostic?(diagnostic, unresolved_import_paths)
197
206
  return false unless diagnostic&.message.to_s.start_with?("unknown import ")
198
207
 
199
- unresolved_import_paths.include?(diagnostic.message.delete_prefix("unknown import "))
208
+ import_path = diagnostic.message.to_s.match(/unknown import (.+)$/)&.captures&.first
209
+ return false unless import_path
210
+
211
+ unresolved_import_paths.include?(import_path)
212
+ end
213
+
214
+ def self.path_to_uri(path)
215
+ escaped_path = path.split('/').map { |seg| CGI.escape(seg).gsub('+', '%20') }.join('/')
216
+ "file://#{escaped_path}"
200
217
  end
201
218
 
202
219
  def self.uri_to_path(uri)
@@ -366,7 +366,8 @@ module MilkTea
366
366
  return { kind: 'full', items: [] } unless uri
367
367
 
368
368
  content = @workspace.get_content(uri)
369
- diagnostics = @workspace.collect_diagnostics(uri)
369
+ result = @workspace.collect_diagnostics(uri)
370
+ diagnostics = result
370
371
  fingerprint = diagnostics_fingerprint(content, diagnostics)
371
372
  previous_result_id = params['previousResultId']
372
373
  cached = @diagnostic_report_cache[uri]
@@ -384,6 +385,16 @@ module MilkTea
384
385
  fingerprint: fingerprint
385
386
  }
386
387
 
388
+ cross = @workspace.cross_file_diagnostics
389
+ if cross&.any?
390
+ cross.each do |target_uri, items|
391
+ @protocol.write_notification('textDocument/publishDiagnostics', {
392
+ uri: target_uri,
393
+ diagnostics: items
394
+ })
395
+ end
396
+ end
397
+
387
398
  {
388
399
  kind: 'full',
389
400
  resultId: result_id,
@@ -473,7 +473,7 @@ module MilkTea
473
473
 
474
474
  current_type = first_type
475
475
  chain_segments[1..].each do |seg|
476
- field_owner = project_field_receiver_type_for_completion(current_type)
476
+ field_owner = project_field_receiver_type_for_completion(current_type, facts)
477
477
  if field_owner.respond_to?(:field) && (field_type = field_owner.field(seg))
478
478
  current_type = field_type
479
479
  else
@@ -674,7 +674,7 @@ module MilkTea
674
674
  def completion_items_for_value_receiver(facts, receiver_type, prefix)
675
675
  items = []
676
676
 
677
- field_receiver_type = project_field_receiver_type_for_completion(receiver_type)
677
+ field_receiver_type = project_field_receiver_type_for_completion(receiver_type, facts)
678
678
  if field_receiver_type.respond_to?(:fields)
679
679
  field_receiver_type.fields.each do |fname, ftype|
680
680
  next unless prefix.empty? || fname.start_with?(prefix)
@@ -763,8 +763,20 @@ module MilkTea
763
763
  dispatch_receiver_type == receiver_type ? receiver_type : dispatch_receiver_type
764
764
  end
765
765
 
766
- def project_field_receiver_type_for_completion(type)
767
- project_receiver_type_for_completion(type)
766
+ def project_field_receiver_type_for_completion(type, facts = nil)
767
+ type = project_receiver_type_for_completion(type)
768
+ return type.definition if type.is_a?(Types::StructInstance)
769
+ return field_type_from_struct_definition(type, facts) if facts
770
+
771
+ type
772
+ end
773
+
774
+ def field_type_from_struct_definition(type, facts)
775
+ return type unless type.is_a?(Types::GenericInstance)
776
+ return type if ref_type_name?(type) || pointer_type_name?(type)
777
+
778
+ struct_def = facts.types[type.name]
779
+ struct_def if struct_def&.respond_to?(:fields)
768
780
  end
769
781
 
770
782
  def project_method_receiver_type_for_completion(type)
@@ -778,8 +790,8 @@ module MilkTea
778
790
  type
779
791
  end
780
792
 
781
- def field_owner_type(receiver_type)
782
- aggregate_type = project_field_receiver_type_for_completion(receiver_type)
793
+ def field_owner_type(receiver_type, facts = nil)
794
+ aggregate_type = project_field_receiver_type_for_completion(receiver_type, facts)
783
795
  return aggregate_type.definition if aggregate_type.is_a?(Types::StructInstance)
784
796
 
785
797
  aggregate_type
@@ -247,7 +247,7 @@ module MilkTea
247
247
  return nil unless current_type
248
248
 
249
249
  chain[:segments][1..hovered_segment[:position]].each do |segment|
250
- field_receiver_type = project_field_receiver_type_for_completion(current_type)
250
+ field_receiver_type = project_field_receiver_type_for_completion(current_type, facts)
251
251
  if field_receiver_type.respond_to?(:field) && (field_type = field_receiver_type.field(segment[:name]))
252
252
  return field_definition_location(current_uri, field_receiver_type, segment[:name]) if segment[:token_index] == token_index
253
253
 
@@ -133,6 +133,16 @@ module MilkTea
133
133
  diagnostics: diagnostics
134
134
  })
135
135
  notify_diagnostic_errors(uri, diagnostics)
136
+
137
+ cross = @workspace.cross_file_diagnostics
138
+ if cross&.any?
139
+ cross.each do |target_uri, items|
140
+ @protocol.write_notification('textDocument/publishDiagnostics', {
141
+ uri: target_uri,
142
+ diagnostics: items
143
+ })
144
+ end
145
+ end
136
146
  end
137
147
  elsif perf_logging?
138
148
  @diagnostics_perf[:dropped_stale] += 1
@@ -649,7 +649,7 @@ module MilkTea
649
649
  return nil unless current_type
650
650
 
651
651
  chain[:segments][1..hovered_segment[:position]].each do |segment|
652
- field_receiver_type = project_field_receiver_type_for_completion(current_type)
652
+ field_receiver_type = project_field_receiver_type_for_completion(current_type, facts)
653
653
  if field_receiver_type.respond_to?(:field) && (field_type = field_receiver_type.field(segment[:name]))
654
654
  source_location = field_definition_location(current_uri, field_receiver_type, segment[:name])
655
655
 
@@ -787,7 +787,7 @@ module MilkTea
787
787
  return nil unless receiver_info
788
788
 
789
789
  field_name = tokens[token_index].lexeme
790
- receiver_type = project_field_receiver_type_for_completion(receiver_info[:type])
790
+ receiver_type = project_field_receiver_type_for_completion(receiver_info[:type], facts)
791
791
  return nil unless receiver_type.respond_to?(:field)
792
792
 
793
793
  field_type = receiver_type.field(field_name)
@@ -809,7 +809,7 @@ module MilkTea
809
809
 
810
810
  field_name = tokens[token_index].lexeme
811
811
  if receiver_info
812
- receiver_type = project_field_receiver_type_for_completion(receiver_info[:type])
812
+ receiver_type = project_field_receiver_type_for_completion(receiver_info[:type], facts)
813
813
  if receiver_type.respond_to?(:field)
814
814
  field_type = receiver_type.field(field_name)
815
815
  if field_type
@@ -1421,7 +1421,7 @@ module MilkTea
1421
1421
  def_index = tokens.index(definition_token)
1422
1422
  if def_index
1423
1423
  prev = previous_non_trivia_token_index(tokens, def_index)
1424
- if prev && tokens[prev].type == :identifier
1424
+ if prev
1425
1425
  case tokens[prev].lexeme
1426
1426
  when "let" then kind = :let
1427
1427
  when "var" then kind = :var
@@ -961,7 +961,7 @@ module MilkTea
961
961
  resolve_receiver_value_type(facts, receiver_tok).then do |receiver_type|
962
962
  next false unless receiver_type
963
963
 
964
- field_receiver_type = project_field_receiver_type_for_completion(receiver_type)
964
+ field_receiver_type = project_field_receiver_type_for_completion(receiver_type, facts)
965
965
  next false unless field_receiver_type.respond_to?(:field)
966
966
 
967
967
  callable_semantic_type?(field_receiver_type.field(name))
@@ -158,9 +158,9 @@ module MilkTea
158
158
  ast = get_ast(uri)
159
159
  return @last_good_tooling_snapshot_cache[uri] if ast.nil?
160
160
 
161
- result = MilkTea::SemanticAnalyzer.check_collecting_errors(ast)
162
- facts = result[:analysis]
163
- MilkTea::SemanticAnalyzer::ToolingSnapshot.new(facts:, diagnostics: (Array(result[:errors]).map { |e| e.to_diagnostic(path: uri) } || []).freeze)
161
+ result = MilkTea::SemanticAnalyzer.check_collecting_errors(ast, path:)
162
+ facts = result[:analysis]
163
+ MilkTea::SemanticAnalyzer::ToolingSnapshot.new(facts:, diagnostics: (Array(result[:errors]).map { |e| e.to_diagnostic(path:) } || []).freeze)
164
164
  end
165
165
  if snapshot&.facts
166
166
  @last_good_tooling_snapshot_cache[uri] = snapshot
@@ -56,10 +56,12 @@ module MilkTea
56
56
  )
57
57
  collect_ms = elapsed_ms(collect_start) if collect_start
58
58
  diagnostics = result[:diagnostics]
59
+ cross_by_uri = result[:cross_file_diagnostics] || {}
59
60
  facts = result[:facts]
60
61
  snapshot = result[:sema_snapshot]
61
62
  @facts_cache_mutex.synchronize do
62
63
  if @facts_generation[uri] == generation
64
+ @cross_file_diagnostics = cross_by_uri
63
65
  @tooling_snapshot_cache[uri] = snapshot if snapshot
64
66
  @last_good_tooling_snapshot_cache[uri] = snapshot if snapshot&.facts
65
67
  @facts_cache[uri] = facts if facts
@@ -93,6 +95,10 @@ module MilkTea
93
95
  end
94
96
  end
95
97
  end
98
+
99
+ def cross_file_diagnostics
100
+ @facts_cache_mutex.synchronize { @cross_file_diagnostics }
101
+ end
96
102
  end
97
103
  end
98
104
  end
@@ -684,12 +684,17 @@ module MilkTea
684
684
  diagnostics = sort_by_location(diagnostics)
685
685
 
686
686
  if diagnostics.any? || closure_errors.any?
687
- source = read_source_file(path)
687
+ main_source = read_source_file(path)
688
+ main_abs = File.expand_path(path)
688
689
  diagnostics.each do |d|
690
+ same_file = !d.respond_to?(:path) || d.path.nil? || File.expand_path(d.path) == main_abs
691
+ source = same_file ? main_source : nil
689
692
  @err.puts(ErrorFormatter.format(d, source:, color: error_color?(@err)))
690
693
  end
691
694
  closure_errors.each do |d|
692
- @err.puts(ErrorFormatter.format(d, color: error_color?(@err)))
695
+ same_file = !d.respond_to?(:path) || d.path.nil? || File.expand_path(d.path) == main_abs
696
+ source = same_file ? main_source : nil
697
+ @err.puts(ErrorFormatter.format(d, source:, color: error_color?(@err)))
693
698
  end
694
699
  all_diagnostics.concat(diagnostics)
695
700
  all_diagnostics.concat(closure_errors)
@@ -711,9 +716,9 @@ module MilkTea
711
716
  parts << "#{info_count} #{info_count == 1 ? 'note' : 'notes'}" if info_count > 0
712
717
  body = parts.join("; ")
713
718
  if error_count > 0
714
- @err.puts("error: could not check due to #{body}")
719
+ @err.puts("#{body} found")
715
720
  elsif warning_count > 0
716
- @err.puts("warning: #{body}")
721
+ @err.puts("#{body}")
717
722
  end
718
723
  final_error_count = error_count + (resolution[:warnings_as_errors] ? warning_count : 0)
719
724
  final_error_count > 0 ? 1 : 0
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.6
4
+ version: 0.2.8
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.6 installed!
586
+ Milk Tea 0.2.8 installed!
587
587
 
588
588
  System requirements:
589
589
  - A C compiler (gcc or clang) must be available on PATH