steep 2.1.0.dev.1 → 2.1.0

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.
@@ -1,13 +1,14 @@
1
1
  module Steep
2
2
  module Server
3
3
  class TypeCheckWorker < BaseWorker
4
- attr_reader :project, :assignment, :service
4
+ attr_reader :project, :assignment
5
5
  attr_reader :commandline_args
6
6
  attr_reader :current_type_check_guid
7
7
 
8
8
  WorkspaceSymbolJob = _ = Struct.new(:query, :id, keyword_init: true)
9
9
  StatsJob = _ = Struct.new(:id, keyword_init: true)
10
10
  QueryDefinitionJob = _ = Struct.new(:id, :name, keyword_init: true)
11
+ QueryDiagnosticsJob = _ = Struct.new(:id, keyword_init: true)
11
12
  StartTypeCheckJob = _ = Struct.new(:guid, :changes, keyword_init: true)
12
13
  TypeCheckCodeJob = _ = Struct.new(:guid, :path, :target, keyword_init: true)
13
14
  ValidateAppSignatureJob = _ = Struct.new(:guid, :path, :target, keyword_init: true)
@@ -68,7 +69,7 @@ module Steep
68
69
  @io_socket = io_socket
69
70
  @service = service if service
70
71
  @child_pids = []
71
- @need_to_warmup = defined?(Process.warmup)
72
+ @need_to_warmup = true
72
73
 
73
74
  if io_socket
74
75
  Signal.trap "SIGCHLD" do
@@ -112,6 +113,8 @@ module Steep
112
113
  when CustomMethods::Query__Definition::METHOD
113
114
  params = request[:params] #: CustomMethods::Query__Definition::params
114
115
  queue << QueryDefinitionJob.new(id: request[:id], name: params[:name])
116
+ when CustomMethods::Query__Diagnostics::METHOD
117
+ queue << QueryDiagnosticsJob.new(id: request[:id])
115
118
  when "textDocument/definition"
116
119
  queue << GotoJob.definition(id: request[:id], params: request[:params])
117
120
  when "textDocument/implementation"
@@ -307,6 +310,10 @@ module Steep
307
310
  writer.write(
308
311
  CustomMethods::Query__Definition.response(job.id, query_definition_result(job.name))
309
312
  )
313
+ when QueryDiagnosticsJob
314
+ writer.write(
315
+ CustomMethods::Query__Diagnostics.response(job.id, query_diagnostics_result())
316
+ )
310
317
  end
311
318
  end
312
319
 
@@ -358,6 +365,45 @@ module Steep
358
365
  end
359
366
  end
360
367
 
368
+ # Returns the diagnostics of the files this worker has type checked so far
369
+ #
370
+ # An array of `Query__Diagnostics::entry`, with the LSP-formatted diagnostics per file URI.
371
+ # Files that are loaded but not type checked yet are not included.
372
+ #
373
+ def query_diagnostics_result
374
+ result = {} #: Hash[String, Array[untyped]]
375
+
376
+ service.source_files.each_value do |file|
377
+ next if file.typing.nil? && file.errors.nil?
378
+
379
+ absolute_path = project.absolute_path(file.path)
380
+
381
+ group_target =
382
+ project.group_for_source_path(absolute_path) ||
383
+ project.group_for_inline_source_path(absolute_path) ||
384
+ project.target_for_source_path(absolute_path) ||
385
+ project.target_for_inline_source_path(absolute_path)
386
+ next unless group_target
387
+
388
+ formatter = Diagnostic::LSPFormatter.new(group_target.code_diagnostics_config)
389
+ uri = PathHelper.to_uri(absolute_path).to_s
390
+ array = result[uri] ||= []
391
+ array.concat(file.diagnostics.filter_map { formatter.format(_1) })
392
+ end
393
+
394
+ formatter = Diagnostic::LSPFormatter.new({}, **{})
395
+ service.signature_validation_diagnostics.each_value do |path_diagnostics|
396
+ path_diagnostics.each do |path, diagnostics|
397
+ absolute_path = path.absolute? ? path : project.absolute_path(path)
398
+ uri = PathHelper.to_uri(absolute_path).to_s
399
+ array = result[uri] ||= []
400
+ array.concat(diagnostics.filter_map { formatter.format(_1) })
401
+ end
402
+ end
403
+
404
+ result.map { { uri: _1, diagnostics: _2 } }
405
+ end
406
+
361
407
  def query_definition_result(name_string)
