steep 1.8.0.dev.1 → 1.8.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -0
  3. data/README.md +3 -2
  4. data/bin/mem_graph.rb +67 -0
  5. data/bin/mem_prof.rb +102 -0
  6. data/bin/stackprof_test.rb +19 -0
  7. data/bin/steep-check.rb +251 -0
  8. data/guides/src/gem-rbs-collection/gem-rbs-collection.md +1 -1
  9. data/lib/steep/annotation_parser.rb +1 -1
  10. data/lib/steep/ast/builtin.rb +5 -5
  11. data/lib/steep/ast/node/type_application.rb +7 -6
  12. data/lib/steep/ast/types/any.rb +1 -9
  13. data/lib/steep/ast/types/boolean.rb +8 -16
  14. data/lib/steep/ast/types/bot.rb +2 -10
  15. data/lib/steep/ast/types/class.rb +1 -13
  16. data/lib/steep/ast/types/factory.rb +101 -85
  17. data/lib/steep/ast/types/instance.rb +1 -13
  18. data/lib/steep/ast/types/intersection.rb +8 -15
  19. data/lib/steep/ast/types/literal.rb +2 -8
  20. data/lib/steep/ast/types/logic.rb +3 -24
  21. data/lib/steep/ast/types/name.rb +5 -16
  22. data/lib/steep/ast/types/nil.rb +3 -12
  23. data/lib/steep/ast/types/proc.rb +4 -13
  24. data/lib/steep/ast/types/record.rb +21 -12
  25. data/lib/steep/ast/types/self.rb +1 -13
  26. data/lib/steep/ast/types/shared_instance.rb +11 -0
  27. data/lib/steep/ast/types/top.rb +1 -9
  28. data/lib/steep/ast/types/tuple.rb +4 -10
  29. data/lib/steep/ast/types/union.rb +10 -15
  30. data/lib/steep/ast/types/var.rb +4 -13
  31. data/lib/steep/ast/types/void.rb +2 -10
  32. data/lib/steep/diagnostic/ruby.rb +10 -10
  33. data/lib/steep/drivers/check.rb +11 -14
  34. data/lib/steep/drivers/checkfile.rb +8 -10
  35. data/lib/steep/drivers/stats.rb +17 -13
  36. data/lib/steep/drivers/utils/driver_helper.rb +24 -3
  37. data/lib/steep/drivers/watch.rb +3 -3
  38. data/lib/steep/interface/builder.rb +162 -138
  39. data/lib/steep/interface/method_type.rb +12 -20
  40. data/lib/steep/interface/shape.rb +66 -10
  41. data/lib/steep/interface/substitution.rb +2 -0
  42. data/lib/steep/interface/type_param.rb +20 -7
  43. data/lib/steep/located_value.rb +20 -0
  44. data/lib/steep/server/change_buffer.rb +5 -7
  45. data/lib/steep/server/custom_methods.rb +61 -0
  46. data/lib/steep/server/delay_queue.rb +8 -1
  47. data/lib/steep/server/interaction_worker.rb +13 -6
  48. data/lib/steep/server/lsp_formatter.rb +8 -6
  49. data/lib/steep/server/master.rb +195 -142
  50. data/lib/steep/server/type_check_worker.rb +25 -22
  51. data/lib/steep/server/work_done_progress.rb +64 -0
  52. data/lib/steep/server/worker_process.rb +1 -1
  53. data/lib/steep/services/completion_provider.rb +32 -24
  54. data/lib/steep/services/goto_service.rb +3 -2
  55. data/lib/steep/services/hover_provider/ruby.rb +30 -17
  56. data/lib/steep/services/signature_help_provider.rb +9 -7
  57. data/lib/steep/services/signature_service.rb +1 -1
  58. data/lib/steep/services/type_check_service.rb +19 -9
  59. data/lib/steep/signature/validator.rb +17 -20
  60. data/lib/steep/source.rb +47 -1
  61. data/lib/steep/subtyping/check.rb +105 -55
  62. data/lib/steep/subtyping/constraints.rb +13 -17
  63. data/lib/steep/type_construction.rb +106 -100
  64. data/lib/steep/type_inference/block_params.rb +8 -5
  65. data/lib/steep/type_inference/logic_type_interpreter.rb +11 -7
  66. data/lib/steep/type_inference/method_call.rb +3 -3
  67. data/lib/steep/type_inference/method_params.rb +1 -1
  68. data/lib/steep/type_inference/send_args.rb +1 -1
  69. data/lib/steep/typing.rb +164 -106
  70. data/lib/steep/version.rb +1 -1
  71. data/lib/steep.rb +29 -4
  72. data/steep.gemspec +2 -2
  73. metadata +16 -9
  74. data/lib/steep/type_inference/context_array.rb +0 -112
