steep 0.33.0 → 0.38.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +28 -0
  3. data/lib/steep.rb +9 -0
  4. data/lib/steep/annotation_parser.rb +1 -1
  5. data/lib/steep/ast/types/factory.rb +167 -102
  6. data/lib/steep/ast/types/logic.rb +20 -1
  7. data/lib/steep/ast/types/proc.rb +32 -20
  8. data/lib/steep/cli.rb +15 -2
  9. data/lib/steep/drivers/print_project.rb +11 -0
  10. data/lib/steep/drivers/stats.rb +85 -0
  11. data/lib/steep/drivers/vendor.rb +1 -20
  12. data/lib/steep/errors.rb +38 -15
  13. data/lib/steep/index/rbs_index.rb +334 -0
  14. data/lib/steep/index/signature_symbol_provider.rb +154 -0
  15. data/lib/steep/index/source_index.rb +100 -0
  16. data/lib/steep/interface/block.rb +79 -0
  17. data/lib/steep/interface/function.rb +770 -0
  18. data/lib/steep/interface/method_type.rb +41 -832
  19. data/lib/steep/method_name.rb +28 -0
  20. data/lib/steep/project/completion_provider.rb +24 -15
  21. data/lib/steep/project/dsl.rb +13 -17
  22. data/lib/steep/project/options.rb +4 -4
  23. data/lib/steep/project/source_file.rb +2 -1
  24. data/lib/steep/project/target.rb +19 -10
  25. data/lib/steep/server/interaction_worker.rb +1 -1
  26. data/lib/steep/server/master.rb +5 -1
  27. data/lib/steep/server/signature_worker.rb +63 -6
  28. data/lib/steep/subtyping/check.rb +70 -32
  29. data/lib/steep/subtyping/variable_occurrence.rb +4 -2
  30. data/lib/steep/subtyping/variable_variance.rb +2 -2
  31. data/lib/steep/type_construction.rb +780 -495
  32. data/lib/steep/type_inference/block_params.rb +1 -1
  33. data/lib/steep/type_inference/constant_env.rb +5 -1
  34. data/lib/steep/type_inference/context.rb +7 -3
  35. data/lib/steep/type_inference/local_variable_type_env.rb +10 -1
  36. data/lib/steep/type_inference/logic_type_interpreter.rb +3 -0
  37. data/lib/steep/type_inference/method_call.rb +116 -0
  38. data/lib/steep/typing.rb +46 -10
  39. data/lib/steep/version.rb +1 -1
  40. data/smoke/regression/range.rb +5 -0
  41. data/smoke/tsort/Steepfile +6 -0
  42. data/smoke/tsort/a.rb +15 -0
  43. data/steep.gemspec +1 -1
  44. metadata +17 -6
@@ -0,0 +1,28 @@
1
+ module Steep
2
+ InstanceMethodName = Struct.new(:type_name, :method_name, keyword_init: true) do
3
+ def to_s
4
+ "#{type_name}##{method_name}"
5
+ end
6
+ end
7
+
8
+ SingletonMethodName = Struct.new(:type_name, :method_name, keyword_init: true) do
9
+ def to_s
10
+ "#{type_name}.#{method_name}"
11
+ end
12
+ end
13
+
14
+ module ::Kernel
15
+ def MethodName(string)
16
+ case string
17
+ when /#/
18
+ type_name, method_name = string.split(/#/, 2)
19
+ InstanceMethodName.new(type_name: TypeName(type_name), method_name: method_name.to_sym)
20
+ when /\./
21
+ type_name, method_name = string.split(/\./, 2)
22
+ SingletonMethodName.new(type_name: TypeName(type_name), method_name: method_name.to_sym)
23
+ else
24
+ raise "Unexpected method name: #{string}"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -10,9 +10,27 @@ 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, :method_def, :method_type, :inherited_method, keyword_init: true) do
13
+ MethodNameItem = Struct.new(:identifier, :range, :receiver_type, :method_type, :method_decls, keyword_init: true) do
14
14
  def comment
