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

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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -0
  3. data/bin/mem_graph.rb +67 -0
  4. data/bin/mem_prof.rb +102 -0
  5. data/bin/stackprof_test.rb +19 -0
  6. data/bin/steep-check.rb +251 -0
  7. data/lib/steep/annotation_parser.rb +1 -1
  8. data/lib/steep/ast/builtin.rb +5 -5
  9. data/lib/steep/ast/node/type_application.rb +7 -6
  10. data/lib/steep/ast/types/any.rb +1 -9
  11. data/lib/steep/ast/types/boolean.rb +8 -16
  12. data/lib/steep/ast/types/bot.rb +2 -10
  13. data/lib/steep/ast/types/class.rb +1 -13
  14. data/lib/steep/ast/types/factory.rb +101 -85
  15. data/lib/steep/ast/types/instance.rb +1 -13
  16. data/lib/steep/ast/types/intersection.rb +8 -15
  17. data/lib/steep/ast/types/literal.rb +2 -8
  18. data/lib/steep/ast/types/logic.rb +3 -24
  19. data/lib/steep/ast/types/name.rb +5 -16
  20. data/lib/steep/ast/types/nil.rb +3 -12
  21. data/lib/steep/ast/types/proc.rb +4 -13
  22. data/lib/steep/ast/types/record.rb +21 -12
  23. data/lib/steep/ast/types/self.rb +1 -13
  24. data/lib/steep/ast/types/shared_instance.rb +11 -0
  25. data/lib/steep/ast/types/top.rb +1 -9
  26. data/lib/steep/ast/types/tuple.rb +4 -10
  27. data/lib/steep/ast/types/union.rb +10 -15
  28. data/lib/steep/ast/types/var.rb +4 -13
  29. data/lib/steep/ast/types/void.rb +2 -10
  30. data/lib/steep/diagnostic/ruby.rb +4 -4
  31. data/lib/steep/drivers/check.rb +11 -14
  32. data/lib/steep/drivers/checkfile.rb +8 -10
  33. data/lib/steep/drivers/stats.rb +17 -13
  34. data/lib/steep/drivers/utils/driver_helper.rb +24 -3
  35. data/lib/steep/drivers/watch.rb +3 -3
  36. data/lib/steep/interface/builder.rb +162 -138
  37. data/lib/steep/interface/method_type.rb +12 -20
  38. data/lib/steep/interface/shape.rb +66 -10
  39. data/lib/steep/interface/substitution.rb +2 -0
  40. data/lib/steep/interface/type_param.rb +20 -7
  41. data/lib/steep/located_value.rb +20 -0
  42. data/lib/steep/server/change_buffer.rb +5 -7
  43. data/lib/steep/server/custom_methods.rb +61 -0
  44. data/lib/steep/server/delay_queue.rb +8 -1
  45. data/lib/steep/server/interaction_worker.rb +10 -5
  46. data/lib/steep/server/lsp_formatter.rb +8 -6
  47. data/lib/steep/server/master.rb +193 -140
  48. data/lib/steep/server/type_check_worker.rb +18 -19
  49. data/lib/steep/server/work_done_progress.rb +64 -0
  50. data/lib/steep/services/completion_provider.rb +24 -22
  51. data/lib/steep/services/goto_service.rb +3 -2
  52. data/lib/steep/services/hover_provider/ruby.rb +7 -6
  53. data/lib/steep/services/signature_help_provider.rb +7 -6
  54. data/lib/steep/services/signature_service.rb +1 -1
  55. data/lib/steep/services/type_check_service.rb +3 -3
  56. data/lib/steep/signature/validator.rb +17 -20
  57. data/lib/steep/subtyping/check.rb +105 -55
  58. data/lib/steep/subtyping/constraints.rb +11 -15
  59. data/lib/steep/type_construction.rb +100 -100
  60. data/lib/steep/type_inference/block_params.rb +6 -6
  61. data/lib/steep/type_inference/logic_type_interpreter.rb +11 -7
  62. data/lib/steep/type_inference/method_call.rb +3 -3
  63. data/lib/steep/type_inference/method_params.rb +1 -1
  64. data/lib/steep/type_inference/send_args.rb +1 -1
  65. data/lib/steep/typing.rb +158 -102
  66. data/lib/steep/version.rb +1 -1
  67. data/lib/steep.rb +28 -3
  68. data/steep.gemspec +2 -2
  69. metadata +16 -9
  70. 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
 
@@ -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