@@ -1,28 +1,83 @@
1
1
  module Steep
2
2
  module Interface
3
3
  class Shape
4
+ class MethodOverload
5
+ attr_reader :method_type
6
+
7
+ attr_reader :method_defs
8
+
9
+ def initialize(method_type, defs)
10
+ @method_type = method_type
11
+ @method_defs = defs.sort_by do |defn|
12
+ buf = +""
13
+
14
+ if loc = defn.type.location
15
+ buf << loc.buffer.name.to_s
16
+ buf << ":"
17
+ buf << loc.start_pos.to_s
18
+ end
19
+
20
+ buf
21
+ end
22
+ @method_defs.uniq!
23
+ end
24
+
25
+ def subst(s)
26
+ overload = MethodOverload.new(method_type.subst(s), [])
27
+ overload.method_defs.replace(method_defs)
28
+ overload
29
+ end
30
+
31
+ def method_decls(name)
32
+ method_defs.map do |defn|
33
+ type_name = defn.implemented_in || defn.defined_in
34
+
35
+ if name == :new && defn.member.is_a?(RBS::AST::Members::MethodDefinition) && defn.member.name == :initialize
36
+ method_name = SingletonMethodName.new(type_name: type_name, method_name: name)
37
+ else
38
+ method_name =
39
+ if defn.member.kind == :singleton
40
+ SingletonMethodName.new(type_name: defn.defined_in, method_name: name)
41
+ else
42
+ # Call the `self?` method an instance method, because the definition is done with instance method definition, not with singleton method
43
+ InstanceMethodName.new(type_name: defn.defined_in, method_name: name)
44
+ end
45
+ end
46
+
47
+ TypeInference::MethodCall::MethodDecl.new(method_def: defn, method_name: method_name)
48
+ end
49
+ end
50
+ end
51
+
4
52
  class Entry
5
- def initialize(method_types: nil, private_method:, &block)
6
- @method_types = method_types
53
+ attr_reader :method_name
54
+
55
+ def initialize(overloads: nil, private_method:, method_name:, &block)
56
+ @overloads = overloads
7
57
  @generator = block
8
58
  @private_method = private_method
59
+ @method_name = method_name
9
60
  end
10
61
 
11
62
  def force
12
- unless @method_types
13
- @method_types = @generator&.call
63
+ unless @overloads
64
+ @overloads = @generator&.call
14
65
  @generator = nil
15
66
  end
16
67
  end
17
68
 
18
- def method_types
69
+ def overloads
19
70
  force
20
- @method_types or raise
71
+ @overloads or raise
72
+ end
73
+
74
+ def method_types
75
+ overloads.map(&:method_type)
21
76
  end
22
77
 
23
78
  def has_method_type?
24
79
  force
25
- @method_types ? true : false
80
+ @overloads ? true : false
26
81
  end
27
82
 
28
83
  def to_s
@@ -50,7 +105,7 @@ module Steep
50
105
  def initialize(substs:, methods:)
51
106
  @substs = substs
52
107
  @methods = methods
53
- @resolved_methods = methods.transform_values { nil }
108
+ @resolved_methods = {}
54
109
  end
55
110
 
56
111
  def key?(name)
@@ -72,8 +127,9 @@ module Steep
72
127
  resolved_methods[name] ||= begin
73
128
  entry = methods[name]
74
129
  Entry.new(
75
- method_types: entry.method_types.map do |method_type|
76
- method_type.subst(subst)
130
+ method_name: name,
131
+ overloads: entry.overloads.map do |overload|
132
+ overload.subst(subst)
77
133
  end,
78
134
  private_method: entry.private_method?
79
135
  )
@@ -82,6 +82,8 @@ module Steep
82
82
  !instance_type.is_a?(AST::Types::Instance)
83
83
  when AST::Types::Class
84
84
  !module_type.is_a?(AST::Types::Class)
85
+ when AST::Types::Name::Applying
86
+ type.args.any? {|ty| apply?(ty) }
85
87
  else
86
88
  type.each_child.any? {|t| apply?(t) }
87
89
  end
@@ -6,13 +6,15 @@ module Steep
6
6
  attr_reader :variance
7
7
  attr_reader :unchecked
8
8
  attr_reader :location
9
+ attr_reader :default_type
9
10
 
10
- def initialize(name:, upper_bound:, variance:, unchecked:, location: nil)
11
+ def initialize(name:, upper_bound:, variance:, unchecked:, location: nil, default_type:)
11
12
  @name = name
12
13
  @upper_bound = upper_bound
13
14
  @variance = variance
14
15
  @unchecked = unchecked
15
16
  @location = location
17
+ @default_type = default_type
16
18
  end
17
19
 