15
- method_def&.comment
15
+ case method_decls.size
16
+ when 0
17
+ nil
18
+ when 1
19
+ method_decls.to_a.first.method_def&.comment
20
+ else
21
+ nil
22
+ end
23
+ end
24
+
25
+ def inherited?
26
+ case receiver_type
27
+ when AST::Types::Name::Instance, AST::Types::Name::Singleton, AST::Types::Name::Interface
28
+ method_decls.any? do |decl|
29
+ decl.method_name.type_name != receiver_type.name
30
+ end
31
+ else
32
+ false
33
+ end
16
34
  end
17
35
  end
18
36
 
@@ -245,15 +263,15 @@ module Steep
245
263
  items << MethodNameItem.new(
246
264
  identifier: name,
247
265
  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)
266
+ receiver_type: type,
267
+ method_type: subtyping.factory.method_type_1(method_type, self_type: type),
268
+ method_decls: method_type.method_decls
251
269
  )
252
270
  end
253
271
  end
254
272
  end
255
273
  end
256
- rescue
274
+ rescue RuntimeError => exn
257
275
  # nop
258
276
  end
259
277
 
@@ -298,15 +316,6 @@ module Steep
298
316
  index
299
317
  end
300
318
 
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
308
- end
309
-
310
319
  def disallowed_method?(name)
311
320
  # initialize isn't invoked by developers when creating
312
321
  # instances of new classes, so don't show it as
@@ -7,12 +7,12 @@ module Steep
7
7
  attr_reader :libraries
8
8
  attr_reader :signatures
9
9
  attr_reader :ignored_sources
10
- attr_reader :no_builtin
11
10
  attr_reader :vendor_dir
12
11
  attr_reader :strictness_level
13
12
  attr_reader :typing_option_hash
13
+ attr_reader :repo_paths
14
14
 
15
- def initialize(name, sources: [], libraries: [], signatures: [], ignored_sources: [])
15
+ def initialize(name, sources: [], libraries: [], signatures: [], ignored_sources: [], repo_paths: [])
16
16
  @name = name
17
17
  @sources = sources
18
18
  @libraries = libraries
@@ -21,6 +21,7 @@ module Steep
21
21
  @vendor_dir = nil
22
22
  @strictness_level = :default
23
23
  @typing_option_hash = {}
24
+ @repo_paths = []
24
25
  end
25
26
 
26
27
  def initialize_copy(other)
@@ -32,6 +33,7 @@ module Steep
32
33
  @vendor_dir = other.vendor_dir
33
34
  @strictness_level = other.strictness_level
34
35
  @typing_option_hash = other.typing_option_hash
36
+ @repo_paths = other.repo_paths.dup
35
37
  end
36
38
 
37
39
  def check(*args)
@@ -71,13 +73,14 @@ module Steep
71
73
 
72
74
  def vendor(dir = "vendor/sigs", stdlib: nil, gems: nil)
73
75
  if stdlib || gems
74
- @vendor_dir = [
75
- stdlib&.yield_self {|x| Pathname(x) },
76
- gems&.yield_self {|x| Pathname(x) }
77
- ]
78
- else
79
- @vendor_dir = Pathname(dir)
76
+ Steep.logger.warn { "#vendor with stdlib: or gems: keyword is deprecated." }
80
77
  end
78
+
79
+ @vendor_dir = Pathname(dir)
80
+ end
81
+
82
+ def repo_path(*paths)
83
+ @repo_paths.push(*paths.map {|s| Pathname(s) })
81
84
  end
82
85
  end
83
86
 
@@ -124,6 +127,8 @@ module Steep
124
127
  signature_patterns: target.signatures,
125
128
  options: Options.new.tap do |options|
126
129
  options.libraries.push(*target.libraries)
130
+ options.repository_paths.push(*target.repo_paths)
131
+ options.vendor_path = target.vendor_dir
127
132
 
128
133
  case target.strictness_level
129
134
  when :strict
@@ -133,15 +138,6 @@ module Steep
133
138
  end
134
139
 