362
408
  name = Services::GotoService.parse_name(name_string)
363
409
 
@@ -524,7 +524,9 @@ module Steep
524
524
  def constant_definition_in_ruby(name, locations:)
525
525
  type_check.source_files.each do |path, source|
526
526
  if typing = source.typing
527
- target = project.target_for_source_path(path) or raise
527
+ # Inline sources are type checked too, but they don't have a Ruby definition to go to.
528
+ # Their declarations are found through the RBS lookup instead.
529
+ target = project.target_for_source_path(path) or next
528
530
  entry = typing.source_index.entry(constant: name)
529
531
  entry.definitions.each do |node|
530
532
  case node.type
@@ -551,7 +553,9 @@ module Steep
551
553
  if in_ruby
552
554
  type_check.source_files.each do |path, source|
553
555
  if typing = source.typing
554
- target = project.target_for_source_path(path) or raise
556
+ # Inline sources are type checked too, but they don't have a Ruby definition to go to.
557
+ # Their declarations are found through the RBS lookup instead.
558
+ target = project.target_for_source_path(path) or next
555
559
  entry = typing.source_index.entry(method: name)
556
560
 
557
561
  if entry.definitions.empty?
@@ -56,7 +56,7 @@ module Steep
56
56
 
57
57
  def class_or_module?
58
58
  (class_decl || class_alias) ? true : false
59
- end
59
+ end
60
60
  end
61
61
 
62
62
  TypeAliasContent = _ = Struct.new(:location, :decl, keyword_init: true)
data/lib/steep/source.rb CHANGED
@@ -231,7 +231,7 @@ module Steep
231
231
  end
232
232
 
233
233
  when :rescue
234
- body, resbodies, else_node, loc = deconstruct_rescue_node!(node)
234
+ body, _, else_node, loc = deconstruct_rescue_node!(node)
235
235
 
236
236
  if else_node
237
237
  loc.else or raise
@@ -356,7 +356,6 @@ module Steep
356
356
 
357
357
  def find_heredoc_nodes(line, column, position)
358
358
  each_heredoc_node() do |nodes, location|
359
- node = nodes[0]
360
359
  loc = location.heredoc_body #: Parser::Source::Range
361
360
 
362
361
  if range = loc.to_range
@@ -470,7 +469,7 @@ module Steep
470
469
  return false unless send_node
471
470
 
472
471
  if send_node.type == :send
473
- receiver, method, args = deconstruct_send_node!(send_node)
472
+ receiver, method, _ = deconstruct_send_node!(send_node)
474
473
 
475
474
  return false unless receiver
476
475
 
@@ -657,7 +656,7 @@ module Steep
657
656
  end
658
657
 
659
658
  if send_node
660
- receiver_node, name, _, location = deconstruct_send_node!(send_node)
659
+ receiver_node, _, _, location = deconstruct_send_node!(send_node)
661
660
 
662
661
  if receiver_node
663
662
  if location.dot && location.selector
@@ -189,7 +189,7 @@ module Steep
189
189
 
190
190
  relation.type!
191
191
 
192
- Steep.logger.tagged "#{relation.sub_type} <: #{relation.super_type}" do
192
+ Steep.logger.tagged(-> { "#{relation.sub_type} <: #{relation.super_type}" }) do
193
193
  bounds = cache_bounds(relation)
194
194
  fvs = relation.sub_type.free_variables + relation.super_type.free_variables
195
195
  cached = cache[relation, @self_type, @instance_type, @class_type, bounds]
@@ -201,7 +201,7 @@ module Steep
201
201
  else
202
202
  push_assumption(relation) do
203
203
  check_type0(relation).tap do |result|
204
- Steep.logger.debug "result=#{result.class}"
204
+ Steep.logger.debug { "result=#{result.class}" }
205
205
  cache[relation, @self_type, @instance_type, @class_type, bounds] = result
206
206
  end
207
207
  end
@@ -644,6 +644,9 @@ module Steep
644
644
 
645
645
  All(relation) do |result|
646
646
  sub_args.zip(sup_args, sup_params.each).each do |sub_arg, sup_arg, sup_param|
647
+ sup_arg or raise
648
+ sup_param or raise
649
+
647
650
  case sup_param.variance