18
20
  def ==(other)
@@ -20,13 +22,14 @@ module Steep
20
22
  other.name == name &&
21
23
  other.upper_bound == upper_bound &&
22
24
  other.variance == variance &&
23
- other.unchecked == unchecked
25
+ other.unchecked == unchecked &&
26
+ other.default_type == default_type
24
27
  end
25
28
 
26
29
  alias eql? ==
27
30
 
28
31
  def hash
29
- name.hash ^ upper_bound.hash ^ variance.hash ^ unchecked.hash
32
+ name.hash ^ upper_bound.hash ^ variance.hash ^ unchecked.hash ^ default_type.hash
30
33
  end
31
34
 
32
35
  def self.rename(params, conflicting_names = params.map(&:name), new_names = conflicting_names.map {|n| AST::Types::Var.fresh_name(n) })
@@ -44,7 +47,8 @@ module Steep
44
47
  upper_bound: param.upper_bound&.subst(subst),
45
48
  variance: param.variance,
46
49
  unchecked: param.unchecked,
47
- location: param.location
50
+ location: param.location,
51
+ default_type: param.default_type&.subst(subst)
48
52
  )
49
53
  else
50
54
  param
@@ -80,19 +84,28 @@ module Steep
80
84
  buf
81
85
  end
82
86
 
83
- def update(name: self.name, upper_bound: self.upper_bound, variance: self.variance, unchecked: self.unchecked, location: self.location)
87
+ def update(name: self.name, upper_bound: self.upper_bound, variance: self.variance, unchecked: self.unchecked, location: self.location, default_type: self.default_type)
84
88
  TypeParam.new(
85
89
  name: name,
86
90
  upper_bound: upper_bound,
87
91
  variance: variance,
88
92
  unchecked: unchecked,
89
- location: location
93
+ location: location,
94
+ default_type: default_type
90
95
  )
91
96
  end
92
97
 
93
98
  def subst(s)
94
99
  if u = upper_bound
95
- update(upper_bound: u.subst(s))
100
+ ub = u.subst(s)
101
+ end
102
+
103
+ if d = default_type
104
+ dt = d.subst(s)
105
+ end
106
+
107
+ if ub || dt
108
+ update(upper_bound: ub, default_type: dt)
96
109
  else
97
110
  self
98
111
  end
@@ -0,0 +1,20 @@
1
+ module Steep
2
+ class LocatedValue
3
+ attr_reader :value, :location
4
+
5
+ def initialize(value:, location:)
6
+ @value = value
7
+ @location = location
8
+ end
9
+
10
+ def ==(other)
11
+ other.is_a?(LocatedValue) && other.value == value
12
+ end
13
+
14
+ alias eql? ==
15
+
16
+ def hash
17
+ value.hash # steep:ignore NoMethod
18
+ end
19
+ end
20
+ end
@@ -24,16 +24,14 @@ module Steep
24
24
  end
25
25
  end
26
26
 
27
- def load_files(project:, commandline_args:)
27
+ def load_files(input)
28
28
  Steep.logger.tagged "#load_files" do
29
29
  push_buffer do |changes|
30
- loader = Services::FileLoader.new(base_dir: project.base_dir)
31
-
32
- Steep.measure "load changes from disk" do
33
- project.targets.each do |target|
34
- loader.load_changes(target.source_pattern, commandline_args, changes: changes)
35
- loader.load_changes(target.signature_pattern, changes: changes)
30
+ input.each do |filename, content|
31
+ if content.is_a?(Hash)
32
+ content = Base64.decode64(content[:text]).force_encoding(Encoding::UTF_8)
36
33
  end
34
+ changes[Pathname(filename.to_s)] = [Services::ContentChange.new(text: content)]
37
35
  end
38
36
  end
39
37
  end
@@ -0,0 +1,61 @@
1
+ module Steep
2
+ module Server
3
+ module CustomMethods
4
+ module FileLoad
5
+ METHOD = "$/steep/file/load"
6
+
7
+ def self.notification(params)
8
+ { method: METHOD, params: params }
9
+ end
10
+ end
11
+
12
+ module FileReset
13
+ METHOD = "$/steep/file/reset"
14
+
15
+ def self.notification(params)
16
+ { method: METHOD, params: params }
17
+ end
18
+ end
19
+
20
+ module TypeCheck
21
+ METHOD = "$/steep/typecheck"
22
+
23
+ def self.request(id, params)
24
+ { method: METHOD, id: id, params: params }
25
+ end
26
+
27
+ def self.response(id, result)
28
+ { id: id, result: result }
29
+ end
30
+ end
31
+
32
+ module TypeCheck__Start
33
+ METHOD = "$/steep/typecheck/start"
34
+
35
+ def self.notification(params)
36
+ { method: METHOD, params: params }
37
+ end
38
+ end
39
+
40
+ module TypeCheck__Progress
41
+ METHOD = "$/steep/typecheck/progress"
42
+
43
+ def self.notification(params)
44
+ { method: METHOD, params: params }
45
+ end
46
+ end
47
+
48
+ module Stats
49
+ METHOD = "$/steep/stats"
50
+
51
+ def self.request(id)
52
+ { method: METHOD, id: id, params: nil }
53
+ end
54
+
55
+ def self.response(id, result)
56
+ { id: id, result: result }
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -25,12 +25,19 @@ module Steep
25
25
  end