135
140
  options.merge!(target.typing_option_hash)
136
-
137
- case target.vendor_dir
138
- when Array
139
- options.vendored_stdlib_path = target.vendor_dir[0]
140
- options.vendored_gems_path = target.vendor_dir[1]
141
- when Pathname
142
- options.vendored_stdlib_path = target.vendor_dir + "stdlib"
143
- options.vendored_gems_path = target.vendor_dir + "gems"
144
- end
145
141
  end
146
142
  ).tap do |target|
147
143
  project.targets << target
@@ -5,16 +5,16 @@ module Steep
5
5
  attr_accessor :allow_missing_definitions
6
6
  attr_accessor :allow_unknown_constant_assignment
7
7
  attr_accessor :allow_unknown_method_calls
8
- attr_accessor :vendored_stdlib_path
9
- attr_accessor :vendored_gems_path
8
+ attr_accessor :vendor_path
10
9
  attr_reader :libraries
10
+ attr_reader :repository_paths
11
11
 
12
12
  def initialize
13
13
  apply_default_typing_options!
14
- self.vendored_gems_path = nil
15
- self.vendored_stdlib_path = nil
14
+ self.vendor_path = nil
16
15
 
17
16
  @libraries = []
17
+ @repository_paths = []
18
18
  end
19
19
 
20
20
  def apply_default_typing_options!
@@ -66,7 +66,8 @@ module Steep
66
66
  break_context: nil,
67
67
  self_type: AST::Builtin::Object.instance_type,
68
68
  type_env: type_env,
69
- lvar_env: lvar_env
69
+ lvar_env: lvar_env,
70
+ call_context: TypeInference::MethodCall::TopLevelContext.new
70
71
  )
71
72
 
72
73
  typing = Typing.new(source: source, root_context: context)
@@ -114,18 +114,27 @@ module Steep
114
114
  end
115
115
  end
116
116
 
117
- def environment
118
- @environment ||= RBS::Environment.new().yield_self do |env|
119
- stdlib_root = options.vendored_stdlib_path || RBS::EnvironmentLoader::STDLIB_ROOT
120
- gem_vendor_path = options.vendored_gems_path
121
- loader = RBS::EnvironmentLoader.new(stdlib_root: stdlib_root, gem_vendor_path: gem_vendor_path)
122
- options.libraries.each do |lib|
123
- loader.add(library: lib)
124
- end
125
- loader.load(env: env)
117
+ def self.construct_env_loader(options:)
118
+ repo = RBS::Repository.new(no_stdlib: options.vendor_path)
119
+ options.repository_paths.each do |path|
120
+ repo.add(path)
121
+ end
126
122
 
127
- env.resolve_type_names
123
+ loader = RBS::EnvironmentLoader.new(
124
+ core_root: options.vendor_path ? nil : RBS::EnvironmentLoader::DEFAULT_CORE_ROOT,
125
+ repository: repo
126
+ )
127
+ loader.add(path: options.vendor_path) if options.vendor_path
128
+ options.libraries.each do |lib|
129
+ name, version = lib.split(/:/, 2)
130
+ loader.add(library: name, version: version)
128
131
  end
132
+
133
+ loader
134
+ end
135
+
136
+ def environment
137
+ @environment ||= RBS::Environment.from_loader(Target.construct_env_loader(options: options))
129
138
  end
130
139
 
131
140
  def load_signatures(validate:)
@@ -196,7 +196,7 @@ HOVER
196
196
  ),
197
197
  documentation: item.comment&.string,
198
198
  insert_text_format: LanguageServer::Protocol::Constant::InsertTextFormat::SNIPPET,
199
- sort_text: item.inherited_method ? 'z' : 'a' # Ensure language server puts non-inherited methods before inherited methods
199
+ sort_text: item.inherited? ? 'z' : 'a' # Ensure language server puts non-inherited methods before inherited methods
200
200
  )
201
201
  when Project::CompletionProvider::InstanceVariableItem
202
202
  label = "#{item.identifier}: #{item.type}"
