steep 0.28.0 → 0.32.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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -0
  3. data/bin/steep-prof +1 -2
  4. data/lib/steep.rb +5 -3
  5. data/lib/steep/annotation_parser.rb +2 -4
  6. data/lib/steep/ast/builtin.rb +9 -1
  7. data/lib/steep/ast/types/factory.rb +177 -53
  8. data/lib/steep/ast/types/logic.rb +63 -0
  9. data/lib/steep/interface/method_type.rb +14 -4
  10. data/lib/steep/module_helper.rb +25 -0
  11. data/lib/steep/project.rb +25 -0
  12. data/lib/steep/project/completion_provider.rb +57 -58
  13. data/lib/steep/project/file_loader.rb +7 -2
  14. data/lib/steep/project/hover_content.rb +92 -83
  15. data/lib/steep/project/signature_file.rb +33 -0
  16. data/lib/steep/project/{file.rb → source_file.rb} +24 -54
  17. data/lib/steep/project/target.rb +31 -12
  18. data/lib/steep/server/code_worker.rb +30 -46
  19. data/lib/steep/server/interaction_worker.rb +42 -38
  20. data/lib/steep/server/master.rb +13 -30
  21. data/lib/steep/server/utils.rb +46 -13
  22. data/lib/steep/server/worker_process.rb +4 -2
  23. data/lib/steep/signature/validator.rb +3 -3
  24. data/lib/steep/source.rb +58 -1
  25. data/lib/steep/subtyping/check.rb +5 -7
  26. data/lib/steep/subtyping/constraints.rb +8 -0
  27. data/lib/steep/type_construction.rb +204 -207
  28. data/lib/steep/type_inference/constant_env.rb +2 -5
  29. data/lib/steep/type_inference/logic_type_interpreter.rb +225 -0
  30. data/lib/steep/type_inference/type_env.rb +2 -2
  31. data/lib/steep/version.rb +1 -1
  32. data/smoke/toplevel/Steepfile +5 -0
  33. data/smoke/toplevel/a.rb +4 -0
  34. data/smoke/toplevel/a.rbs +3 -0
  35. data/smoke/type_case/a.rb +0 -7
  36. data/steep.gemspec +2 -2
  37. metadata +18 -14
  38. data/lib/steep/ast/method_type.rb +0 -126
  39. data/lib/steep/ast/namespace.rb +0 -80
  40. data/lib/steep/names.rb +0 -86
@@ -0,0 +1,63 @@
1
+ module Steep
2
+ module AST
3
+ module Types
4
+ module Logic
5
+ class Base
6
+ attr_reader :location
7
+
8
+ def subst(s)
9
+ self
10
+ end
11
+
12
+ def free_variables
13
+ @fvs ||= Set[]
14
+ end
15
+
16
+ def hash
17
+ self.class.hash
18
+ end
19
+
20
+ def ==(other)
21
+ other.class ==self.class
22
+ end
23
+
24
+ alias eql? ==
25
+
26
+ def to_s
27
+ "<% #{self.class} %>"
28
+ end
29
+ end
30
+
31
+ class Not < Base
32
+ def initialize(location: nil)
33
+ @location = location
34
+ end
35
+ end
36
+
37
+ class ReceiverIsNil < Base
38
+ def initialize(location: nil)
39
+ @location = location
40
+ end
41
+ end
42
+
43
+ class ReceiverIsNotNil < Base
44
+ def initialize(location: nil)
45
+ @location = location
46
+ end
47
+ end
48
+
49
+ class ReceiverIsArg < Base
50
+ def initialize(location: nil)
51
+ @location = location
52
+ end
53
+ end
54
+
55
+ class ArgIsReceiver < Base
56
+ def initialize(location: nil)
57
+ @location = location
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -775,13 +775,15 @@ module Steep
775
775
  attr_reader :block
776
776
  attr_reader :return_type
777
777
  attr_reader :location
778
+ attr_reader :method_def
778
779
 
