steep 0.27.0 → 0.31.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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +38 -0
  3. data/bin/smoke_runner.rb +3 -4
  4. data/bin/steep-prof +1 -2
  5. data/lib/steep.rb +6 -4
  6. data/lib/steep/annotation_parser.rb +2 -4
  7. data/lib/steep/ast/builtin.rb +11 -21
  8. data/lib/steep/ast/types/factory.rb +234 -101
  9. data/lib/steep/ast/types/intersection.rb +12 -9
  10. data/lib/steep/ast/types/logic.rb +63 -0
  11. data/lib/steep/ast/types/name.rb +2 -58
  12. data/lib/steep/ast/types/union.rb +5 -6
  13. data/lib/steep/errors.rb +14 -0
  14. data/lib/steep/interface/interface.rb +5 -62
  15. data/lib/steep/interface/method_type.rb +346 -75
  16. data/lib/steep/interface/substitution.rb +16 -4
  17. data/lib/steep/module_helper.rb +25 -0
  18. data/lib/steep/project.rb +25 -0
  19. data/lib/steep/project/completion_provider.rb +57 -58
  20. data/lib/steep/project/file_loader.rb +7 -2
  21. data/lib/steep/project/hover_content.rb +92 -83
  22. data/lib/steep/project/signature_file.rb +33 -0
  23. data/lib/steep/project/{file.rb → source_file.rb} +24 -54
  24. data/lib/steep/project/target.rb +31 -12
  25. data/lib/steep/server/base_worker.rb +1 -0
  26. data/lib/steep/server/code_worker.rb +31 -45
  27. data/lib/steep/server/interaction_worker.rb +42 -38
  28. data/lib/steep/server/master.rb +23 -33
  29. data/lib/steep/server/utils.rb +46 -13
  30. data/lib/steep/server/worker_process.rb +4 -2
  31. data/lib/steep/signature/validator.rb +3 -3
  32. data/lib/steep/source.rb +60 -3
  33. data/lib/steep/subtyping/check.rb +34 -47
  34. data/lib/steep/subtyping/constraints.rb +8 -0
  35. data/lib/steep/type_construction.rb +366 -365
  36. data/lib/steep/type_inference/block_params.rb +5 -0
  37. data/lib/steep/type_inference/constant_env.rb +2 -5
  38. data/lib/steep/type_inference/logic_type_interpreter.rb +219 -0
  39. data/lib/steep/type_inference/type_env.rb +2 -2
  40. data/lib/steep/version.rb +1 -1
  41. data/smoke/alias/a.rb +1 -1
  42. data/smoke/case/a.rb +1 -1
  43. data/smoke/if/a.rb +1 -1
  44. data/smoke/module/a.rb +1 -1
  45. data/smoke/rescue/a.rb +4 -13
  46. data/smoke/toplevel/Steepfile +5 -0
  47. data/smoke/toplevel/a.rb +4 -0
  48. data/smoke/toplevel/a.rbs +3 -0
  49. data/smoke/type_case/a.rb +0 -7
  50. data/steep.gemspec +3 -3
  51. metadata +20 -16
  52. data/lib/steep/ast/method_type.rb +0 -126
  53. data/lib/steep/ast/namespace.rb +0 -80
  54. data/lib/steep/names.rb +0 -86
@@ -14,6 +14,8 @@ module Steep
14
14
  attr_reader :signature_worker
15
15
  attr_reader :code_workers
16
16
 
17
+ include Utils
18
+
17
19
  def initialize(project:, reader:, writer:, interaction_worker:, signature_worker:, code_workers:, queue: Queue.new)
18
20
  @project = project
19
21
  @reader = reader
@@ -23,23 +25,28 @@ module Steep
23
25
  @signature_worker = signature_worker
24
26
  @code_workers = code_workers
25
27
  @worker_to_paths = {}
26
- @shutdown = false
28
+ @shutdown_request_id = nil
27
29
  end
28
30
 
29
31
  def start