@@ -111,7 +111,8 @@ module Steep
111
111
  hover_provider: true,
112
112
  completion_provider: LSP::Interface::CompletionOptions.new(
113
113
  trigger_characters: [".", "@"]
114
- )
114
+ ),
115
+ workspace_symbol_provider: true
115
116
  )
116
117
  )
117
118
  }
@@ -143,6 +144,9 @@ module Steep
143
144
  when "textDocument/open"
144
145
  # Ignores open notification
145
146
 
147
+ when "workspace/symbol"
148
+ signature_worker << message
149
+
146
150
  when "shutdown"
147
151
  queue << { id: id, result: nil }
148
152
  @shutdown_request_id = id
@@ -24,7 +24,12 @@ module Steep
24
24
  def enqueue_target(target:, timestamp:)
25
25
  Steep.logger.debug "queueing target #{target.name}@#{timestamp}"
26
26
  last_target_validated_at[target] = timestamp
27
- queue << [target, timestamp]
27
+ queue << [:validate, [target, timestamp]]
28
+ end
29
+
30
+ def enqueue_symbol(id:, query:)
31
+ Steep.logger.debug "queueing symbol #{query} (#{id})"
32
+ queue << [:symbol, [id, query]]
28
33
  end
29
34
 
30
35
  def handle_request(request)
@@ -37,6 +42,8 @@ module Steep
37
42
  when "textDocument/didChange"
38
43
  update_source(request)
39
44
  validate_signature_if_required(request)
45
+ when "workspace/symbol"
46
+ enqueue_symbol(query: request[:params][:query], id: request[:id])
40
47
  end
41
48
  end
42
49
 
@@ -138,13 +145,63 @@ module Steep
138
145
  end
139
146
  end
140
147
 
148
+ def handle_workspace_symbol(query:, id:)
149
+ provider = Index::SignatureSymbolProvider.new()
150
+
151
+ project.targets.each do |target|
152
+ case target.status
153
+ when Project::Target::TypeCheckStatus
154
+ index = Index::RBSIndex.new()
155
+
156
+ builder = Index::RBSIndex::Builder.new(index: index)
157
+ builder.env(target.status.environment)
158
+
159
+ provider.indexes << index
160
+ end
161
+ end
162
+
163
+ symbols = provider.query_symbol(query)
164
+
165
+ result = symbols.map do |symbol|
166
+ {
167
+ name: symbol.name.to_s,
168
+ kind: symbol.kind,
169
+ deprecated: false,
170
+ containerName: symbol.container_name.to_s,
171
+ location: {
172
+ uri: URI.parse(project.absolute_path(symbol.location.buffer.name).to_s),
173
+ range: {
174
+ start: LSP::Interface::Position.new(
175
+ line: symbol.location.start_line - 1,
176
+ character: symbol.location.start_column,
177
+ ),
178
+ end: LSP::Interface::Position.new(
179
+ line: symbol.location.end_line - 1,
180
+ character: symbol.location.end_column
181
+ )
182
+ }
183
+ }
184
+ }
185
+ end
186
+
187
+ writer.write(id: id, result: result)
188
+ end
189
+
141
190
  def handle_job(job)
142
- target, timestamp = job
191
+ action, data = job
192
+
193
+ case action
194
+ when :validate
195
+ target, timestamp = data
143
196
 
144
- if active_job?(target, timestamp)
145
- validate_signature(target, timestamp: timestamp)
146
- else
147
- Steep.logger.info "Skipping signature validation: #{target.name}, queued timestamp=#{timestamp}, latest timestamp=#{last_target_validated_at[target]}"
197
+ if active_job?(target, timestamp)
198
+ validate_signature(target, timestamp: timestamp)
199
+ else
200
+ Steep.logger.info "Skipping signature validation: #{target.name}, queued timestamp=#{timestamp}, latest timestamp=#{last_target_validated_at[target]}"
201
+ end
202
+ when :symbol
203
+ id, query = data
204
+ handle_workspace_symbol(query: query, id: id)
148
205
  end