779
- def initialize(type_params:, params:, block:, return_type:, location:)
780
+ def initialize(type_params:, params:, block:, return_type:, location:, method_def:)
780
781
  @type_params = type_params
781
782
  @params = params
782
783
  @block = block
783
784
  @return_type = return_type
784
785
  @location = location
786
+ @method_def = method_def
785
787
  end
786
788
 
787
789
  def ==(other)
@@ -790,6 +792,7 @@ module Steep
790
792
  other.params == params &&
791
793
  other.block == block &&
792
794
  other.return_type == return_type &&
795
+ (!other.method_def || !method_def || other.method_def == method_def) &&
793
796
  (!other.location || !location || other.location == location)
794
797
  end
795
798
 
@@ -821,6 +824,7 @@ module Steep
821
824
  params: params.subst(s_),
822
825
  block: block&.subst(s_),
823
826
  return_type: return_type.subst(s_),
827
+ method_def: method_def,
824
828
  location: location
825
829
  )
826
830
  end
@@ -843,14 +847,16 @@ module Steep
843
847
  params: params.subst(s),
844
848
  block: block&.subst(s),
845
849
  return_type: return_type.subst(s),
846
- location: location)
850
+ location: location,
851
+ method_def: method_def)
847
852
  end
848
853
 
849
- def with(type_params: self.type_params, params: self.params, block: self.block, return_type: self.return_type, location: self.location)
854
+ def with(type_params: self.type_params, params: self.params, block: self.block, return_type: self.return_type, location: self.location, method_def: self.method_def)
850
855
  self.class.new(type_params: type_params,
851
856
  params: params,
852
857
  block: block,
853
858
  return_type: return_type,
859
+ method_def: method_def,
854
860
  location: location)
855
861
  end
856
862
 
@@ -867,7 +873,8 @@ module Steep
867
873
  params: params.map_type(&block),
868
874
  block: self.block&.yield_self {|blk| blk.map_type(&block) },
869
875
  return_type: yield(return_type),
870
- location: location)
876
+ location: location,
877
+ method_def: method_def)
871
878
  end
872
879
 
873
880
  # Returns a new method type which can be used for the method implementation type of both `self` and `other`.
@@ -895,6 +902,7 @@ module Steep
895
902
  return_type: AST::Types::Union.build(
896
903
  types: [return_type.subst(s1),other.return_type.subst(s2)]
897
904
  ),
905
+ method_def: method_def,
898
906
  location: nil
899
907
  )
900
908
  end
@@ -950,6 +958,7 @@ module Steep
950
958
  block: block,
951
959
  return_type: return_type,
952
960
  type_params: type_params,
961
+ method_def: nil,
953
962
  location: nil
954
963
  )
955
964
  end
@@ -997,6 +1006,7 @@ module Steep
997
1006
  block: block,
998
1007
  return_type: return_type,
999
1008
  type_params: type_params,
1009
+ method_def: nil,
1000
1010
  location: nil
1001
1011
  )
1002
1012
  end
@@ -0,0 +1,25 @@
1
+ module Steep
2
+ module ModuleHelper
3
+ def module_name_from_node(node)
4
+ case node.type
5
+ when :const, :casgn
6
+ namespace = namespace_from_node(node.children[0]) or return
7
+ name = node.children[1]
8
+ RBS::TypeName.new(name: name, namespace: namespace)
9
+ end
10
+ end
11
+
12
+ def namespace_from_node(node)
13
+ case node&.type
14
+ when nil
15
+ RBS::Namespace.empty
16
+ when :cbase
17
+ RBS::Namespace.root
18
+ when :const
19
+ namespace_from_node(node.children[0])&.yield_self do |parent|
20
+ parent.append(node.children[1])
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -24,6 +24,31 @@ module Steep
24
24
  (base_dir + path).cleanpath
25
25
  end
26
26
 
