steep 1.4.0.dev.1 → 1.4.0.dev.3

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 (96) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +1 -2
  3. data/Gemfile +2 -2
  4. data/Gemfile.lock +13 -15
  5. data/Gemfile.steep +1 -2
  6. data/Gemfile.steep.lock +20 -18
  7. data/README.md +7 -1
  8. data/Steepfile +16 -3
  9. data/bin/rbs +0 -1
  10. data/guides/README.md +5 -0
  11. data/guides/src/gem-rbs-collection/gem-rbs-collection.md +143 -0
  12. data/guides/src/getting-started/getting-started.md +164 -0
  13. data/guides/src/nil-optional/nil-optional.md +195 -0
  14. data/lib/steep/annotation_parser.rb +40 -20
  15. data/lib/steep/ast/types/factory.rb +56 -10
  16. data/lib/steep/ast/types/name.rb +10 -0
  17. data/lib/steep/diagnostic/ruby.rb +80 -5
  18. data/lib/steep/diagnostic/signature.rb +40 -0
  19. data/lib/steep/drivers/check.rb +4 -4
  20. data/lib/steep/index/rbs_index.rb +12 -3
  21. data/lib/steep/index/signature_symbol_provider.rb +1 -1
  22. data/lib/steep/interface/block.rb +10 -0
  23. data/lib/steep/module_helper.rb +13 -11
  24. data/lib/steep/path_helper.rb +4 -0
  25. data/lib/steep/project/target.rb +1 -3
  26. data/lib/steep/server/interaction_worker.rb +102 -72
  27. data/lib/steep/server/lsp_formatter.rb +14 -5
  28. data/lib/steep/services/completion_provider.rb +10 -12
  29. data/lib/steep/services/goto_service.rb +15 -14
  30. data/lib/steep/services/hover_provider/rbs.rb +29 -9
  31. data/lib/steep/services/hover_provider/ruby.rb +16 -10
  32. data/lib/steep/services/signature_service.rb +36 -39
  33. data/lib/steep/services/type_name_completion.rb +157 -0
  34. data/lib/steep/signature/validator.rb +28 -6
  35. data/lib/steep/source.rb +1 -0
  36. data/lib/steep/subtyping/check.rb +1 -1
  37. data/lib/steep/type_construction.rb +414 -239
  38. data/lib/steep/type_inference/block_params.rb +13 -0
  39. data/lib/steep/type_inference/constant_env.rb +7 -3
  40. data/lib/steep/type_inference/context.rb +3 -3
  41. data/lib/steep/type_inference/method_params.rb +42 -16
  42. data/lib/steep/type_inference/send_args.rb +79 -50
  43. data/lib/steep/type_inference/type_env.rb +7 -1
  44. data/lib/steep/version.rb +1 -1
  45. data/lib/steep.rb +1 -0
  46. data/rbs_collection.steep.lock.yaml +9 -41
  47. data/rbs_collection.steep.yaml +11 -8
  48. data/sample/lib/conference.rb +22 -0
  49. data/sample/sig/conference.rbs +28 -0
  50. data/sig/shims/language-server_protocol.rbs +12 -0
  51. data/sig/shims/parser/nodes.rbs +37 -0
  52. data/sig/shims/parser.rbs +1 -0
  53. data/sig/shims/string.rbs +4 -0
  54. data/sig/steep/annotation_parser.rbs +3 -2
  55. data/sig/steep/ast/annotation/collection.rbs +1 -1
  56. data/sig/steep/ast/types/factory.rbs +12 -8
  57. data/sig/steep/ast/types/name.rbs +4 -0
  58. data/sig/steep/diagnostic/lsp_formatter.rbs +1 -1
  59. data/sig/steep/diagnostic/ruby.rbs +38 -2
  60. data/sig/steep/diagnostic/signature.rbs +18 -14
  61. data/sig/steep/drivers/check.rbs +1 -1
  62. data/sig/steep/drivers/checkfile.rbs +1 -1
  63. data/sig/steep/drivers/diagnostic_printer.rbs +1 -1
  64. data/sig/steep/drivers/watch.rbs +1 -1
  65. data/sig/steep/index/rbs_index.rbs +6 -2
  66. data/sig/steep/index/signature_symbol_provider.rbs +1 -1
  67. data/sig/steep/interface/block.rbs +2 -0
  68. data/sig/steep/interface/builder.rbs +5 -3
  69. data/sig/steep/interface/method_type.rbs +5 -3
  70. data/sig/steep/module_helper.rbs +9 -0
  71. data/sig/steep/path_helper.rbs +3 -1
  72. data/sig/steep/project/target.rbs +7 -7
  73. data/sig/steep/server/base_worker.rbs +1 -1
  74. data/sig/steep/server/interaction_worker.rbs +46 -17
  75. data/sig/steep/server/lsp_formatter.rbs +4 -2
  76. data/sig/steep/server/master.rbs +1 -1
  77. data/sig/steep/server/type_check_worker.rbs +7 -5
  78. data/sig/steep/server/worker_process.rbs +6 -4
  79. data/sig/steep/services/completion_provider.rbs +8 -0
  80. data/sig/steep/services/hover_provider/rbs.rbs +6 -4
  81. data/sig/steep/services/hover_provider/ruby.rbs +8 -4
  82. data/sig/steep/services/signature_service.rbs +27 -3
  83. data/sig/steep/services/type_name_completion.rbs +122 -0
  84. data/sig/steep/signature/validator.rbs +9 -5
  85. data/sig/steep/type_construction.rbs +100 -31
  86. data/sig/steep/type_inference/block_params.rbs +4 -0
  87. data/sig/steep/type_inference/constant_env.rbs +2 -0
  88. data/sig/steep/type_inference/context.rbs +70 -22
  89. data/sig/steep/type_inference/method_params.rbs +43 -24
  90. data/sig/steep/type_inference/multiple_assignment.rbs +1 -1
  91. data/sig/steep/type_inference/send_args.rbs +13 -3
  92. data/sig/steep/typing.rbs +7 -2
  93. data/smoke/diagnostics/test_expectations.yml +1 -0
  94. data/smoke/regexp/a.rb +2 -2
  95. data/steep.gemspec +0 -1
  96. metadata +11 -17
