steep 0.28.0 → 0.32.0

Sign up to get free protection for your applications and to get access to all the features.
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