26
26
 
27
27
  if proc.equal?(last_task)
28
- proc[]
28
+ unless @cancelled
29
+ proc[]
30
+ end
29
31
  end
30
32
  end
31
33
  end
32
34
  end
33
35
 
36
+ def cancel
37
+ @cancelled = true
38
+ queue.clear()
39
+ end
40
+
34
41
  def execute(&block)
35
42
  @last_task = block
36
43
  scheduled_at = Time.now + delay
@@ -82,17 +82,22 @@ module Steep
82
82
  def handle_request(request)
83
83
  case request[:method]
84
84
  when "initialize"
85
- load_files(project: project, commandline_args: [])
86
- queue_job ApplyChangeJob.new
87
85
  writer.write({ id: request[:id], result: nil })
88
86
 
89
87
  when "textDocument/didChange"
90
88
  collect_changes(request)
91
89
  queue_job ApplyChangeJob.new
92
90
 
93
- when "$/file/reset"
94
- uri = request[:params][:uri]
95
- text = request[:params][:content]
91
+ when CustomMethods::FileLoad::METHOD
92
+ params = request[:params] #: CustomMethods::FileLoad::params
93
+ input = params[:content]
94
+ load_files(input)
95
+ queue_job ApplyChangeJob.new
96
+
97
+ when CustomMethods::FileReset::METHOD
98
+ params = request[:params] #: CustomMethods::FileReset::params
99
+ uri = params[:uri]
100
+ text = params[:content]
96
101
  reset_change(uri: uri, text: text)
97
102
  queue_job ApplyChangeJob.new
98
103
 
@@ -464,9 +469,11 @@ module Steep
464
469
 
465
470
  if (items, index = provider.run(line: job.line, column: job.column))
466
471
  signatures = items.map do |item|
472
+ params = item.parameters or raise
473
+
467
474
  LSP::Interface::SignatureInformation.new(
468
475
  label: item.method_type.to_s,
469
- parameters: item.parameters.map { |param| LSP::Interface::ParameterInformation.new(label: param)},
476
+ parameters: params.map { |param| LSP::Interface::ParameterInformation.new(label: param)},
470
477
  active_parameter: item.active_parameter,
471
478
  documentation: item.comment&.yield_self do |comment|
472
479
  LSP::Interface::MarkupContent.new(
@@ -36,7 +36,8 @@ module Steep
36
36
  ----
37
37
  MD
38
38
 
39
- method_types = call.method_decls.map(&:method_type)
39
+ method_decls = call.method_decls.sort_by {|decl| decl.method_name.to_s }
40
+ method_types = method_decls.map(&:method_type)
40
41
 
41
42
  if call.is_a?(TypeInference::MethodCall::Special)
42
43
  method_types = [
@@ -52,7 +53,8 @@ module Steep
52
53
  MD
53
54
  end
54
55
  when TypeInference::MethodCall::Error
55
- method_types = call.method_decls.map {|decl| decl.method_type }
56
+ method_decls = call.method_decls.sort_by {|decl| decl.method_name.to_s }
57
+ method_types = method_decls.map {|decl| decl.method_type }
56
58
 
57
59
  header = <<~MD
58
60
  **🚨 No compatible method type found**
@@ -61,8 +63,8 @@ module Steep
61
63
  MD
62
64
  end
63
65
 
64
- method_names = call.method_decls.map {|decl| decl.method_name.relative }
65
- docs = call.method_decls.map {|decl| [decl.method_name, decl.method_def.comment] }.to_h
66
+ method_names = method_decls.map {|decl| decl.method_name.relative }
67
+ docs = method_decls.map {|decl| [decl.method_name, decl.method_def.comment] }.to_h
66
68
 
67
69
  if header
68
70
  io.puts header
@@ -373,8 +375,8 @@ module Steep
373
375
  end
374
376
  s << param.name.to_s
375
377
 
376
- if param.upper_bound
377
- s << " < #{param.upper_bound.to_s}"
378
+ if param.upper_bound_type
379
+ s << " < #{param.upper_bound_type.to_s}"
378
380
  end
379
381
 
380
382
  s