648
651
  when :covariant
649
652
  result.add(Relation.new(sub_type: sub_arg, super_type: sup_arg)) do |rel|
@@ -854,7 +857,7 @@ module Steep
854
857
  end
855
858
 
856
859
  def check_method_type(name, relation)
857
- Steep.logger.tagged "#{name} : #{relation.sub_type} <: #{relation.super_type}" do
860
+ Steep.logger.tagged(-> { "#{name} : #{relation.sub_type} <: #{relation.super_type}" }) do
858
861
  relation.method!
859
862
 
860
863
  sub_type, super_type = relation
@@ -33,7 +33,10 @@ module Steep
33
33
  end
34
34
 
35
35
  def formatted_tags
36
- current_tags.map { |tag| "[#{tag}]" }.join(" ")
36
+ current_tags.map do |tag|
37
+ tag = tag.call if tag.is_a?(Proc)
38
+ "[#{tag}]"
39
+ end.join(" ")
37
40
  end
38
41
  end
39
42
  end
@@ -653,7 +653,6 @@ module Steep
653
653
  instance_definition: instance_definition
654
654
  )
655
655
 
656
- singleton_definition = checker.factory.definition_builder.build_singleton(module_context.class_name)
657
656
  type_env =
658
657
  TypeInference::TypeEnvBuilder.new(
659
658
  TypeInference::TypeEnvBuilder::Command::ImportGlobalDeclarations.new(checker.factory),
@@ -730,7 +729,7 @@ module Steep
730
729
  end
731
730
 
732
731
  def synthesize(node, hint: nil, condition: false)
733
- Steep.logger.tagged "synthesize:(#{node.location&.yield_self {|loc| loc.expression.to_s.split(/:/, 2).last } || "-"})" do
732
+ Steep.logger.tagged(-> { "synthesize:(#{node.location&.yield_self {|loc| loc.expression.to_s.split(/:/, 2).last } || "-"})" }) do
734
733
  Steep.logger.debug node.type
735
734
  case node.type
736
735
  when :begin, :kwbegin
@@ -781,7 +780,7 @@ module Steep
781
780
  end
782
781
 
783
782
  if rhs
784
- rhs_type, rhs_constr, rhs_context = synthesize(rhs, hint: hint).to_ary
783
+ rhs_type, rhs_constr, _ = synthesize(rhs, hint: hint).to_ary
785
784
 
786
785
  constr = rhs_constr.update_type_env do |type_env|
787
786
  var_type = rhs_type
@@ -1761,6 +1760,11 @@ module Steep
1761
1760
  else
1762
1761
  if hint
1763
1762
  tuples = select_flatten_types(hint) {|type| type.is_a?(AST::Types::Tuple) } #: Array[AST::Types::Tuple]
1763
+ if tuples.empty?
1764
+ if converted = try_convert(hint, :to_ary)
1765
+ tuples = select_flatten_types(converted) {|type| type.is_a?(AST::Types::Tuple) } #: Array[AST::Types::Tuple]
1766
+ end
1767
+ end
1764
1768
  unless tuples.empty?
1765
1769
  fallback_pair = nil #: Pair?
1766
1770
  tuples.each do |tuple|
@@ -1913,7 +1917,7 @@ module Steep
1913
1917
  yield_self do
1914
1918
  cond, true_clause, false_clause = node.children
1915
1919
 
1916
- cond_type, constr = synthesize(cond, condition: true).to_ary
1920
+ _, constr = synthesize(cond, condition: true).to_ary
1917
1921
  interpreter = TypeInference::LogicTypeInterpreter.new(subtyping: checker, typing: constr.typing, config: builder_config)
1918
1922
  truthy, falsy = interpreter.eval(env: constr.context.type_env, node: cond)
1919
1923
 
@@ -2045,7 +2049,7 @@ module Steep
2045
2049
  branch_reachable = false
2046
2050
 
2047
2051
  tests.each do |test|
2048
- test_type, condition_constr = condition_constr.synthesize(test, condition: true)
2052
+ _, condition_constr = condition_constr.synthesize(test, condition: true)
2049
2053
  truthy, falsy = interpreter.eval(env: condition_constr.context.type_env, node: test)
2050
2054
  truthy_env = truthy.env
2051
2055
  falsy_env = falsy.env
@@ -2295,7 +2299,7 @@ module Steep
2295
2299
  when :while, :until
2296
2300
  yield_self do
2297
2301
  cond, body = node.children
2298
- cond_type, constr = synthesize(cond, condition: true).to_ary
2302
+ _, constr = synthesize(cond, condition: true).to_ary
2299
2303
 
2300
2304
  interpreter = TypeInference::LogicTypeInterpreter.new(subtyping: checker, typing: typing, config: builder_config)
2301
2305
  truthy, falsy = interpreter.eval(env: constr.context.type_env, node: cond)
@@ -2347,7 +2351,7 @@ module Steep
2347
2351
  .for_branch(body, break_context: TypeInference::Context::BreakContext.new(break_type: hint || AST::Builtin.nil_type, next_type: nil))
2348
2352
 
2349
2353
  typing.cursor_context.set_node_context(body, for_loop.context)
2350
- _, body_constr, body_context = for_loop.synthesize(body)
2354
+ _, _, body_context = for_loop.synthesize(body)
2351
2355
 
2352
2356
  constr = cond_constr.update_type_env {|env| env.join(env, body_context.type_env) }
2353
2357
 
@@ -2886,10 +2890,8 @@ module Steep
2886
2890
  if node.type == :splat
2887
2891
  asgn_node = node.children[0]
2888
2892
  next unless asgn_node
2889
- var_type = asgn_node.type
2890
2893
  else
2891
2894
  asgn_node = node
2892
- var_type = type
2893
2895
  end
2894
2896
 
2895
2897
  case asgn_node.type
@@ -3164,11 +3166,14 @@ module Steep
3164
3166
  block =
3165
3167
  if block_param = params.block_param
3166
3168
  if block_param_type = block_param.type
3167
- case block_param_type
3169
+ # Expanding aliases because the cases below test the structure of the type
3170
+ expanded_type = deep_expand_alias(block_param_type) || block_param_type
3171
+
3172
+ case expanded_type
3168
3173
  when AST::Types::Proc
3169
- Interface::Block.new(type: block_param_type.type, optional: false, self_type: block_param_type.self_type)
3174
+ Interface::Block.new(type: expanded_type.type, optional: false, self_type: expanded_type.self_type)
3170
3175
  else
3171
- if proc_type = optional_proc?(block_param_type)
3176
+ if proc_type = optional_proc?(expanded_type)
3172
3177
  Interface::Block.new(type: proc_type.type, optional: true, self_type: proc_type.self_type)
3173
3178
  else
3174
3179
  block_constr.typing.add_error(
@@ -4358,7 +4363,9 @@ module Steep
4358
4363
  when arg.compatible?
4359
4364
  if arg.node
4360
4365
  # Block pass (&block) is given
4361
- node_type, constr = constr.synthesize(arg.node, hint: arg.node_type)
4366
+ # Passing `proc_type` instead of `node_type` because a block-pass argument is a proc, not `nil`.
4367
+ # `nil` is still allowed by the `node_type` check below.
4368
+ node_type, constr = constr.synthesize(arg.node, hint: arg.proc_type)
4362
4369
 
4363
4370
  nil_given =
4364
4371
  constr.check_relation(sub_type: node_type, super_type: AST::Builtin.nil_type).success? &&
@@ -20,14 +20,14 @@ module Steep
20
20
 
21
21
  latest_constr, unreachable_pattern = latest_result
22
22
 
23
- type, constr = yield(test_node, latest_constr, unreachable_pattern)
23
+ yield(test_node, latest_constr, unreachable_pattern)
24
24
  truthy_result, falsy_result = logic.eval(env: latest_constr.context.type_env, node: test_node)
25
25
 
26
26
  pattern_results << [pat, truthy_result, falsy_result]
27
27
  end
28
28
 
29
29
  def latest_result
30
- if (_, truthy, falsy = pattern_results.last)
30
+ if (_, _, falsy = pattern_results.last)
31
31
  [
32
32
  initial_constr.update_type_env { falsy.env },
33
33
  falsy.unreachable
@@ -473,16 +473,20 @@ module Steep
473
473
  end
474
474
  end
475
475
 
476
- def node_type
476
+ def proc_type
477
477
  raise unless block
478
478
 
479
- type = AST::Types::Proc.new(type: block.type, block: nil, self_type: block.self_type)
479
+ AST::Types::Proc.new(type: block.type, block: nil, self_type: block.self_type)
480
+ end
481
+
482
+ def node_type
483
+ raise unless block
480
484
 
481
485
  if block.optional?
482
- type = AST::Types::Union.build(types: [type, AST::Builtin.nil_type])
486
+ AST::Types::Union.build(types: [proc_type, AST::Builtin.nil_type])
487
+ else
488
+ proc_type
483
489
  end
484
-
485
- type
486
490
  end
487
491
  end
488
492
 
data/lib/steep/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Steep
2
- VERSION = "2.1.0.dev.1"
2
+ VERSION = "2.1.0"
3
3
  end
data/lib/steep.rb CHANGED
@@ -133,6 +133,7 @@ require "steep/server/target_group_files"
133
133
  require "steep/server/type_check_controller"
134
134
  require "steep/server/inline_source_change_detector"
135
135
  require "steep/server/master"
136
+ require "steep/server/command_socket"
136
137
  require "steep/daemon"
137
138
 
138
139
  require "steep/project"
data/steep.gemspec CHANGED
@@ -29,20 +29,14 @@ Gem::Specification.new do |spec|
29
29
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
30
  spec.require_paths = ["lib"]
31
31
 
32
- if false
33
- # This is to let dependabot use 3.3, instead of 3.1.x.
34
- # They use `required_ruby_version=` method to detect the Ruby version they use.
35
- spec.required_ruby_version = ">= 3.3.0"
36
- end
37
-
38
- spec.required_ruby_version = '>= 3.2.0'
32
+ spec.required_ruby_version = '>= 3.3.0'
39
33
 
40
34
  spec.add_runtime_dependency "parser", ">= 3.2"
41
35
  spec.add_runtime_dependency "prism", ">= 0.25.0"
42
36
  spec.add_runtime_dependency "rainbow", ">= 2.2.2", "< 4.0"
43
37
  spec.add_runtime_dependency "listen", "~> 3.0"
44
38
  spec.add_runtime_dependency "language_server-protocol", ">= 3.17.0.4", "< 4.0"
45
- spec.add_runtime_dependency "rbs", "~> 4.0"
39
+ spec.add_runtime_dependency "rbs", "~> 4.2"
46
40
  spec.add_runtime_dependency "concurrent-ruby", ">= 1.1.10"
47
41
  spec.add_runtime_dependency "terminal-table", ">= 2", "< 5"
48
42
  spec.add_runtime_dependency "securerandom", ">= 0.1"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: steep
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0.dev.1
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Soutaro Matsumoto
@@ -97,14 +97,14 @@ dependencies:
97
97
  requirements:
98
98
  - - "~>"
99
99
  - !ruby/object:Gem::Version
100
- version: '4.0'
100
+ version: '4.2'
101
101
  type: :runtime
102
102
  prerelease: false
103
103
  version_requirements: !ruby/object:Gem::Requirement
104
104
  requirements:
105
105
  - - "~>"
106
106
  - !ruby/object:Gem::Version
107
- version: '4.0'
107
+ version: '4.2'
108
108
  - !ruby/object:Gem::Dependency
109
109
  name: concurrent-ruby
110
110
  requirement: !ruby/object:Gem::Requirement
@@ -362,6 +362,7 @@ files:
362
362
  - lib/steep/range_extension.rb
363
363
  - lib/steep/server/base_worker.rb
364
364
  - lib/steep/server/change_buffer.rb
365
+ - lib/steep/server/command_socket.rb
365
366
  - lib/steep/server/custom_methods.rb
366
367
  - lib/steep/server/delay_queue.rb
367
368
  - lib/steep/server/inline_source_change_detector.rb
@@ -442,14 +443,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
442
443
  requirements:
443
444
  - - ">="
444
445
  - !ruby/object:Gem::Version
445
- version: 3.2.0
446
+ version: 3.3.0
446
447
  required_rubygems_version: !ruby/object:Gem::Requirement
447
448
  requirements:
448
449
  - - ">="
449
450
  - !ruby/object:Gem::Version
450
451
  version: '0'
451
452
  requirements: []
452
- rubygems_version: 4.0.18
453
+ rubygems_version: 4.0.19
453
454
  specification_version: 4
454
455
  summary: Gradual Typing for Ruby
455
456
  test_files: []