27
+ def target_for_source_path(path)
28
+ targets.find do |target|
29
+ target.possible_source_file?(path)
30
+ end
31
+ end
32
+
33
+ def targets_for_path(path)
34
+ if target = target_for_source_path(path)
35
+ [target, []]
36
+ else
37
+ [
38
+ nil,
39
+ targets.select do |target|
40
+ target.possible_signature_file?(path)
41
+ end
42
+ ]
43
+ end
44
+ end
45
+
46
+ def all_source_files
47
+ targets.each.with_object(Set[]) do |target, paths|
48
+ paths.merge(target.source_files.keys)
49
+ end
50
+ end
51
+
27
52
  def type_of_node(path:, line:, column:)
28
53
  source_file = targets.map {|target| target.source_files[path] }.compact[0]
29
54
 
@@ -10,13 +10,9 @@ module Steep
10
10
 
11
11
  InstanceVariableItem = Struct.new(:identifier, :range, :type, keyword_init: true)
12
12
  LocalVariableItem = Struct.new(:identifier, :range, :type, keyword_init: true)
13
- MethodNameItem = Struct.new(:identifier, :range, :definition, :def_type, :inherited_method, keyword_init: true) do
14
- def method_type
15
- def_type.type
16
- end
17
-
13
+ MethodNameItem = Struct.new(:identifier, :range, :method_def, :method_type, :inherited_method, keyword_init: true) do
18
14
  def comment
19
- def_type.comment
15
+ method_def&.comment
20
16
  end
21
17
  end
22
18
 
@@ -33,11 +29,13 @@ module Steep
33
29
  @subtyping = subtyping
34
30
  end
35
31
 
36
- def type_check!(text)
32
+ def type_check!(text, line:, column:)
37
33
  @modified_text = text
38
34
 
39
35
  Steep.measure "parsing" do
40
- @source = SourceFile.parse(text, path: path, factory: subtyping.factory)
36
+ @source = SourceFile
37
+ .parse(text, path: path, factory: subtyping.factory)
38
+ .without_unrelated_defs(line: line, column: column)
41
39
  end
42
40
 
43
41
  Steep.measure "typechecking" do
@@ -57,7 +55,7 @@ module Steep
57
55
  begin
58
56
  Steep.logger.tagged "completion_provider#run(line: #{line}, column: #{column})" do
59
57
  Steep.measure "type_check!" do
60
- type_check!(source_text)
58
+ type_check!(source_text, line: line, column: column)
61
59
  end
62
60
  end
63
61
 
@@ -70,11 +68,11 @@ module Steep
70
68
  case possible_trigger
71
69
  when "."
72
70
  source_text[index-1] = " "
73
- type_check!(source_text)
71
+ type_check!(source_text, line: line, column: column)
74
72
  items_for_dot(position: position)
75
73
  when "@"
76
74
  source_text[index-1] = " "
77
- type_check!(source_text)
75
+ type_check!(source_text, line: line, column: column)
78
76
  items_for_atmark(position: position)
79
77
  else
80
78
  []
@@ -90,7 +88,9 @@ module Steep
90
88
  end
91
89
 
92
90
  def at_end?(pos, of:)
93
- of.last_line == pos.line && of.last_column == pos.column
91
+ if of
92
+ of.last_line == pos.line && of.last_column == pos.column
93
+ end
94
94
  end
95
95
 
96
96
  def range_for(position, prefix: "")
@@ -194,21 +194,25 @@ module Steep
194
194
  return [] unless node
195
195
 
196
196
  if at_end?(shift_pos, of: node.loc)
197
- context = typing.context_at(line: position.line, column: position.column)
198
- receiver_type = case (type = typing.type_of(node: node))
199
- when AST::Types::Self
200
- context.self_type
201
- else
202
- type
203
- end
204
-
205
- items = []
206
- method_items_for_receiver_type(receiver_type,
207
- include_private: false,
208
- prefix: "",
209
- position: position,
210
- items: items)
211
- items
197
+ begin
198
+ context = typing.context_at(line: position.line, column: position.column)
199
+ receiver_type = case (type = typing.type_of(node: node))
200
+ when AST::Types::Self
201
+ context.self_type
202
+ else
203
+ type
204
+ end
205
+
206
+ items = []
207
+ method_items_for_receiver_type(receiver_type,
208
+ include_private: false,
209
+ prefix: "",
210
+ position: position,
211
+ items: items)
212
+ items
213
+ rescue Typing::UnknownNodeError
214
+ []
215
+ end
212
216
  else