@@ -75,7 +75,7 @@ module Steep
75
75
  end
76
76
  end
77
77
 
78
- FileStatus = _ = Struct.new(:path, :content, :decls, keyword_init: true)
78
+ FileStatus = _ = Struct.new(:path, :content, :signature, keyword_init: true)
79
79
 
80
80
  def initialize(env:)
81
81
  builder = RBS::DefinitionBuilder.new(env: env)
@@ -150,7 +150,7 @@ module Steep
150
150
  def apply_changes(files, changes)
151
151
  Steep.logger.tagged "#apply_changes" do
152
152
  Steep.measure2 "Applying change" do |sampler|
153
- changes.each.with_object({}) do |pair, update|
153
+ changes.each.with_object({}) do |pair, update| # $ Hash[Pathname, FileStatus]
154
154
  path, cs = pair
155
155
  sampler.sample "#{path}" do
156
156
  old_text = files[path]&.content
@@ -158,17 +158,18 @@ module Steep
158
158
 
159
159
  buffer = RBS::Buffer.new(name: path, content: content)
160
160
 
161
- update[path] = begin
162
- FileStatus.new(path: path, content: content, decls: RBS::Parser.parse_signature(buffer))
163
- rescue ArgumentError => exn
164
- error = Diagnostic::Signature::UnexpectedError.new(
165
- message: exn.message,
166
- location: RBS::Location.new(buffer: buffer, start_pos: 0, end_pos: content.size)
167
- )
168
- FileStatus.new(path: path, content: content, decls: error)
169
- rescue RBS::ParsingError => exn
170
- FileStatus.new(path: path, content: content, decls: exn)
171
- end
161
+ update[path] =
162
+ begin
163
+ FileStatus.new(path: path, content: content, signature: RBS::Parser.parse_signature(buffer))
164
+ rescue ArgumentError => exn
165
+ error = Diagnostic::Signature::UnexpectedError.new(
166
+ message: exn.message,
167
+ location: RBS::Location.new(buffer: buffer, start_pos: 0, end_pos: content.size)
168
+ )
169
+ FileStatus.new(path: path, content: content, signature: error)
170
+ rescue RBS::ParsingError => exn
171
+ FileStatus.new(path: path, content: content, signature: exn)
172
+ end
172
173
  end
173
174
  end
174
175
  end
@@ -181,16 +182,16 @@ module Steep
181
182
  paths = Set.new(updates.each_key)
182
183
  paths.merge(pending_changed_paths)
183
184
 
184
- if updates.each_value.any? {|file| !file.decls.is_a?(Array) }
185
- diagnostics = []
185
+ if updates.each_value.any? {|file| !file.signature.is_a?(Array) }
186
+ diagnostics = [] #: Array[Diagnostic::Signature::Base]
186
187
 
187
188
  updates.each_value do |file|