30
- source_paths = project.targets.flat_map {|target| target.source_files.keys }
32
+ source_paths = project.all_source_files
31
33
  bin_size = (source_paths.size / code_workers.size) + 1
32
34
  source_paths.each_slice(bin_size).with_index do |paths, index|
33
35
  register_code_to_worker(paths, worker: code_workers[index])
34
36
  end
35
37
 
38
+ tags = Steep.logger.formatter.current_tags.dup
39
+ tags << "master"
40
+
36
41
  Thread.new do
42
+ Steep.logger.formatter.push_tags(*tags, "from-worker@interaction")
37
43
  interaction_worker.reader.read do |message|
38
44
  process_message_from_worker(message)
39
45
  end
40
46
  end
41
47
 
42
48
  Thread.new do
49
+ Steep.logger.formatter.push_tags(*tags, "from-worker@signature")
43
50
  signature_worker.reader.read do |message|
44
51
  process_message_from_worker(message)
45
52
  end
@@ -47,6 +54,7 @@ module Steep
47
54
 
48
55
  code_workers.each do |worker|
49
56
  Thread.new do
57
+ Steep.logger.formatter.push_tags(*tags, "from-worker@#{worker.name}")
50
58
  worker.reader.read do |message|
51
59
  process_message_from_worker(message)
52
60
  end
@@ -54,13 +62,21 @@ module Steep
54
62
  end
55
63
 
56
64
  Thread.new do
65
+ Steep.logger.formatter.push_tags(*tags, "from-client")
57
66
  reader.read do |request|
58
67
  process_message_from_client(request)
59
68
  end
60
69
  end
61
70
 
62
71
  while job = queue.pop
63
- writer.write(job) unless @shutdown
72
+ if @shutdown_request_id
73
+ if job[:id] == @shutdown_request_id
74
+ writer.write(job)
75
+ break
76
+ end
77
+ else
78
+ writer.write(job)
79
+ end
64
80
  end
65
81
 
66
82
  writer.io.close
