steep 0.24.0 → 0.30.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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +48 -0
  3. data/bin/smoke_runner.rb +3 -4
  4. data/lib/steep.rb +6 -4
  5. data/lib/steep/annotation_parser.rb +2 -4
  6. data/lib/steep/ast/builtin.rb +11 -21
  7. data/lib/steep/ast/types.rb +5 -3
  8. data/lib/steep/ast/types/any.rb +1 -3
  9. data/lib/steep/ast/types/boolean.rb +1 -3
  10. data/lib/steep/ast/types/bot.rb +1 -3
  11. data/lib/steep/ast/types/class.rb +2 -2
  12. data/lib/steep/ast/types/factory.rb +251 -89
  13. data/lib/steep/ast/types/helper.rb +6 -0
  14. data/lib/steep/ast/types/instance.rb +2 -2
  15. data/lib/steep/ast/types/intersection.rb +20 -13
  16. data/lib/steep/ast/types/literal.rb +1 -3
  17. data/lib/steep/ast/types/logic.rb +63 -0
  18. data/lib/steep/ast/types/name.rb +15 -67
  19. data/lib/steep/ast/types/nil.rb +1 -3
  20. data/lib/steep/ast/types/proc.rb +5 -2
  21. data/lib/steep/ast/types/record.rb +9 -4
  22. data/lib/steep/ast/types/self.rb +1 -1
  23. data/lib/steep/ast/types/top.rb +1 -3
  24. data/lib/steep/ast/types/tuple.rb +5 -3
  25. data/lib/steep/ast/types/union.rb +16 -9
  26. data/lib/steep/ast/types/var.rb +2 -2
  27. data/lib/steep/ast/types/void.rb +1 -3
  28. data/lib/steep/drivers/check.rb +4 -0
  29. data/lib/steep/errors.rb +14 -0
  30. data/lib/steep/interface/interface.rb +5 -62
  31. data/lib/steep/interface/method_type.rb +394 -93
  32. data/lib/steep/interface/substitution.rb +48 -6
  33. data/lib/steep/module_helper.rb +25 -0
  34. data/lib/steep/project.rb +25 -0
  35. data/lib/steep/project/completion_provider.rb +48 -51
  36. data/lib/steep/project/file_loader.rb +7 -2
  37. data/lib/steep/project/hover_content.rb +4 -6
  38. data/lib/steep/project/signature_file.rb +33 -0
  39. data/lib/steep/project/{file.rb → source_file.rb} +24 -54
  40. data/lib/steep/project/target.rb +36 -14
  41. data/lib/steep/server/base_worker.rb +5 -3
  42. data/lib/steep/server/code_worker.rb +31 -45
  43. data/lib/steep/server/master.rb +23 -31
  44. data/lib/steep/server/utils.rb +46 -13
  45. data/lib/steep/server/worker_process.rb +4 -2
  46. data/lib/steep/signature/validator.rb +3 -3
  47. data/lib/steep/source.rb +4 -3
  48. data/lib/steep/subtyping/check.rb +46 -59
  49. data/lib/steep/subtyping/constraints.rb +8 -0
  50. data/lib/steep/type_construction.rb +771 -513
  51. data/lib/steep/type_inference/block_params.rb +5 -0
  52. data/lib/steep/type_inference/constant_env.rb +3 -6
  53. data/lib/steep/type_inference/context.rb +8 -0
  54. data/lib/steep/type_inference/context_array.rb +4 -3
  55. data/lib/steep/type_inference/logic.rb +31 -0
  56. data/lib/steep/type_inference/logic_type_interpreter.rb +219 -0
  57. data/lib/steep/type_inference/type_env.rb +2 -2
  58. data/lib/steep/typing.rb +7 -0
  59. data/lib/steep/version.rb +1 -1
  60. data/smoke/alias/a.rb +1 -1
  61. data/smoke/case/a.rb +1 -1
  62. data/smoke/hash/d.rb +1 -1
  63. data/smoke/if/a.rb +1 -1
  64. data/smoke/module/a.rb +1 -1
  65. data/smoke/rescue/a.rb +4 -13
  66. data/smoke/toplevel/Steepfile +5 -0
  67. data/smoke/toplevel/a.rb +4 -0
  68. data/smoke/toplevel/a.rbs +3 -0
  69. data/smoke/type_case/a.rb +0 -7
  70. data/steep.gemspec +2 -2
  71. metadata +15 -11
  72. data/lib/steep/ast/method_type.rb +0 -126
  73. data/lib/steep/ast/namespace.rb +0 -80
  74. data/lib/steep/names.rb +0 -86
@@ -26,7 +26,32 @@ module Steep
26
26
  end
27
27
 
28
28
  def self.empty