188
- unless file.decls.is_a?(Array)
189
- diagnostic = if file.decls.is_a?(Diagnostic::Signature::Base)
190
- file.decls
189
+ unless file.signature.is_a?(Array)
190
+ diagnostic = if file.signature.is_a?(Diagnostic::Signature::Base)
191
+ file.signature
191
192
  else
192
193
  # factory is not used here because the error is a syntax error.
193
- Diagnostic::Signature.from_rbs_error(file.decls, factory: _ = nil)
194
+ Diagnostic::Signature.from_rbs_error(file.signature, factory: _ = nil)
194
195
  end
195
196
  diagnostics << diagnostic
196
197
  end
@@ -204,9 +205,7 @@ module Steep
204
205
  )
205
206
  else
206
207
  files = self.files.merge(updates)
207
- updated_files = paths.each_with_object({}) do |path, hash|
208
- hash[path] = files[path]
209
- end
208
+ updated_files = files.slice(*paths.to_a)
210
209
  result =
211
210
  Steep.measure "#update_env with updated #{paths.size} files" do
212
211
  update_env(updated_files, paths: paths)
@@ -229,33 +228,29 @@ module Steep
229
228
  end
230
229
 
231
230
  def update_env(updated_files, paths:)
231
+
232
232
  Steep.logger.tagged "#update_env" do
233
- # @type var errors: Array[RBS::BaseError]
234
- errors = []
235
- new_decls = Set[].compare_by_identity
233
+ errors = [] #: Array[RBS::BaseError]
234
+ new_decls = Set[].compare_by_identity #: Set[RBS::AST::Declarations::t]
236
235
 
237
236
  env =
238
237
  Steep.measure "Deleting out of date decls" do
239
- latest_env.reject do |decl|
240
- if decl.location
241
- paths.include?(decl.location.buffer.name)
242
- end
243
- end
238
+ bufs = latest_env.buffers.select {|buf| paths.include?(buf.name) }
239
+ latest_env.unload(Set.new(bufs))
244
240
  end
245
241
 
246
242
  Steep.measure "Loading new decls" do
247
243
  updated_files.each_value do |content|
248
- case decls = content.decls
249
- when RBS::BaseError
250
- errors << decls
244
+ case content.signature
245
+ when RBS::ParsingError
246
+ errors << content.signature
251
247
  when Diagnostic::Signature::UnexpectedError
252
- return [decls]
248
+ return [content.signature]
253
249
  else
254
250
  begin
255
- decls.each do |decl|
256
- env << decl
257
- new_decls << decl
258
- end
251
+ buffer, dirs, decls = content.signature
252
+ env.add_signature(buffer: buffer, directives: dirs, decls: decls)
253
+ new_decls.merge(decls)
259
254
  rescue RBS::LoadingError => exn
260
255
  errors << exn
261
256
  end
@@ -375,8 +370,10 @@ module Steep
375
370
  type_name_from_decl(member, set: set)
376
371
  end
377
372
  end
378
- when RBS::AST::Declarations::Alias
373
+ when RBS::AST::Declarations::TypeAlias
379
374
  set << decl.name
375
+ when RBS::AST::Declarations::ClassAlias, RBS::AST::Declarations::ModuleAlias
376
+ set << decl.new_name
380
377
  end
381
378
  end
382
379
 