@@ -90,7 +106,7 @@ module Steep
90
106
  result: LSP::Interface::InitializeResult.new(
91
107
  capabilities: LSP::Interface::ServerCapabilities.new(
92
108
  text_document_sync: LSP::Interface::TextDocumentSyncOptions.new(
93
- change: LSP::Constant::TextDocumentSyncKind::FULL
109
+ change: LSP::Constant::TextDocumentSyncKind::INCREMENTAL
94
110
  ),
95
111
  hover_provider: true,
96
112
  completion_provider: LSP::Interface::CompletionOptions.new(
@@ -105,36 +121,10 @@ module Steep
105
121
  end
106
122
 
107
123
  when "textDocument/didChange"
124
+ update_source(message)
125
+
108
126
  uri = URI.parse(message[:params][:textDocument][:uri])
109
127
  path = project.relative_path(Pathname(uri.path))
110
- text = message[:params][:contentChanges][0][:text]
111
-
112
- project.targets.each do |target|
113
- case
114
- when target.source_file?(path)
115
- if text.empty? && !path.file?
116
- Steep.logger.info { "Deleting source file: #{path}..." }
117
- target.remove_source(path)
118
- else
119
- Steep.logger.info { "Updating source file: #{path}..." }
120
- target.update_source(path, text)
121
- end
122
- when target.possible_source_file?(path)
123
- Steep.logger.info { "Adding source file: #{path}..." }
124
- target.add_source(path, text)
125
- when target.signature_file?(path)
126
- if text.empty? && !path.file?
127
- Steep.logger.info { "Deleting signature file: #{path}..." }
128
- target.remove_signature(path)
129
- else
130
- Steep.logger.info { "Updating signature file: #{path}..." }
131
- target.update_signature(path, text)
132
- end
133
- when target.possible_signature_file?(path)
134
- Steep.logger.info { "Adding signature file: #{path}..." }
135
- target.add_signature(path, text)
136
- end
137
- end
138
128
 
139
129
  unless registered_path?(path)
140
130
  register_code_to_worker [path], worker: least_busy_worker()
@@ -155,7 +145,7 @@ module Steep
155
145
 
156
146
  when "shutdown"
157
147
  queue << { id: id, result: nil }
158
- @shutdown = true
148
+ @shutdown_request_id = id
159
149
 
160
150
  when "exit"
161
151
  queue << nil
@@ -7,23 +7,56 @@ module Steep
7
7
  project.relative_path(Pathname(uri.path))
8
8
  end
9
9
 
10
+ def apply_change(change, text)
11
+ range = change[:range]
12
+
13
+ if range
14
+ text = text.dup
15
+
16
+ buf = AST::Buffer.new(name: :_, content: text)
17
+
18
+ start_pos = buf.loc_to_pos(range[:start].yield_self {|pos| [pos[:line]+1, pos[:character]] })
19
+ end_pos = buf.loc_to_pos(range[:end].yield_self {|pos| [pos[:line]+1, pos[:character]] })
20
+
21
+ text[start_pos...end_pos] = change[:text]
22
+ text
23
+ else
24
+ change[:text]
25
+ end
26
+ end
27
+
10
28
  def update_source(request)
11
29
  path = source_path(URI.parse(request[:params][:textDocument][:uri]))
12
- text = request[:params][:contentChanges][0][:text]
13
30
  version = request[:params][:textDocument][:version]
31
+ Steep.logger.info { "Updating source: path=#{path}, version=#{version}..." }
32
+
33
+ changes = request[:params][:contentChanges]
34
+
35
+ source_target, signature_targets = project.targets_for_path(path)
36
+
37
+ if source_target
38
+ changes.each do |change|
39
+ case
40
+ when source_target.source_file?(path)
41
+ Steep.logger.debug { "Updating source in #{source_target.name}: path=#{path}" }
42
+ source_target.update_source(path) {|text| apply_change(change, text) }
43
+ when source_target.possible_source_file?(path)
44
+ Steep.logger.debug { "Adding source to #{source_target.name}: path=#{path}" }
45
+ source_target.add_source(path, change[:text])
46
+ end
47
+ end
48
+ end
14
49
 
15
- Steep.logger.debug "Updateing source: path=#{path}, version=#{version}, size=#{text.bytesize}"
16
-
17
- project.targets.each do |target|
18
- case
19
- when target.source_file?(path)
20
- target.update_source path, text
21
- when target.possible_source_file?(path)
22
- target.add_source path, text
23
- when target.signature_file?(path)
24
- target.update_signature path, text
25
- when target.possible_signature_file?(path)
26
- target.add_signature path, text
50
+ signature_targets.each do |target|
51
+ changes.each do |change|
52
+ case
53
+ when target.signature_file?(path)
54
+ Steep.logger.debug { "Updating signature in #{target.name}: path=#{path}" }
55
+ target.update_signature(path) {|text| apply_change(change, text) }
56
+ when target.possible_signature_file?(path)
57
+ Steep.logger.debug { "Adding signature to #{target.name}: path=#{path}" }
58
+ target.add_signature(path, change[:text])
59
+ end
27
60
  end
28
61
  end
29
62
 
@@ -5,13 +5,15 @@ module Steep
5
5
  attr_reader :writer
6
6
  attr_reader :stderr
7
7
 
8
+ attr_reader :name
8
9
  attr_reader :wait_thread
9
10
 
10
- def initialize(reader:, writer:, stderr:, wait_thread:)
11
+ def initialize(reader:, writer:, stderr:, wait_thread:, name:)
11
12
  @reader = reader
12
13
  @writer = writer
13
14
  @stderr = stderr
14
15
  @wait_thread = wait_thread
16
+ @name = name
15
17
  end
16
18
 
17
19
  def self.spawn_worker(type, name:, steepfile:)
@@ -33,7 +35,7 @@ module Steep
33
35
  writer = LanguageServer::Protocol::Transport::Io::Writer.new(stdin)
34
36
  reader = LanguageServer::Protocol::Transport::Io::Reader.new(stdout)
35
37
 
36
- new(reader: reader, writer: writer, stderr: stderr, wait_thread: thread)
38
+ new(reader: reader, writer: writer, stderr: stderr, wait_thread: thread, name: name)
37
39
  end
38
40
 
39
41
  def self.spawn_code_workers(steepfile:, count: [Etc.nprocessors-3, 1].max)
@@ -130,19 +130,19 @@ module Steep
130
130
  yield
131
131
  rescue RBS::InvalidTypeApplicationError => exn
132
132
  @errors << Errors::InvalidTypeApplicationError.new(
133
- name: factory.type_name(exn.type_name),
133
+ name: exn.type_name,
134
134
  args: exn.args.map {|ty| factory.type(ty) },
135
135
  params: exn.params,
136
136
  location: exn.location
137
137
  )
138
138
  rescue RBS::NoTypeFoundError, RBS::NoSuperclassFoundError, RBS::NoMixinFoundError => exn
139
139
  @errors << Errors::UnknownTypeNameError.new(
140
- name: factory.type_name(exn.type_name),
140
+ name: exn.type_name,
141
141
  location: exn.location
142
142
  )
143
143
  rescue RBS::InvalidOverloadMethodError => exn
144
144
  @errors << Errors::InvalidMethodOverloadError.new(
145
- class_name: factory.type_name(exn.type_name),
145
+ class_name: exn.type_name,
146
146
  method_name: exn.method_name,
147
147
  location: exn.members[0].location
148
148
  )
@@ -38,7 +38,7 @@ module Steep
38
38
  end
39
39
 
40
40
  def self.parser
41
- ::Parser::Ruby25.new(Builder.new).tap do |parser|
41
+ ::Parser::Ruby27.new(Builder.new).tap do |parser|
42
42
  parser.diagnostics.all_errors_are_fatal = true
43
43
  parser.diagnostics.ignore_warnings = true
44
44
  end
@@ -60,7 +60,7 @@ module Steep
60
60
  _, comments, _ = yield_self do
61
61
  buffer = ::Parser::Source::Buffer.new(path.to_s)
62
62
  buffer.source = source_code
63
- parser = ::Parser::Ruby25.new
63
+ parser = ::Parser::Ruby27.new
64
64
 
65
65
  parser.tokenize(buffer)
66
66
  end
@@ -107,7 +107,7 @@ module Steep
107
107
 
108
108
  if node.children[1]
109
109
  if node.loc.keyword.source == "if" || node.loc.keyword.source == "elsif"
110
- then_start = node.loc.begin&.loc&.last_line || node.children[0].loc.last_line
110
+ then_start = node.loc.begin&.last_line || node.children[0].loc.last_line
111
111
  then_end = node.children[2] ? node.loc.else.line : node.loc.last_line
112
112
  else
113
113
  then_start = node.loc.else.last_line
@@ -276,6 +276,18 @@ module Steep
276
276
  end
277
277
  end
278
278
 
279
+ def self.map_child_nodes(node)
280
+ children = node.children.map do |child|
281
+ if child.is_a?(::AST::Node)
282
+ yield child
283
+ else
284
+ child
285
+ end
286
+ end
287
+
288
+ node.updated(nil, children)
289
+ end
290
+
279
291
  def annotations(block:, factory:, current_module:)
280
292
  AST::Annotation::Collection.new(
281
293
  annotations: mapping[block.__id__] || [],
@@ -316,5 +328,50 @@ module Steep
316
328
  end
317
329
  end
318
330
  end
331
+
332
+ def self.delete_defs(node, allow_list)
333
+ case node.type
334
+ when :def
335
+ if allow_list.include?(node)
336
+ node
337
+ else
338
+ node.updated(:nil, [])
339
+ end
340
+ when :defs
341
+ if allow_list.include?(node)
342
+ node
343
+ else
344
+ delete_defs(node.children[0], allow_list)
345
+ end
346
+ else
347
+ map_child_nodes(node) do |child|
348
+ delete_defs(child, allow_list)
349
+ end
350
+ end
351
+ end
352
+
353
+ def without_unrelated_defs(line:, column:)
354
+ nodes = find_nodes(line: line, column: column) || []
355
+ defs = Set[].compare_by_identity.merge(nodes.select {|node| node.type == :def || node.type == :defs })
356
+
357
+ node_ = Source.delete_defs(node, defs)
358
+
359
+ Source.new(path: path, node: node_, mapping: mapping)
360
+ end
361
+
362
+ def compact_siblings(node)
363
+ case node
364
+ when :def
365
+ node.updated(:nil, [])
366
+ when :defs
367
+ node.children[0]
368
+ when :class
369
+ node.updated(:class, [node.children[0], node.children[1], nil])
370
+ when :module
371
+ node.updated(:module, [node.children[0], nil])
372
+ else
373
+ node
374
+ end
375
+ end
319
376
  end
320
377
  end
@@ -10,8 +10,7 @@ module Steep
10
10
  end
11
11
 
12
12
  def instance_super_types(type_name, args:)
13
- type_name_1 = factory.type_name_1(type_name)
14
- ancestors = factory.definition_builder.one_instance_ancestors(type_name_1)
13
+ ancestors = factory.definition_builder.one_instance_ancestors(type_name)
15
14
 
16
15
  subst = unless args.empty?
17
16
  args_ = args.map {|type| factory.type_1(type) }
@@ -19,7 +18,7 @@ module Steep
19
18
  end
20
19
 
21
20
  ancestors.each_ancestor.map do |ancestor|
22
- name = factory.type_name(ancestor.name)
21
+ name = ancestor.name
23
22
 
24
23
  case ancestor
25
24
  when RBS::Definition::Ancestor::Instance
@@ -42,9 +41,8 @@ module Steep
42
41
  )
43
42
  end
44
43
  when RBS::Definition::Ancestor::Singleton
45
- AST::Types::Name::Class.new(
44
+ AST::Types::Name::Singleton.new(
46
45
  name: name,
47
- constructor: nil,
48
46
  location: nil
49
47
  )
50
48
  end
@@ -52,11 +50,10 @@ module Steep
52
50
  end
53
51
 
54
52
  def singleton_super_types(type_name)
55
- type_name_1 = factory.type_name_1(type_name)
56
- ancestors = factory.definition_builder.one_singleton_ancestors(type_name_1)
53
+ ancestors = factory.definition_builder.one_singleton_ancestors(type_name)
57
54
 
58
55
  ancestors.each_ancestor.map do |ancestor|
59
- name = factory.type_name(ancestor.name)
56
+ name = ancestor.name
60
57
 
61
58
  case ancestor
62
59
  when RBS::Definition::Ancestor::Instance
@@ -78,9 +75,8 @@ module Steep
78
75
  )
79
76
  end
80
77
  when RBS::Definition::Ancestor::Singleton
81
- AST::Types::Name::Class.new(
78
+ AST::Types::Name::Singleton.new(
82
79
  name: name,
83
- constructor: nil,
84
80
  location: nil
85
81
  )
86
82
  end
@@ -180,6 +176,14 @@ module Steep
180
176
  constraints: constraints
181
177
  )
182
178
 
179
+ when relation.super_type.is_a?(AST::Types::Var) && constraints.unknown?(relation.super_type.name)
180
+ constraints.add(relation.super_type.name, sub_type: relation.sub_type)
181
+ success(constraints: constraints)
182
+
183
+ when relation.sub_type.is_a?(AST::Types::Var) && constraints.unknown?(relation.sub_type.name)
184
+ constraints.add(relation.sub_type.name, super_type: relation.super_type)
185
+ success(constraints: constraints)
186
+
183
187
  when relation.sub_type.is_a?(AST::Types::Union)
184
188
  results = relation.sub_type.types.map do |sub_type|
185
189
  check(Relation.new(sub_type: sub_type, super_type: relation.super_type),
@@ -232,14 +236,6 @@ module Steep
232
236
  results.find(&:failure?)
233
237
  end
234
238
 
235
- when relation.super_type.is_a?(AST::Types::Var) && constraints.unknown?(relation.super_type.name)
236
- constraints.add(relation.super_type.name, sub_type: relation.sub_type)
237
- success(constraints: constraints)
238
-
239
- when relation.sub_type.is_a?(AST::Types::Var) && constraints.unknown?(relation.sub_type.name)
240
- constraints.add(relation.sub_type.name, super_type: relation.super_type)
241
- success(constraints: constraints)
242
-
243
239
  when relation.super_type.is_a?(AST::Types::Var) || relation.sub_type.is_a?(AST::Types::Var)
244
240
  failure(error: Result::Failure::UnknownPairError.new(relation: relation),
245
241
  trace: trace)
@@ -266,7 +262,7 @@ module Steep
266
262
  possible_sub_types = case relation.sub_type
267
263
  when AST::Types::Name::Instance
268
264
  instance_super_types(relation.sub_type.name, args: relation.sub_type.args)
269
- when AST::Types::Name::Class
265
+ when AST::Types::Name::Singleton
270
266
  singleton_super_types(relation.sub_type.name)
271
267
  else
272
268
  []
@@ -394,12 +390,12 @@ module Steep
394
390
  end
395
391
 
396
392
  def definition_for_type(type)
397
- type_name = factory.type_name_1(type.name)
393
+ type_name = type.name
398
394
 
399
395
  case type
400
396
  when AST::Types::Name::Instance
401
397
  factory.definition_builder.build_instance(type_name)
402
- when AST::Types::Name::Class
398
+ when AST::Types::Name::Singleton
403
399
  factory.definition_builder.build_singleton(type_name)
404
400
  when AST::Types::Name::Interface
405
401
  factory.definition_builder.build_interface(type_name)
@@ -484,11 +480,7 @@ module Steep
484
480
  if sub_type.name == super_type.name && sub_type.args.size == super_type.args.size
485
481
  sub_type.args.zip(super_type.args)
486
482
  end
487
- when sub_type.is_a?(AST::Types::Name::Class) && super_type.is_a?(AST::Types::Name::Class)
488
- if sub_type.name == super_type.name
489
- []
490
- end
491
- when sub_type.is_a?(AST::Types::Name::Module) && super_type.is_a?(AST::Types::Name::Module)
483
+ when sub_type.is_a?(AST::Types::Name::Singleton) && super_type.is_a?(AST::Types::Name::Singleton)
492
484
  if sub_type.name == super_type.name
493
485
  []
494
486
  end
@@ -535,29 +527,24 @@ module Steep
535
527
 
536
528
  def check_method(name, sub_method, super_method, self_type:, assumption:, trace:, constraints:)
537
529
  trace.method name, sub_method, super_method do
538
- case
539
- when sub_method.overload? && super_method.overload?
540
- super_method.types.map do |super_type|
541
- sub_method.types.map do |sub_type|
542
- check_generic_method_type name,
543
- sub_type,
544
- super_type,
545
- self_type: self_type,
546
- assumption: assumption,
547
- trace: trace,
548
- constraints: constraints
549
- end.yield_self do |results|
550
- results.find(&:success?) || results[0]
551
- end
530
+ super_method.method_types.map do |super_type|
531
+ sub_method.method_types.map do |sub_type|
532
+ check_generic_method_type name,
533
+ sub_type,
534
+ super_type,
535
+ self_type: self_type,
536
+ assumption: assumption,
537
+ trace: trace,
538
+ constraints: constraints
552
539
  end.yield_self do |results|
553
- if results.all?(&:success?) || sub_method.incompatible?
554
- success constraints: constraints
555
- else
556
- results.select(&:failure?).last
557
- end
540
+ results.find(&:success?) || results[0]
541
+ end
542
+ end.yield_self do |results|
543
+ if results.all?(&:success?)
544
+ success constraints: constraints
545
+ else
546
+ results.select(&:failure?).last
558
547
  end
559
- else
560
- raise "aaaaaaaaaaaaaa"
561
548
  end
562
549
  end
563
550
  end