149
206
  end
150
207
  end
@@ -127,6 +127,24 @@ module Steep
127
127
  Result::Failure.new(error: error, trace: trace)
128
128
  end
129
129
 
130
+ def true_type?(type)
131
+ case type
132
+ when AST::Types::Literal
133
+ type.value == true
134
+ else
135
+ AST::Builtin::TrueClass.instance_type?(type)
136
+ end
137
+ end
138
+
139
+ def false_type?(type)
140
+ case type
141
+ when AST::Types::Literal
142
+ type.value == false
143
+ else
144
+ AST::Builtin::FalseClass.instance_type?(type)
145
+ end
146
+ end
147
+
130
148
  def check0(relation, self_type:, assumption:, trace:, constraints:)
131
149
  # puts relation
132
150
  trace.type(relation.sub_type, relation.super_type) do
@@ -146,9 +164,28 @@ module Steep
146
164
  when relation.sub_type.is_a?(AST::Types::Bot)
147
165
  success(constraints: constraints)
148
166
 
149
- when relation.super_type.is_a?(AST::Types::Boolean)
167
+ when relation.sub_type.is_a?(AST::Types::Logic::Base) && (true_type?(relation.super_type) || false_type?(relation.super_type))
150
168
  success(constraints: constraints)
151
169
 
170
+ when relation.super_type.is_a?(AST::Types::Boolean)
171
+ check(
172
+ Relation.new(sub_type: relation.sub_type, super_type: AST::Types::Union.build(types: [AST::Builtin.true_type, AST::Builtin.false_type])),
173
+ self_type: self_type,
174
+ assumption: assumption,
175
+ trace: trace,
176
+ constraints: constraints
177
+ )
178
+
179
+ when relation.sub_type.is_a?(AST::Types::Boolean)
180
+ check(
181
+ Relation.new(sub_type: AST::Types::Union.build(types: [AST::Builtin.true_type, AST::Builtin.false_type]),
182
+ super_type: relation.super_type),
183
+ self_type: self_type,
184
+ assumption: assumption,
185
+ trace: trace,
186
+ constraints: constraints
187
+ )
188
+
152
189
  when relation.sub_type.is_a?(AST::Types::Self) && !self_type.is_a?(AST::Types::Self)
153
190
  check(
154
191
  Relation.new(sub_type: self_type, super_type: relation.super_type),
@@ -282,17 +319,20 @@ module Steep
282
319
  end
283
320
 
284
321
  when relation.sub_type.is_a?(AST::Types::Proc) && relation.super_type.is_a?(AST::Types::Proc)
285
- check_method_params(:__proc__,
286
- relation.sub_type.params, relation.super_type.params,
287
- self_type: self_type,
288
- assumption: assumption,
289
- trace: trace,
290
- constraints: constraints).then do
291
- check(Relation.new(sub_type: relation.sub_type.return_type, super_type: relation.super_type.return_type),
292
- self_type: self_type,
293
- assumption: assumption,
294
- trace: trace,
295
- constraints: constraints)
322
+ name = :__proc__
323
+
324
+ sub_type = relation.sub_type
325
+ super_type = relation.super_type
326
+
327
+ check_method_params(name, sub_type.type.params, super_type.type.params, self_type: self_type, assumption: assumption, trace: trace, constraints: constraints).then do
328
+ check_block_given(name, sub_type.block, super_type.block, trace: trace, constraints: constraints).then do
329
+ check_block_params(name, sub_type.block, super_type.block, self_type: self_type, assumption: assumption, trace: trace, constraints: constraints).then do
330
+ check_block_return(sub_type.block, super_type.block, self_type: self_type, assumption: assumption, trace: trace, constraints:constraints).then do
331
+ relation = Relation.new(super_type: super_type.type.return_type, sub_type: sub_type.type.return_type)
332
+ check(relation, self_type: self_type, assumption: assumption, trace: trace, constraints: constraints)
333
+ end
334
+ end
335
+ end
296
336
  end
297
337
 
298
338
  when relation.sub_type.is_a?(AST::Types::Tuple) && relation.super_type.is_a?(AST::Types::Tuple)
@@ -326,23 +366,21 @@ module Steep
326
366
  constraints: constraints)