213
217
  []
214
218
  end
@@ -217,50 +221,40 @@ module Steep
217
221
  def items_for_atmark(position:)
218
222
  # @ ←
219
223
  shift_pos = position-1
220
- node, *parents = source.find_nodes(line: shift_pos.line, column: shift_pos.column)
224
+ node, *_ = source.find_nodes(line: shift_pos.line, column: shift_pos.column)
221
225
  node ||= source.node
222
226
 
223
227
  return [] unless node
224
228
 
225
229
  context = typing.context_at(line: position.line, column: position.column)
226
230
  items = []
227
- instance_variable_items_for_context(context, prefix: "", position: position, items: items)
231
+ instance_variable_items_for_context(context, prefix: "@", position: position, items: items)
228
232
  items
229
233
  end
230
234
 
231
235
  def method_items_for_receiver_type(type, include_private:, prefix:, position:, items:)
232
236
  range = range_for(position, prefix: prefix)
233
- definition = case type
234
- when AST::Types::Name::Instance
235
- type_name = subtyping.factory.type_name_1(type.name)
236
- subtyping.factory.definition_builder.build_instance(type_name)
237
- when AST::Types::Name::Singleton
238
- type_name = subtyping.factory.type_name_1(type.name)
239
- subtyping.factory.definition_builder.build_singleton(type_name)
240
- when AST::Types::Name::Interface
241
- type_name = subtyping.factory.type_name_1(type.name)
242
- subtyping.factory.definition_builder.build_interface(type_name)
243
- end
244
-
245
- if definition
246
- definition.methods.each do |name, method|
247
- next if disallowed_method?(name)
248
-
249
- if include_private || method.public?
250
- if name.to_s.start_with?(prefix)
251
- if word_name?(name.to_s)
252
- method.defs.each do |def_type|
253
- items << MethodNameItem.new(identifier: name,
254
- range: range,
255
- definition: method,
256
- def_type: def_type,
257
- inherited_method: inherited_method?(method, definition))
258
- end
259
- end
237
+ interface = subtyping.factory.interface(type, self_type: type, private: include_private)
238
+
239
+ interface.methods.each do |name, method_entry|
240
+ next if disallowed_method?(name)
241
+
242
+ if name.to_s.start_with?(prefix)
243
+ if word_name?(name.to_s)
244
+ method_entry.method_types.each do |method_type|
245
+ items << MethodNameItem.new(
246
+ identifier: name,
247
+ range: range,
248
+ method_def: method_type.method_def,
249
+ method_type: method_type.method_def&.type || subtyping.factory.method_type_1(method_type, self_type: type),
250
+ inherited_method: inherited_method?(method_type.method_def, type)
251
+ )
260
252
  end
261
253
  end
262
254
  end
263
255
  end
256
+ rescue
257
+ # nop
264
258
  end
265
259
 
266
260
  def word_name?(name)
@@ -304,8 +298,13 @@ module Steep
304
298
  index
305
299
  end
306
300
 
307
- def inherited_method?(method, definition)
308
- method.implemented_in != definition.type_name
301
+ def inherited_method?(method_def, type)
302
+ case type
303
+ when AST::Types::Name::Instance, AST::Types::Name::Singleton, AST::Types::Name::Interface
304
+ method_def.implemented_in != type.name
305
+ else
306
+ false
307
+ end
309
308
  end
310
309
 
311
310
  def disallowed_method?(name)
@@ -28,15 +28,20 @@ module Steep
28
28
  end
29
29
 
30
30
  def load_sources(command_line_patterns)
31
+ loaded_paths = Set[]
32
+
31
33
  project.targets.each do |target|
32
34
  Steep.logger.tagged "target=#{target.name}" do
33
35
  target_patterns = command_line_patterns.empty? ? target.source_patterns : command_line_patterns