@@ -0,0 +1,157 @@
1
+ module Steep
2
+ module Services
3
+ class TypeNameCompletion
4
+ module Prefix
5
+ RawIdentPrefix = _ = Struct.new(:ident) do
6
+ # @implements RawIdentPrefix
7
+
8
+ def const_name?
9
+ ident.start_with?(/[A-Z]/)
10
+ end
11
+
12
+ def size
13
+ ident.length
14
+ end
15
+ end
16
+
17
+ NamespacedIdentPrefix = _ = Struct.new(:namespace, :ident, :size) do
18
+ # @implements NamespacedIdentPrefix
19
+
20
+ def const_name?
21
+ ident.start_with?(/[A-Z]/)
22
+ end
23
+ end
24
+
25
+ NamespacePrefix = _ = Struct.new(:namespace, :size)
26
+
27
+ def self.parse(buffer, line:, column:)
28
+ pos = buffer.loc_to_pos([line, column])
29
+ prefix = buffer.content[0...pos] or raise
30
+ prefix.reverse!
31
+
32
+ case prefix
33
+ when /\A((::\w+[A-Z])+(::)?)/
34
+ NamespacePrefix.new(RBS::Namespace.parse($1.reverse), $1.size)
35
+ when /\A::/
36
+ NamespacePrefix.new(RBS::Namespace.root, 2)
37
+ when /\A(\w*[A-Za-z_])((::\w+[A-Z])+(::)?)/
38
+ NamespacedIdentPrefix.new(RBS::Namespace.parse($2.reverse), $1.reverse, $1.size + $2.size)
39
+ when /\A(\w*[A-Za-z_])::/
40
+ NamespacedIdentPrefix.new(RBS::Namespace.root, $1.reverse, $1.size + 2)
41
+ when /\A(\w*[A-Za-z_])/
42
+ RawIdentPrefix.new($1.reverse)
43
+ end
44
+ end
45
+ end
46
+
47
+ attr_reader :env, :context, :type_name_resolver, :map
48
+
49
+ def initialize(env:, context:, dirs:)
50
+ @env = env
51
+ @context = context
52
+
53
+ table = RBS::Environment::UseMap::Table.new()
54
+ table.known_types.merge(env.class_decls.keys)
55
+ table.known_types.merge(env.class_alias_decls.keys)
56
+ table.known_types.merge(env.type_alias_decls.keys)
57
+ table.known_types.merge(env.interface_decls.keys)
58
+ table.compute_children
59
+
60
+ @map = RBS::Environment::UseMap.new(table: table)
61
+ dirs.each do |dir|
62
+ dir.clauses.each do |clause|
63
+ @map.build_map(clause)
64
+ end
65
+ end
66
+
67
+ @type_name_resolver = RBS::Resolver::TypeNameResolver.new(env)
68
+ end
69
+
70
+ def each_outer_module(context = self.context, &block)
71
+ if block
72
+ if (parent, con = context)
73
+ namespace = each_outer_module(parent, &block)
74
+ case con
75
+ when false
76
+ namespace
77
+ when RBS::TypeName
78
+ ns = con.with_prefix(namespace).to_namespace
79
+ yield(ns)
80
+ ns
81
+ end
82
+ else
83
+ yield(RBS::Namespace.root)
84
+ RBS::Namespace.root
85
+ end
86
+ else
87
+ enum_for :each_outer_module
88
+ end
89
+ end
90
+
91
+ def each_type_name(&block)
92
+ if block
93
+ map.instance_eval do
94
+ @map.each_key do |name|
95
+ yield RBS::TypeName.new(name: name, namespace: RBS::Namespace.empty)
96
+ end
97
+ end
98
+ env.class_decls.each_key(&block)
99
+ env.class_alias_decls.each_key(&block)
100
+ env.type_alias_decls.each_key(&block)
101
+ env.interface_decls.each_key(&block)
102
+ else
103
+ enum_for :each_type_name
104
+ end
105
+ end
106
+
107
+ def resolve_name_in_context(name)
108
+ if resolved_name = map.resolve?(name)
109
+ return [resolved_name, name]
110
+ end
111
+
112
+ name.absolute? or raise
113
+
114
+ name.namespace.path.reverse_each.inject(RBS::TypeName.new(namespace: RBS::Namespace.empty, name: name.name)) do |relative_name, component|
115
+ if type_name_resolver.resolve(relative_name, context: context) == name
116
+ return [name, relative_name]
117
+ end
118
+
119
+ RBS::TypeName.new(
120
+ namespace: RBS::Namespace.new(path: [component, *relative_name.namespace.path], absolute: false),
121
+ name: name.name
122
+ )
123
+ end
124
+
125
+ if type_name_resolver.resolve(name.relative!, context: context) == name && !map.resolve?(name.relative!)
126
+ [name, name.relative!]
127
+ else
128
+ [name, name]
129
+ end
130
+ end
131
+
132
+ def find_type_names(prefix)
133
+ case prefix
134
+ when Prefix::RawIdentPrefix
135
+ each_type_name.filter do |type_name|
136
+ type_name.split.any? {|sym| sym.to_s.downcase.include?(prefix.ident.downcase) }
137
+ end
138
+ when Prefix::NamespacedIdentPrefix
139
+ absolute_namespace = type_name_resolver.resolve(prefix.namespace.to_type_name, context: context)&.to_namespace || prefix.namespace
140
+
141
+ each_type_name.filter do|name|
142
+ name.namespace == absolute_namespace &&
143
+ name.name.to_s.downcase.include?(prefix.ident.downcase)
144
+ end
145
+ when Prefix::NamespacePrefix
146
+ absolute_namespace = type_name_resolver.resolve(prefix.namespace.to_type_name, context: context)&.to_namespace || prefix.namespace
147
+ each_type_name.filter {|name| name.namespace == absolute_namespace }
148
+ else
149
+ # Returns all of the accessible type names from the context
150
+ namespaces = each_outer_module.to_set
151
+ # Relative type name means a *use*d type name
152
+ each_type_name.filter {|name| namespaces.include?(name.namespace) || !name.absolute? }
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
@@ -36,7 +36,7 @@ module Steep
36
36
  end