29
- new(dictionary: {}, instance_type: AST::Types::Instance.new, module_type: AST::Types::Class.new, self_type: AST::Types::Self.new)
29
+ new(dictionary: {},
30
+ instance_type: INSTANCE_TYPE,
31
+ module_type: CLASS_TYPE,
32
+ self_type: SELF_TYPE)
33
+ end
34
+
35
+ def empty?
36
+ dictionary.empty? &&
37
+ instance_type.is_a?(AST::Types::Instance) &&
38
+ module_type.is_a?(AST::Types::Class) &&
39
+ self_type.is_a?(AST::Types::Self)
40
+ end
41
+
42
+ INSTANCE_TYPE = AST::Types::Instance.new
43
+ CLASS_TYPE = AST::Types::Class.new
44
+ SELF_TYPE = AST::Types::Self.new
45
+
46
+ def domain
47
+ set = Set.new
48
+
49
+ set.merge(dictionary.keys)
50
+ set << INSTANCE_TYPE unless instance_type.is_a?(AST::Types::Instance)
51
+ set << CLASS_TYPE unless instance_type.is_a?(AST::Types::Class)
52
+ set << SELF_TYPE unless instance_type.is_a?(AST::Types::Self)
53
+
54
+ set
30
55
  end
31
56
 
32
57
  def to_s
@@ -65,22 +90,39 @@ module Steep
65
90
 
66
91
  def except(vars)
67
92
  self.class.new(
68
- dictionary: dictionary.reject {|k, _| vars.include?(k) },
93
+ dictionary: dictionary,
69
94
  instance_type: instance_type,
70
95
  module_type: module_type,
71
96
  self_type: self_type
72
- )
97
+ ).except!(vars)
73
98
  end
74
99
 
75
- def merge!(s)
100
+ def except!(vars)
101
+ vars.each do |var|
102
+ dictionary.delete(var)
103
+ end
104
+
105
+ self
106
+ end
107
+
108
+ def merge!(s, overwrite: false)
76
109
  dictionary.transform_values! {|ty| ty.subst(s) }
77
110
  dictionary.merge!(s.dictionary) do |key, a, b|
78
111
  if a == b
79
112
  a
80
113
  else
81
- raise "Duplicated key on merge!: #{key}, #{a}, #{b}"
114
+ if overwrite
115
+ b
116
+ else
117
+ raise "Duplicated key on merge!: #{key}, #{a}, #{b} (#{self})"
118
+ end
82
119
  end
83
120
  end
121
+
122
+ @instance_type = instance_type.subst(s)
123
+ @module_type = module_type.subst(s)
124
+ @self_type = self_type.subst(s)
125
+
84
126
  self
85
127
  end
86
128
 
@@ -92,7 +134,7 @@ module Steep
92
134
  end
93
135
 
94
136
  def add!(v, ty)
95
- merge!(Substitution.new(dictionary: { v => ty }, instance_type: nil, module_type: nil, self_type: nil))
137
+ merge!(Substitution.new(dictionary: { v => ty }, instance_type: instance_type, module_type: module_type, self_type: self_type))
96
138
  end
97
139
  end
98
140
  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
 
@@ -90,7 +86,9 @@ module Steep
90
86
  end
91
87
 
92
88
  def at_end?(pos, of:)
93
- of.last_line == pos.line && of.last_column == pos.column
89
+ if of
90
+ of.last_line == pos.line && of.last_column == pos.column
91
+ end
94
92
  end
95
93
 
96
94
  def range_for(position, prefix: "")
@@ -194,21 +192,25 @@ module Steep
194
192
  return [] unless node
195
193
 
196
194
  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
195
+ begin
196
+ context = typing.context_at(line: position.line, column: position.column)
197
+ receiver_type = case (type = typing.type_of(node: node))
198
+ when AST::Types::Self
199
+ context.self_type
200
+ else
201
+ type
202
+ end
203
+
204
+ items = []
205
+ method_items_for_receiver_type(receiver_type,
206
+ include_private: false,
207
+ prefix: "",
208
+ position: position,
209
+ items: items)
210
+ items
211
+ rescue Typing::UnknownNodeError
212
+ []
213
+ end
212
214
  else
213
215
  []
214
216
  end
@@ -230,37 +232,27 @@ module Steep
230
232
 
231
233
  def method_items_for_receiver_type(type, include_private:, prefix:, position:, items:)
232
234
  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::Class, AST::Types::Name::Module
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
235
+ interface = subtyping.factory.interface(type, self_type: type, private: include_private)
236
+
237
+ interface.methods.each do |name, method_entry|
238
+ next if disallowed_method?(name)
239
+
240
+ if name.to_s.start_with?(prefix)
241
+ if word_name?(name.to_s)
242
+ method_entry.method_types.each do |method_type|
243
+ items << MethodNameItem.new(
244
+ identifier: name,
245
+ range: range,
246
+ method_def: method_type.method_def,
247
+ method_type: method_type.method_def&.type || subtyping.factory.method_type_1(method_type, self_type: type),
248
+ inherited_method: inherited_method?(method_type.method_def, type)
249
+ )
260
250
  end