34
36
 
35
- each_path_in_patterns target_patterns, ".rb" do |path|
37
+ each_path_in_patterns(target_patterns, ".rb") do |path|
36
38
  if target.possible_source_file?(path)
37
- unless target.source_file?(path)
39
+ if loaded_paths.include?(path)
40
+ Steep.logger.warn { "Skipping #{target} while loading #{path}... (Already loaded to another target.)" }
41
+ else
38
42
  Steep.logger.info { "Adding source file: #{path}" }
39
43
  target.add_source path, project.absolute_path(path).read
44
+ loaded_paths << path
40
45
  end
41
46
  end
42
47
  end
@@ -21,9 +21,7 @@ module Steep
21
21
  @project = project
22
22
  end
23
23
 
24
- def method_definition_for(factory, module_name, singleton_method: nil, instance_method: nil)
25
- type_name = factory.type_name_1(module_name)
26
-
24
+ def method_definition_for(factory, type_name, singleton_method: nil, instance_method: nil)
27
25
  case
28
26
  when instance_method
29
27
  factory.definition_builder.build_instance(type_name).methods[instance_method]
@@ -38,102 +36,113 @@ module Steep
38
36
  end
39
37
  end
40
38
 
39
+ def typecheck(target, path:, line:, column:)
40
+ target.type_check(target_sources: [], validate_signatures: false)
41
+
42
+ case (status = target.status)
43
+ when Project::Target::TypeCheckStatus
44
+ subtyping = status.subtyping
45
+ source = SourceFile
46
+ .parse(target.source_files[path].content, path: path, factory: subtyping.factory)
47
+ .without_unrelated_defs(line: line, column: column)
48
+ SourceFile.type_check(source, subtyping: subtyping)
49
+ end
50
+ rescue
51
+ nil
52
+ end
53
+
41
54
  def content_for(path:, line:, column:)
42
- target = project.targets.find {|target| target.source_file?(path) }
55
+ target = project.target_for_source_path(path)
43
56
 
44
57
  if target
45
- source_file = target.source_files[path]
46
- target.type_check(target_sources: [source_file], validate_signatures: false)
58
+ typing = typecheck(target, path: path, line: line, column: column) or return
59
+
60
+ node, *parents = typing.source.find_nodes(line: line, column: column)
47
61
 
48
- case (status = source_file.status)
49
- when SourceFile::TypeCheckStatus
50
- node, *parents = status.source.find_nodes(line: line, column: column)
62
+ if node
63
+ case node.type
64
+ when :lvar
65
+ var_name = node.children[0]
66
+ context = typing.context_at(line: line, column: column)
67
+ var_type = context.lvar_env[var_name.name] || AST::Types::Any.new(location: nil)
51
68
 
52
- if node
53
- case node.type
54
- when :lvar
55
- var_name = node.children[0]
56
- context = status.typing.context_at(line: line, column: column)
57
- var_type = context.lvar_env[var_name.name] || AST::Types::Any.new(location: nil)
69
+ VariableContent.new(node: node, name: var_name.name, type: var_type, location: node.location.name)
70
+ when :lvasgn
71
+ var_name, rhs = node.children
72
+ context = typing.context_at(line: line, column: column)
73
+ type = context.lvar_env[var_name.name] || typing.type_of(node: rhs)
58
74
 
59
- VariableContent.new(node: node, name: var_name.name, type: var_type, location: node.location.name)
60
- when :lvasgn
61
- var_name, rhs = node.children
62
- context = status.typing.context_at(line: line, column: column)
63
- type = context.lvar_env[var_name.name] || status.typing.type_of(node: rhs)
75
+ VariableContent.new(node: node, name: var_name.name, type: type, location: node.location.name)
76
+ when :send
77
+ receiver, method_name, *_ = node.children
64
78
 
65
- VariableContent.new(node: node, name: var_name.name, type: type, location: node.location.name)
66
- when :send
67
- receiver, method_name, *_ = node.children
68
79
 