327
367
 
328
368
  when relation.sub_type.is_a?(AST::Types::Record) && relation.super_type.is_a?(AST::Types::Record)
329
- if Set.new(relation.sub_type.elements.keys).superset?(Set.new(relation.super_type.elements.keys))
330
- keys = relation.super_type.elements.keys
331
- type_pairs = keys.map {|key| [relation.sub_type.elements[key], relation.super_type.elements[key]] }
332
- results = type_pairs.flat_map do |t1, t2|
333
- relation = Relation.new(sub_type: t1, super_type: t2)
334
- [check(relation, self_type: self_type, assumption: assumption, trace: trace, constraints: constraints),
335
- check(relation.flip, self_type: self_type, assumption: assumption, trace: trace, constraints: constraints)]
336
- end
369
+ keys = relation.super_type.elements.keys
370
+ relations = keys.map {|key|
371
+ Relation.new(
372
+ sub_type: relation.sub_type.elements[key] || AST::Builtin.nil_type,
373
+ super_type: relation.super_type.elements[key]
374
+ )
375
+ }
376
+ results = relations.map do |relation|
377
+ check(relation, self_type: self_type, assumption: assumption, trace: trace, constraints: constraints)
378
+ end
337
379
 
338
- if results.all?(&:success?)
339
- success(constraints: constraints)
340
- else
341
- results.find(&:failure?)
342
- end
380
+ if results.all?(&:success?)
381
+ success(constraints: constraints)
343
382
  else
344
- failure(error: Result::Failure::UnknownPairError.new(relation: relation),
345
- trace: trace)
383
+ results.find(&:failure?)
346
384
  end
347
385
 
348
386
  when relation.sub_type.is_a?(AST::Types::Record) && relation.super_type.is_a?(AST::Types::Name::Base)
@@ -665,12 +703,12 @@ module Steep
665
703
 
666
704
  def check_method_type(name, sub_type, super_type, self_type:, assumption:, trace:, constraints:)
667
705
  Steep.logger.tagged("#{name}: #{sub_type} <: #{super_type}") do
668
- check_method_params(name, sub_type.params, super_type.params, self_type: self_type, assumption: assumption, trace: trace, constraints: constraints).then do
706
+ check_method_params(name, sub_type.type.params, super_type.type.params, self_type: self_type, assumption: assumption, trace: trace, constraints: constraints).then do
669
707
  check_block_given(name, sub_type.block, super_type.block, trace: trace, constraints: constraints).then do
670
708
  check_block_params(name, sub_type.block, super_type.block, self_type: self_type, assumption: assumption, trace: trace, constraints: constraints).then do
671
709
  check_block_return(sub_type.block, super_type.block, self_type: self_type, assumption: assumption, trace: trace, constraints:constraints).then do
672
- relation = Relation.new(super_type: super_type.return_type,
673
- sub_type: sub_type.return_type)
710
+ relation = Relation.new(super_type: super_type.type.return_type,
711
+ sub_type: sub_type.type.return_type)
674
712
  check(relation, self_type: self_type, assumption: assumption, trace: trace, constraints: constraints)
675
713
  end
676
714
  end
@@ -715,10 +753,10 @@ module Steep
715
753
 
716
754
  def match_method_type(name, sub_type, super_type, trace:)
717
755
  [].tap do |pairs|
718
- match_params(name, sub_type.params, super_type.params, trace: trace).yield_self do |result|
756
+ match_params(name, sub_type.type.params, super_type.type.params, trace: trace).yield_self do |result|
719
757
  return result unless result.is_a?(Array)
720
758
  pairs.push(*result)
721
- pairs.push [sub_type.return_type, super_type.return_type]
759
+ pairs.push [sub_type.type.return_type, super_type.type.return_type]
722
760
 
723
761
  case
724
762
  when !super_type.block && !sub_type.block