mt-lang 0.3.37 → 0.3.39
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/docs/lsp-performance.md +347 -0
- data/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/core/c_backend/reinterpret.rb +2 -2
- data/lib/milk_tea/core/c_backend.rb +12 -0
- data/lib/milk_tea/core/compile_time.rb +46 -17
- data/lib/milk_tea/core/lexer.rb +12 -0
- data/lib/milk_tea/core/lowering/resolve.rb +2 -3
- 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.rb +12 -0
- 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/statements.rb +10 -10
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +9 -11
- 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/diagnostics.rb +15 -5
- data/lib/milk_tea/lsp/server/code_actions.rb +12 -2
- data/lib/milk_tea/lsp/server/completion.rb +100 -77
- data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +4 -6
- data/lib/milk_tea/lsp/server/formatting.rb +184 -60
- data/lib/milk_tea/lsp/server/hover.rb +321 -41
- data/lib/milk_tea/lsp/server/inlay_hints.rb +33 -11
- data/lib/milk_tea/lsp/server/lifecycle.rb +12 -0
- data/lib/milk_tea/lsp/server/references.rb +3 -1
- data/lib/milk_tea/lsp/server/selection_range.rb +4 -4
- data/lib/milk_tea/lsp/server/semantic_tokens.rb +63 -20
- data/lib/milk_tea/lsp/server/text_documents.rb +2 -2
- data/lib/milk_tea/lsp/server/type_hierarchy.rb +7 -7
- data/lib/milk_tea/lsp/server/utilities.rb +4 -0
- data/lib/milk_tea/lsp/server.rb +9 -0
- data/lib/milk_tea/lsp/workspace/analysis.rb +14 -3
- data/lib/milk_tea/lsp/workspace/caches.rb +17 -1
- data/lib/milk_tea/lsp/workspace/collection.rb +4 -0
- data/lib/milk_tea/lsp/workspace/module_index.rb +134 -0
- data/lib/milk_tea/lsp/workspace/store.rb +9 -4
- data/lib/milk_tea/lsp/workspace.rb +8 -0
- data/lib/milk_tea/packages/manifest.rb +9 -0
- metadata +5 -3
|
@@ -14,6 +14,18 @@ module MilkTea
|
|
|
14
14
|
def code
|
|
15
15
|
"module/error"
|
|
16
16
|
end
|
|
17
|
+
|
|
18
|
+
def to_diagnostic(path: nil)
|
|
19
|
+
Diagnostic.new(
|
|
20
|
+
path: @path || path,
|
|
21
|
+
line: @line,
|
|
22
|
+
column: @column,
|
|
23
|
+
length: nil,
|
|
24
|
+
code: code,
|
|
25
|
+
message: message,
|
|
26
|
+
severity: :error,
|
|
27
|
+
)
|
|
28
|
+
end
|
|
17
29
|
end
|
|
18
30
|
|
|
19
31
|
class ModuleLoader
|
|
@@ -69,11 +81,10 @@ module MilkTea
|
|
|
69
81
|
suffix_platform = platform_suffix_for_path(path)
|
|
70
82
|
return suffix_platform if suffix_platform
|
|
71
83
|
|
|
72
|
-
|
|
84
|
+
manifest = PackageManifest.load_option(path)
|
|
85
|
+
manifest_platform = manifest&.platform
|
|
73
86
|
return manifest_platform if manifest_platform
|
|
74
87
|
|
|
75
|
-
normalize_platform_name(host_platform || default_host_platform)
|
|
76
|
-
rescue PackageManifestError
|
|
77
88
|
normalize_platform_name(host_platform || default_host_platform)
|
|
78
89
|
end
|
|
79
90
|
|
|
@@ -167,7 +178,10 @@ module MilkTea
|
|
|
167
178
|
|
|
168
179
|
def check_program(path)
|
|
169
180
|
with_check_context(path) do |root_path|
|
|
170
|
-
|
|
181
|
+
errors = []
|
|
182
|
+
check_program_parallel(root_path, collecting_errors: errors)
|
|
183
|
+
raise errors.first if errors.any?
|
|
184
|
+
|
|
171
185
|
build_program(root_path)
|
|
172
186
|
end
|
|
173
187
|
end
|
|
@@ -313,7 +327,7 @@ module MilkTea
|
|
|
313
327
|
check_path(resolved_path)
|
|
314
328
|
rescue SemanticError => e
|
|
315
329
|
@analysis_cache[resolved_path] = previous if previous
|
|
316
|
-
collecting_errors << e
|
|
330
|
+
collecting_errors << e
|
|
317
331
|
end
|
|
318
332
|
@forward_bindings.clear
|
|
319
333
|
|
|
@@ -322,13 +336,14 @@ module MilkTea
|
|
|
322
336
|
# are not themselves part of a cycle; they just depended on cycle members.
|
|
323
337
|
(levels + [tail_members]).each do |level_paths|
|
|
324
338
|
if level_paths.length == 1
|
|
325
|
-
|
|
339
|
+
begin
|
|
340
|
+
check_path(level_paths.first)
|
|
341
|
+
rescue SemanticError => e
|
|
342
|
+
collecting_errors << e
|
|
343
|
+
end
|
|
326
344
|
elsif level_paths.any?
|
|
327
|
-
check_level_parallel(level_paths)
|
|
345
|
+
collecting_errors.concat(check_level_parallel(level_paths))
|
|
328
346
|
end
|
|
329
|
-
rescue SemanticError => e
|
|
330
|
-
raise unless collecting_errors
|
|
331
|
-
collecting_errors << e
|
|
332
347
|
end
|
|
333
348
|
end
|
|
334
349
|
|
|
@@ -369,28 +384,31 @@ module MilkTea
|
|
|
369
384
|
levels
|
|
370
385
|
end
|
|
371
386
|
|
|
387
|
+
# Checks a level of independent modules on worker threads. Each worker
|
|
388
|
+
# returns { ok: analysis } or { error: e } as an ordinary value; the join
|
|
389
|
+
# point collects the failed outcomes instead of re-raising them, so the
|
|
390
|
+
# caller decides whether to propagate or accumulate.
|
|
372
391
|
def check_level_parallel(paths)
|
|
373
|
-
|
|
392
|
+
workers = paths.map do |resolved_path|
|
|
374
393
|
Thread.new do
|
|
375
|
-
Thread.current[:resolved_path] = resolved_path
|
|
376
394
|
begin
|
|
377
|
-
|
|
378
|
-
Thread.current[:analysis] = analysis
|
|
395
|
+
{ ok: check_path(resolved_path) }
|
|
379
396
|
rescue ModuleLoadError, PackageLockError, SemanticError => e
|
|
380
|
-
|
|
397
|
+
{ error: e }
|
|
381
398
|
end
|
|
382
399
|
end
|
|
383
400
|
end
|
|
384
401
|
|
|
385
|
-
|
|
402
|
+
workers.each(&:join)
|
|
386
403
|
|
|
387
|
-
|
|
388
|
-
raise t[:error] if t[:error]
|
|
389
|
-
end
|
|
404
|
+
workers.map(&:value).filter_map { |result| result[:error] }
|
|
390
405
|
end
|
|
391
406
|
|
|
392
407
|
def imported_modules_for_ast(ast, importer_path: nil)
|
|
393
|
-
resolve_imports_for_ast(ast, importer_path:, collecting: false)
|
|
408
|
+
result = resolve_imports_for_ast(ast, importer_path:, collecting: false)
|
|
409
|
+
raise result.errors.first.error if result.errors.any?
|
|
410
|
+
|
|
411
|
+
result.modules
|
|
394
412
|
end
|
|
395
413
|
|
|
396
414
|
def imported_modules_for_ast_collecting_errors(ast, importer_path: nil)
|
|
@@ -421,29 +439,27 @@ module MilkTea
|
|
|
421
439
|
modules[import.path.to_s] = @binder.module_binding(import_analysis)
|
|
422
440
|
end
|
|
423
441
|
rescue ModuleLoadError, PackageLockError, SemanticError => e
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
442
|
+
if collecting
|
|
443
|
+
handle_circular_import_in_collecting_mode(import, import_path, modules, errors, e)
|
|
444
|
+
else
|
|
445
|
+
errors << ImportResolutionError.new(import:, error: e)
|
|
446
|
+
end
|
|
427
447
|
end
|
|
428
448
|
end
|
|
429
449
|
|
|
430
450
|
begin
|
|
431
451
|
@async_runtime_installer.install_async_runtime_dependency!(ast, modules, importer_path:, collecting_errors: collecting)
|
|
432
452
|
rescue ModuleLoadError, PackageLockError => e
|
|
433
|
-
raise unless collecting
|
|
434
453
|
errors << ImportResolutionError.new(import: nil, error: e)
|
|
435
454
|
end
|
|
436
455
|
|
|
437
456
|
begin
|
|
438
457
|
@prelude_installer.install_prelude_modules!(ast, modules, importer_path:, collecting_errors: collecting)
|
|
439
458
|
rescue ModuleLoadError, PackageLockError => e
|
|
440
|
-
raise unless collecting
|
|
441
459
|
errors << ImportResolutionError.new(import: nil, error: e)
|
|
442
460
|
end
|
|
443
461
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
modules.freeze
|
|
462
|
+
ImportResolution.new(modules: modules.freeze, errors: errors.freeze)
|
|
447
463
|
ensure
|
|
448
464
|
@import_resolve_depth -= 1 if @import_resolve_depth
|
|
449
465
|
end
|
|
@@ -528,7 +544,7 @@ module MilkTea
|
|
|
528
544
|
if use_shared_cache?
|
|
529
545
|
entry = @shared_cache[resolved_path]
|
|
530
546
|
if entry
|
|
531
|
-
mtime =
|
|
547
|
+
mtime = source_mtime(resolved_path)
|
|
532
548
|
if mtime && entry[:mtime] == mtime
|
|
533
549
|
@analysis_cache[resolved_path] = entry[:analysis]
|
|
534
550
|
return [resolved_path, nil, entry[:analysis]]
|
|
@@ -540,10 +556,19 @@ module MilkTea
|
|
|
540
556
|
[resolved_path, ast, nil]
|
|
541
557
|
end
|
|
542
558
|
|
|
559
|
+
# Option-style source modification time: nil when the file's mtime cannot
|
|
560
|
+
# be read (e.g. the file was removed between resolution and stat), so
|
|
561
|
+
# shared-cache reads simply miss instead of raising.
|
|
562
|
+
def source_mtime(path)
|
|
563
|
+
File.mtime(path).to_f
|
|
564
|
+
rescue SystemCallError
|
|
565
|
+
nil
|
|
566
|
+
end
|
|
567
|
+
|
|
543
568
|
def update_shared_cache(resolved_path, analysis)
|
|
544
569
|
return unless use_shared_cache?
|
|
545
570
|
|
|
546
|
-
mtime =
|
|
571
|
+
mtime = source_mtime(resolved_path)
|
|
547
572
|
@shared_cache[resolved_path] = { mtime:, analysis: } if mtime
|
|
548
573
|
end
|
|
549
574
|
|
|
@@ -575,11 +600,7 @@ module MilkTea
|
|
|
575
600
|
end
|
|
576
601
|
|
|
577
602
|
def inferred_module_name_for_path(path)
|
|
578
|
-
manifest =
|
|
579
|
-
PackageManifest.load(path)
|
|
580
|
-
rescue PackageManifestError
|
|
581
|
-
nil
|
|
582
|
-
end
|
|
603
|
+
manifest = PackageManifest.load_option(path)
|
|
583
604
|
|
|
584
605
|
if manifest && path_within_root?(path, manifest.source_root)
|
|
585
606
|
return module_name_for_path(path, manifest.source_root)
|
|
@@ -632,8 +653,9 @@ module MilkTea
|
|
|
632
653
|
import_result = resolve_imports_for_ast(ast, importer_path: resolved_path, collecting: true)
|
|
633
654
|
result = SemanticAnalyzer.check_collecting_errors(ast, imported_modules: import_result.modules, path: resolved_path)
|
|
634
655
|
@analysis_cache[resolved_path] = result[:analysis] if result[:analysis]
|
|
635
|
-
rescue
|
|
636
|
-
# Analysis capture is best-effort
|
|
656
|
+
rescue ModuleLoadError, PackageLockError, SemanticError
|
|
657
|
+
# Analysis capture is best-effort; named load/check failures leave the
|
|
658
|
+
# cycle member to the pass-2 re-check instead.
|
|
637
659
|
ensure
|
|
638
660
|
@checking_paths.pop
|
|
639
661
|
end
|
data/lib/milk_tea/core/parser.rb
CHANGED
|
@@ -31,6 +31,18 @@ module MilkTea
|
|
|
31
31
|
def code
|
|
32
32
|
"parse/error"
|
|
33
33
|
end
|
|
34
|
+
|
|
35
|
+
def to_diagnostic(path: nil)
|
|
36
|
+
Diagnostic.new(
|
|
37
|
+
path: @path || path,
|
|
38
|
+
line: line,
|
|
39
|
+
column: column,
|
|
40
|
+
length: nil,
|
|
41
|
+
code: code,
|
|
42
|
+
message: message,
|
|
43
|
+
severity: :error,
|
|
44
|
+
)
|
|
45
|
+
end
|
|
34
46
|
end
|
|
35
47
|
|
|
36
48
|
class SyntaxTokenStream
|
|
@@ -9,7 +9,8 @@ module MilkTea
|
|
|
9
9
|
case decl
|
|
10
10
|
when AST::StructDecl
|
|
11
11
|
packed, alignment = check_decl_attribute_applications!(decl.attributes, target_kind: :struct, target_label: "struct #{decl.name}", target_node: decl)
|
|
12
|
-
@ctx.types
|
|
12
|
+
struct_type = @ctx.types[decl.name]
|
|
13
|
+
struct_type.set_layout(packed:, alignment:) if struct_type
|
|
13
14
|
|
|
14
15
|
decl.fields.each do |field|
|
|
15
16
|
with_error_node(field) do
|
|
@@ -249,32 +249,20 @@ module MilkTea
|
|
|
249
249
|
end
|
|
250
250
|
end
|
|
251
251
|
|
|
252
|
-
def check_functions
|
|
253
|
-
@ctx.top_level_functions.each_value do |binding|
|
|
254
|
-
check_function(binding)
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
@ctx.methods.each_value do |method_map|
|
|
258
|
-
method_map.each_value do |binding|
|
|
259
|
-
check_function(binding)
|
|
260
|
-
end
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
|
|
264
252
|
# Validates the body of a specialized (instantiated) function or method
|
|
265
|
-
# binding. The owner checker
|
|
266
|
-
#
|
|
267
|
-
#
|
|
268
|
-
# the SemanticError directly.
|
|
253
|
+
# binding. The owner checker always accumulates body errors into its
|
|
254
|
+
# structural buffer, so this pulls out the new errors and surfaces the
|
|
255
|
+
# first non-tolerated one directly; tolerated ones are dropped.
|
|
269
256
|
def validate_specialized_function_body!(binding)
|
|
270
257
|
owner = binding.owner
|
|
271
|
-
|
|
272
|
-
|
|
258
|
+
structural_errors = owner.instance_variable_get(:@structural_errors)
|
|
259
|
+
prev_count = structural_errors.length
|
|
273
260
|
owner.check_function(binding)
|
|
261
|
+
new_errors = structural_errors[prev_count..].to_a
|
|
262
|
+
structural_errors.slice!(prev_count..) unless new_errors.empty?
|
|
263
|
+
new_errors.each { |error| raise error unless error.message.include?("cannot assign through immutable") }
|
|
274
264
|
rescue SemanticError => e
|
|
275
265
|
raise unless e.message.include?("cannot assign through immutable")
|
|
276
|
-
ensure
|
|
277
|
-
owner.instance_variable_set(:@collecting_errors, prev_collecting) if owner
|
|
278
266
|
end
|
|
279
267
|
|
|
280
268
|
# Per-function error collection used by check_collecting_errors.
|
|
@@ -9,7 +9,8 @@ module MilkTea
|
|
|
9
9
|
next unless decl.is_a?(AST::StructDecl) || decl.is_a?(AST::OpaqueDecl)
|
|
10
10
|
next if decl.implements.empty?
|
|
11
11
|
|
|
12
|
-
receiver_type = @ctx.types
|
|
12
|
+
receiver_type = @ctx.types[decl.name]
|
|
13
|
+
next unless receiver_type
|
|
13
14
|
resolved_interfaces = []
|
|
14
15
|
seen = {}
|
|
15
16
|
|
|
@@ -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
|
|
|
@@ -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
|
|
@@ -31,8 +31,10 @@ module MilkTea
|
|
|
31
31
|
# Parse
|
|
32
32
|
begin
|
|
33
33
|
parse_start = total_start ? monotonic_time : nil
|
|
34
|
+
parse_errors = []
|
|
34
35
|
ast = if path && File.file?(path)
|
|
35
36
|
parse_result = Parser.parse_collecting_errors(content, path: uri)
|
|
37
|
+
parse_errors = parse_result.errors
|
|
36
38
|
parse_result.errors.each { |error| diagnostics << format_error(error) }
|
|
37
39
|
parse_result.ast
|
|
38
40
|
else
|
|
@@ -63,6 +65,10 @@ module MilkTea
|
|
|
63
65
|
source_overrides: source_overrides,
|
|
64
66
|
workspace_root_path: workspace_root_path,
|
|
65
67
|
content: content,
|
|
68
|
+
# A file whose own parse recovered with errors can poison the loader's
|
|
69
|
+
# program-check cache and yield no facts on the standard path; skip the
|
|
70
|
+
# program check and resolve imports directly for those files.
|
|
71
|
+
skip_program_check: !parse_errors.empty?,
|
|
66
72
|
)
|
|
67
73
|
imports_ms = elapsed_ms(imports_start) if imports_start
|
|
68
74
|
unresolved_import_paths = imported_modules.fetch(:unresolved_import_paths)
|
|
@@ -167,7 +173,7 @@ module MilkTea
|
|
|
167
173
|
|
|
168
174
|
private
|
|
169
175
|
|
|
170
|
-
def self.resolve_imported_modules(uri, ast, diagnostics, resolution:, effective_platform:, shared_module_cache: nil, source_overrides: nil, workspace_root_path: nil, content: nil)
|
|
176
|
+
def self.resolve_imported_modules(uri, ast, diagnostics, resolution:, effective_platform:, shared_module_cache: nil, source_overrides: nil, workspace_root_path: nil, content: nil, skip_program_check: false)
|
|
171
177
|
path = uri_to_path(uri)
|
|
172
178
|
return { modules: {}, unresolved_import_paths: [], module_name: nil } unless path && File.file?(path)
|
|
173
179
|
|
|
@@ -186,10 +192,14 @@ module MilkTea
|
|
|
186
192
|
# cached analysis and resolves correctly. If the program check fails
|
|
187
193
|
# (e.g. missing module), fall through to the standard resolution
|
|
188
194
|
# path so that prelude modules and regular errors are still reported.
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
195
|
+
# Skipped when the file's own parse recovered with errors: the program
|
|
196
|
+
# check poisons the loader cache and yields no facts on that path.
|
|
197
|
+
unless skip_program_check
|
|
198
|
+
begin
|
|
199
|
+
loader.check_program_collecting(path)
|
|
200
|
+
rescue ModuleLoadError, PackageLockError
|
|
201
|
+
# best-effort — standard path below handles diagnostics
|
|
202
|
+
end
|
|
193
203
|
end
|
|
194
204
|
|
|
195
205
|
resolution_result = loader.imported_modules_for_ast_collecting_errors(ast, importer_path: path)
|
|
@@ -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
|