261
251
  end
262
252
  end
263
253
  end
254
+ rescue
255
+ # nop
264
256
  end
265
257
 
266
258
  def word_name?(name)
@@ -304,8 +296,13 @@ module Steep
304
296
  index
305
297
  end
306
298
 
307
- def inherited_method?(method, definition)
308
- method.implemented_in != definition.type_name
299
+ def inherited_method?(method_def, type)
300
+ case type
301
+ when AST::Types::Name::Instance, AST::Types::Name::Singleton, AST::Types::Name::Interface
302
+ method_def.implemented_in != type.name
303
+ else
304
+ false
305
+ end
309
306
  end
310
307
 
311
308
  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]
@@ -86,16 +84,16 @@ module Steep
86
84
  when AST::Types::Name::Instance
87
85
  method_definition = method_definition_for(factory, receiver_type.name, instance_method: method_name)
88
86
  if method_definition&.defined_in
89
- owner_name = factory.type_name(method_definition.defined_in)
87
+ owner_name = method_definition.defined_in
90
88
  [
91
89
  InstanceMethodName.new(owner_name, method_name),
92
90
  method_definition
93
91
  ]
94
92
  end
95
- when AST::Types::Name::Class
93
+ when AST::Types::Name::Singleton
96
94
  method_definition = method_definition_for(factory, receiver_type.name, singleton_method: method_name)
97
95
  if method_definition&.defined_in