80
+ result_node = if parents[0]&.type == :block
81
+ parents[0]
82
+ else
83
+ node
84
+ end
69
85
 
70
- result_node = if parents[0]&.type == :block
71
- parents[0]
86
+ context = typing.context_at(line: line, column: column)
87
+
88
+ receiver_type = if receiver
89
+ typing.type_of(node: receiver)
72
90
  else
73
- node
91
+ context.self_type
74
92
  end
75
93
 
76
- context = status.typing.context_at(line: line, column: column)
77
-
78
- receiver_type = if receiver
79
- status.typing.type_of(node: receiver)
80
- else
81
- context.self_type
82
- end
83
-
84
- factory = context.type_env.subtyping.factory
85
- method_name, definition = case receiver_type
86
- when AST::Types::Name::Instance
87
- method_definition = method_definition_for(factory, receiver_type.name, instance_method: method_name)
88
- if method_definition&.defined_in
89
- owner_name = factory.type_name(method_definition.defined_in)
90
- [
91
- InstanceMethodName.new(owner_name, method_name),
92
- method_definition
93
- ]
94
- end
95
- when AST::Types::Name::Singleton
96
- method_definition = method_definition_for(factory, receiver_type.name, singleton_method: method_name)
97
- if method_definition&.defined_in
98
- owner_name = factory.type_name(method_definition.defined_in)
99
- [
100
- SingletonMethodName.new(owner_name, method_name),
101
- method_definition
102
- ]
103
- end
104
- else
105
- nil
94
+ factory = context.type_env.subtyping.factory
95
+ method_name, definition = case receiver_type
96
+ when AST::Types::Name::Instance
97
+ method_definition = method_definition_for(factory, receiver_type.name, instance_method: method_name)
98
+ if method_definition&.defined_in
99
+ owner_name = method_definition.defined_in
100
+ [
101
+ InstanceMethodName.new(owner_name, method_name),
102
+ method_definition
103
+ ]
106
104
  end
107
-
108
- MethodCallContent.new(
109
- node: node,
110
- method_name: method_name,
111
- type: status.typing.type_of(node: result_node),
112
- definition: definition,
113
- location: result_node.location.expression
114
- )
115
- when :def, :defs
116
- context = status.typing.context_at(line: line, column: column)
117
- method_context = context.method_context
118
-
119
- if method_context && method_context.method
120
- DefinitionContent.new(
121
- node: node,
122
- method_name: method_context.name,
123
- method_type: method_context.method_type,
124
- definition: method_context.method,
125
- location: node.loc.expression
126
- )
127
- end
128
- else
129
- type = status.typing.type_of(node: node)
130
-
131
- TypeContent.new(
105
+ when AST::Types::Name::Singleton
106
+ method_definition = method_definition_for(factory, receiver_type.name, singleton_method: method_name)
107
+ if method_definition&.defined_in
108
+ owner_name = method_definition.defined_in
109
+ [
110
+ SingletonMethodName.new(owner_name, method_name),
111
+ method_definition
112
+ ]
113
+ end
114
+ else
115
+ nil
116
+ end
117
+
118
+ MethodCallContent.new(
119
+ node: node,
120
+ method_name: method_name,
121
+ type: typing.type_of(node: result_node),
122
+ definition: definition,
123
+ location: result_node.location.expression
124
+ )
125
+ when :def, :defs
126
+ context = typing.context_at(line: line, column: column)
127
+ method_context = context.method_context
128
+
129
+ if method_context && method_context.method
130
+ DefinitionContent.new(
132
131
  node: node,
133
- type: type,
134
- location: node.location.expression
132
+ method_name: method_context.name,
133
+ method_type: method_context.method_type,
134
+ definition: method_context.method,
135
+ location: node.loc.expression
135
136
  )
136
137
  end
138
+ else
139
+ type = typing.type_of(node: node)
140
+
141
+ TypeContent.new(
142
+ node: node,
143
+ type: type,
144
+ location: node.location.expression
145
+ )
137
146
  end
138
147
  end
139
148
  end