37
37
 
38
38
  def type_name_resolver
39
- @type_name_resolver ||= RBS::TypeNameResolver.from_env(env)
39
+ @type_name_resolver ||= RBS::Resolver::TypeNameResolver.new(env)
40
40
  end
41
41
 
42
42
  def validator
@@ -112,7 +112,7 @@ module Steep
112
112
  type.args
113
113
  ]
114
114
  when RBS::Types::Alias
115
- entry = env.alias_decls[type.name]
115
+ entry = env.type_alias_decls[type.name]
116
116
 
117
117
  [
118
118
  type.name,
@@ -135,7 +135,7 @@ module Steep
135
135
  def validate_type(type)
136
136
  Steep.logger.debug "#{Location.to_string type.location}: Validating #{type}..."
137
137
 
138
- validator.validate_type type, context: [RBS::Namespace.root]
138
+ validator.validate_type(type, context: nil)
139
139
  validate_type_application(type)
140
140
  end
141
141
 
@@ -235,7 +235,7 @@ module Steep
235
235
  end
236
236
  end
237
237
 
238
- def validate_one_class(name)
238
+ def validate_one_class_decl(name)
239
239
  rescue_validation_errors(name) do
240
240
  Steep.logger.debug { "Validating class definition `#{name}`..." }
241
241
 
@@ -390,6 +390,17 @@ module Steep
390
390
  end
391
391
  end
392
392
 
393
+ def validate_one_class(name)
394
+ entry = env.constant_entry(name)
395
+
396
+ case entry
397
+ when RBS::Environment::ClassEntry, RBS::Environment::ModuleEntry
398
+ validate_one_class_decl(name)
399
+ when RBS::Environment::ClassAliasEntry, RBS::Environment::ModuleAliasEntry
400
+ validate_one_class_alias(name, entry)
401
+ end
402
+ end
403
+
393
404
  def validate_ancestor_application(name, ancestor)
394
405
  unless ancestor.args.empty?
395
406
  definition =
@@ -465,6 +476,10 @@ module Steep
465
476
  validate_one_class(name)
466
477
  end
467
478
 
479
+ env.class_alias_decls.each do |name, entry|
480
+ validate_one_class_alias(name, entry)
481
+ end
482
+
468
483
  env.interface_decls.each_key do |name|
469
484
  validate_one_interface(name)
470
485
  end
@@ -497,7 +512,7 @@ module Steep
497
512
  end
498
513
  end
499
514
 
500
- def validate_one_alias(name, entry = env.alias_decls[name])
515
+ def validate_one_alias(name, entry = env.type_alias_decls[name])
501
516
  rescue_validation_errors(name) do
502
517
  Steep.logger.debug "Validating alias `#{name}`..."
503
518
  upper_bounds = entry.decl.type_params.each.with_object({}) do |param, bounds|
@@ -512,8 +527,15 @@ module Steep
512
527
  end
513
528
  end
514
529
 
530
+ def validate_one_class_alias(name, entry)
531
+ rescue_validation_errors(name) do
532
+ Steep.logger.debug "Validating class/module alias `#{name}`..."
533
+ validator.validate_class_alias(entry: entry)
534
+ end
535
+ end
536
+
515
537
  def validate_alias
516
- env.alias_decls.each do |name, entry|
538
+ env.type_alias_decls.each do |name, entry|
517
539
  validate_one_alias(name, entry)
518
540
  end
519
541
  end
data/lib/steep/source.rb CHANGED
@@ -20,6 +20,7 @@ module Steep
20
20
  self.emit_lambda = true
21
21
  self.emit_procarg0 = true
22
22
  self.emit_kwargs = true
23
+ self.emit_forward_arg = true
23
24
  end
24
25
 
25
26
  def self.new_parser
@@ -619,7 +619,7 @@ module Steep
619
619
  return true
620
620
  end
621
621
 
622
- relation.sub_type == relation.super_type
622
+ builder.factory.normalize_type(relation.sub_type) == builder.factory.normalize_type(relation.super_type)
623
623
  end
624
624
 
625
625
  def check_interface(relation)