98
- owner_name = factory.type_name(method_definition.defined_in)
96
+ owner_name = method_definition.defined_in
99
97
  [
100
98
  SingletonMethodName.new(owner_name, method_name),
101
99
  method_definition
@@ -0,0 +1,33 @@
1
+ module Steep
2
+ class Project
3
+ class SignatureFile
4
+ attr_reader :path
5
+ attr_reader :content
6
+ attr_reader :content_updated_at
7
+
8
+ attr_reader :status
9
+
10
+ ParseErrorStatus = Struct.new(:error, :timestamp, keyword_init: true)
11
+ DeclarationsStatus = Struct.new(:declarations, :timestamp, keyword_init: true)
12
+
13
+ def initialize(path:)
14
+ @path = path
15
+ self.content = ""
16
+ end
17
+
18
+ def content=(content)
19
+ @content_updated_at = Time.now
20
+ @content = content
21
+ @status = nil
22
+ end
23
+
24
+ def load!
25
+ buffer = RBS::Buffer.new(name: path, content: content)
26
+ decls = RBS::Parser.parse_signature(buffer)
27
+ @status = DeclarationsStatus.new(declarations: decls, timestamp: Time.now)
28
+ rescue RBS::Parser::SyntaxError, RBS::Parser::SemanticsError => exn
29
+ @status = ParseErrorStatus.new(error: exn, timestamp: Time.now)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -8,10 +8,10 @@ module Steep
8
8
 
9
9
  attr_accessor :status
10
10
 
11
- ParseErrorStatus = Struct.new(:error, keyword_init: true)
12
- AnnotationSyntaxErrorStatus = Struct.new(:error, :location, keyword_init: true)
11
+ ParseErrorStatus = Struct.new(:error, :timestamp, keyword_init: true)
12
+ AnnotationSyntaxErrorStatus = Struct.new(:error, :location, :timestamp, keyword_init: true)
13
13
  TypeCheckStatus = Struct.new(:typing, :source, :timestamp, keyword_init: true)
14
- TypeCheckErrorStatus = Struct.new(:error, keyword_init: true)
14
+ TypeCheckErrorStatus = Struct.new(:error, :timestamp, keyword_init: true)
15
15
 
16
16
  def initialize(path:)
17
17
  @path = path
@@ -20,11 +20,9 @@ module Steep
20
20
  end
21
21
 
22
22
  def content=(content)
23
- if @content != content
24
- @content_updated_at = Time.now
25
- @content = content
26
- @status = nil
27
- end
23
+ @content_updated_at = Time.now
24
+ @content = content
25
+ @status = nil
28
26
  end
29
27
 
30
28
  def errors
@@ -41,8 +39,8 @@ module Steep
41
39
  end
42
40
 
43
41
  def self.type_check(source, subtyping:)
44
- annotations = source.annotations(block: source.node, factory: subtyping.factory, current_module: AST::Namespace.root)
45
- const_env = TypeInference::ConstantEnv.new(factory: subtyping.factory, context: [AST::Namespace.root])
42
+ annotations = source.annotations(block: source.node, factory: subtyping.factory, current_module: RBS::Namespace.root)
43
+ const_env = TypeInference::ConstantEnv.new(factory: subtyping.factory, context: [RBS::Namespace.root])
46
44
  type_env = TypeInference::TypeEnv.build(annotations: annotations,
47
45
  subtyping: subtyping,
48
46
  const_env: const_env,
@@ -55,12 +53,14 @@ module Steep
55
53
  context = TypeInference::Context.new(
56
54
  block_context: nil,
57
55
  module_context: TypeInference::Context::ModuleContext.new(
58
- instance_type: nil,
59
- module_type: nil,
56
+ instance_type: AST::Builtin::Object.instance_type,
57
+ module_type: AST::Builtin::Object.module_type,
60
58
  implement_name: nil,
61
- current_namespace: AST::Namespace.root,
59
+ current_namespace: RBS::Namespace.root,
62
60
  const_env: const_env,
63
- class_name: nil
61
+ class_name: AST::Builtin::Object.module_name,
62
+ instance_definition: subtyping.factory.definition_builder.build_instance(AST::Builtin::Object.module_name),
63
+ module_definition: subtyping.factory.definition_builder.build_singleton(AST::Builtin::Object.module_name)
64
64
  ),
65
65
  method_context: nil,
66
66
  break_context: nil,
@@ -86,31 +86,31 @@ module Steep
86
86
 
87
87
  def type_check(subtyping, env_updated_at)
88
88
  # skip type check
89
- return false if status.is_a?(TypeCheckStatus) && env_updated_at <= status.timestamp
89
+ return false if status && env_updated_at <= status.timestamp
90
+
91
+ now = Time.now
90
92
 
91
93
  parse(subtyping.factory) do |source|
92
94
  typing = self.class.type_check(source, subtyping: subtyping)
93
- @status = TypeCheckStatus.new(
94
- typing: typing,
95
- source: source,
96
- timestamp: Time.now
97
- )
95
+ @status = TypeCheckStatus.new(typing: typing, source: source, timestamp: now)
98
96
  rescue RBS::NoTypeFoundError,
99
97
  RBS::NoMixinFoundError,
100
98
  RBS::NoSuperclassFoundError,
101
99
  RBS::DuplicatedMethodDefinitionError,
102
100
  RBS::InvalidTypeApplicationError => exn
103
101
  # Skip logging known signature errors (they are handled with load_signatures(validate: true))
104
- @status = TypeCheckErrorStatus.new(error: exn)
102
+ @status = TypeCheckErrorStatus.new(error: exn, timestamp: now)
105
103
  rescue => exn
106
104
  Steep.log_error(exn)
107
- @status = TypeCheckErrorStatus.new(error: exn)
105
+ @status = TypeCheckErrorStatus.new(error: exn, timestamp: now)
108
106
  end
109
107
 
110
108
  true
111
109
  end
112
110
 
113
111
  def parse(factory)
112
+ now = Time.now
113
+
114
114
  if status.is_a?(TypeCheckStatus)
115
115
  yield status.source
116
116
  else
@@ -118,40 +118,10 @@ module Steep
118
118
  end
119
119
  rescue AnnotationParser::SyntaxError => exn
120
120
  Steep.logger.warn { "Annotation syntax error on #{path}: #{exn.inspect}" }
121
- @status = AnnotationSyntaxErrorStatus.new(error: exn, location: exn.location)
121
+ @status = AnnotationSyntaxErrorStatus.new(error: exn, location: exn.location, timestamp: now)
122
122
  rescue ::Parser::SyntaxError, EncodingError => exn
123
123
  Steep.logger.warn { "Source parsing error on #{path}: #{exn.inspect}" }
124
- @status = ParseErrorStatus.new(error: exn)
125
- end
126
- end
127
-
128
- class SignatureFile
129
- attr_reader :path
130
- attr_reader :content
131
- attr_reader :content_updated_at
132
-
133
- attr_reader :status
134
-
135
- ParseErrorStatus = Struct.new(:error, keyword_init: true)
136
- DeclarationsStatus = Struct.new(:declarations, keyword_init: true)
137
-
138
- def initialize(path:)
139
- @path = path
140
- self.content = ""
141
- end
142
-
143
- def content=(content)
144
- @content_updated_at = Time.now
145
- @content = content
146
- @status = nil
147
- end
148
-
149
- def load!
150
- buffer = RBS::Buffer.new(name: path, content: content)
151
- decls = RBS::Parser.parse_signature(buffer)
152
- @status = DeclarationsStatus.new(declarations: decls)
153
- rescue RBS::Parser::SyntaxError, RBS::Parser::SemanticsError => exn
154
- @status = ParseErrorStatus.new(error: exn)
124
+ @status = ParseErrorStatus.new(error: exn, timestamp: now)
155
125
  end
156
126
  end
157
127
  end