steep 0.27.0 → 0.31.1

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