steep 0.31.0 → 0.35.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/lib/steep.rb +3 -2
  4. data/lib/steep/annotation_parser.rb +1 -1
  5. data/lib/steep/ast/types/factory.rb +66 -60
  6. data/lib/steep/cli.rb +15 -2
  7. data/lib/steep/drivers/print_project.rb +11 -0
  8. data/lib/steep/drivers/stats.rb +85 -0
  9. data/lib/steep/drivers/vendor.rb +1 -20
  10. data/lib/steep/errors.rb +19 -15
  11. data/lib/steep/interface/method_type.rb +12 -23
  12. data/lib/steep/method_name.rb +28 -0
  13. data/lib/steep/project/completion_provider.rb +24 -15
  14. data/lib/steep/project/dsl.rb +13 -17
  15. data/lib/steep/project/options.rb +4 -4
  16. data/lib/steep/project/source_file.rb +2 -1
  17. data/lib/steep/project/target.rb +19 -10
  18. data/lib/steep/server/interaction_worker.rb +1 -1
  19. data/lib/steep/server/utils.rb +1 -1
  20. data/lib/steep/source.rb +3 -3
  21. data/lib/steep/subtyping/check.rb +30 -16
  22. data/lib/steep/subtyping/variable_occurrence.rb +2 -0
  23. data/lib/steep/type_construction.rb +585 -416
  24. data/lib/steep/type_inference/context.rb +7 -3
  25. data/lib/steep/type_inference/context_array.rb +1 -1
  26. data/lib/steep/type_inference/local_variable_type_env.rb +10 -1
  27. data/lib/steep/type_inference/logic_type_interpreter.rb +6 -0
  28. data/lib/steep/type_inference/method_call.rb +116 -0
  29. data/lib/steep/typing.rb +38 -8
  30. data/lib/steep/version.rb +1 -1
  31. data/smoke/regression/fun.rb +8 -0
  32. data/smoke/regression/fun.rbs +4 -0
  33. data/smoke/regression/range.rb +5 -0
  34. data/steep.gemspec +1 -1
  35. metadata +10 -6
  36. data/lib/steep/ast/buffer.rb +0 -51
  37. data/lib/steep/ast/location.rb +0 -86
@@ -18,26 +18,7 @@ module Steep
18
18
  end
19
19
 
20
20
  def run
21
- stdout.puts "Vendoring into #{vendor_dir}..."
22
-
23
- vendorer = RBS::Vendorer.new(vendor_dir: vendor_dir)
24
-
25
- if clean_before
26
- stdout.puts " Cleaning directory..."
27
- vendorer.clean!
28
- end
29
-
30
- stdout.puts " Vendoring standard libraries..."
31
- vendorer.stdlib!
32
-
33
- if defined?(Bundler)
34
- Bundler.locked_gems.specs.each do |spec|
35
- if RBS::EnvironmentLoader.gem_sig_path(spec.name, spec.version.to_s).directory?
36
- stdout.puts " Vendoring rubygem: #{spec.full_name}..."
37
- vendorer.gem! spec.name, spec.version.to_s
38
- end
39
- end
40
- end
21
+ stdout.puts "`steep vendor` is deprecated. Use `rbs vendor` command directly"
41
22
 
42
23
  0
43
24
  end
@@ -101,20 +101,6 @@ module Steep
101
101
  end
102
102
  end
103
103
 
104
- class IncompatibleBlockParameters < Base
105
- attr_reader :node
106
- attr_reader :method_type
107
-
108
- def initialize(node:, method_type:)
109
- super(node: node)
110
- @method_type = method_type
111
- end
112
-
113
- def to_s
114
- "#{location_to_str}: IncompatibleBlockParameters: method_type=#{method_type}"
115
- end
116
- end
117
-
118
104
  class BlockParameterTypeMismatch < Base
119
105
  attr_reader :expected
120
106
  attr_reader :actual
@@ -186,7 +172,7 @@ module Steep
186
172
  end
187
173
 
188
174
  def to_s
189
- "#{location_to_str}: RequiredBlockMissing: method_type=#{method_type.location&.source}"
175
+ "#{location_to_str}: RequiredBlockMissing: method_type=#{method_type.to_s}"
190
176
  end
191
177
  end
192
178
 
@@ -557,5 +543,23 @@ module Steep
557
543
  "#{location_to_str}: UnsupportedSyntax: #{msg}"
558
544
  end
559
545
  end
546
+
547
+ class UnexpectedError < Base
548
+ attr_reader :message
549
+ attr_reader :error
550
+
551
+ def initialize(node:, error:)
552
+ super(node: node)
553
+ @error = error
554
+ @message = error.message
555
+ end
556
+
557
+ def to_s
558
+ <<-MESSAGE
559
+ #{location_to_str}: UnexpectedError: #{error.class}
560
+ >> #{message}
561
+ MESSAGE
562
+ end
563
+ end
560
564
  end
561
565
  end
@@ -774,16 +774,14 @@ module Steep
774
774
  attr_reader :params
775
775
  attr_reader :block
776
776
  attr_reader :return_type
777
- attr_reader :location
778
- attr_reader :method_def
777
+ attr_reader :method_decls
779
778
 
780
- def initialize(type_params:, params:, block:, return_type:, location:, method_def:)
779
+ def initialize(type_params:, params:, block:, return_type:, method_decls:)
781
780
  @type_params = type_params
782
781
  @params = params
783
782
  @block = block
784
783
  @return_type = return_type
785
- @location = location
786
- @method_def = method_def
784
+ @method_decls = method_decls
787
785
  end
788
786
 
789
787
  def ==(other)
@@ -791,9 +789,7 @@ module Steep
791
789
  other.type_params == type_params &&
792
790
  other.params == params &&
793
791
  other.block == block &&
794
- other.return_type == return_type &&
795
- (!other.method_def || !method_def || other.method_def == method_def) &&
796
- (!other.location || !location || other.location == location)
792
+ other.return_type == return_type
797
793
  end
798
794
 
799
795
  alias eql? ==
@@ -824,8 +820,7 @@ module Steep
824
820
  params: params.subst(s_),
825
821
  block: block&.subst(s_),
826
822
  return_type: return_type.subst(s_),
827
- method_def: method_def,
828
- location: location
823
+ method_decls: method_decls
829
824
  )
830
825
  end
831
826
 
@@ -847,17 +842,15 @@ module Steep
847
842
  params: params.subst(s),
848
843
  block: block&.subst(s),
849
844
  return_type: return_type.subst(s),
850
- location: location,
851
- method_def: method_def)
845
+ method_decls: method_decls)
852
846
  end
853
847
 
854
- def with(type_params: self.type_params, params: self.params, block: self.block, return_type: self.return_type, location: self.location, method_def: self.method_def)
848
+ def with(type_params: self.type_params, params: self.params, block: self.block, return_type: self.return_type, method_decls: self.method_decls)
855
849
  self.class.new(type_params: type_params,
856
850
  params: params,
857
851
  block: block,
858
852
  return_type: return_type,
859
- method_def: method_def,
860
- location: location)
853
+ method_decls: method_decls)
861
854
  end
862
855
 
863
856
  def to_s
@@ -873,8 +866,7 @@ module Steep
873
866
  params: params.map_type(&block),
874
867
  block: self.block&.yield_self {|blk| blk.map_type(&block) },
875
868
  return_type: yield(return_type),
876
- location: location,
877
- method_def: method_def)
869
+ method_decls: method_decls)
878
870
  end
879
871
 
880
872
  # Returns a new method type which can be used for the method implementation type of both `self` and `other`.
@@ -902,8 +894,7 @@ module Steep
902
894
  return_type: AST::Types::Union.build(
903
895
  types: [return_type.subst(s1),other.return_type.subst(s2)]
904
896
  ),
905
- method_def: method_def,
906
- location: nil
897
+ method_decls: method_decls + other.method_decls
907
898
  )
908
899
  end
909
900
 
@@ -958,8 +949,7 @@ module Steep
958
949
  block: block,
959
950
  return_type: return_type,
960
951
  type_params: type_params,
961
- method_def: nil,
962
- location: nil
952
+ method_decls: method_decls + other.method_decls
963
953
  )
964
954
  end
965
955
 
@@ -1006,8 +996,7 @@ module Steep
1006
996
  block: block,
1007
997
  return_type: return_type,
1008
998
  type_params: type_params,
1009
- method_def: nil,
1010
- location: nil
999
+ method_decls: method_decls + other.method_decls
1011
1000
  )
1012
1001
  end
1013
1002
  end
@@ -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}"
@@ -13,7 +13,7 @@ module Steep
13
13
  if range
14
14
  text = text.dup
15
15
 
16
- buf = AST::Buffer.new(name: :_, content: text)
16
+ buf = RBS::Buffer.new(name: :_, content: text)
17
17
 
18
18
  start_pos = buf.loc_to_pos(range[:start].yield_self {|pos| [pos[:line]+1, pos[:character]] })
19
19
  end_pos = buf.loc_to_pos(range[:end].yield_self {|pos| [pos[:line]+1, pos[:character]] })
@@ -65,11 +65,11 @@ module Steep
65
65
  parser.tokenize(buffer)
66
66
  end
67
67
 
68
- buffer = AST::Buffer.new(name: path, content: source_code)
68
+ buffer = RBS::Buffer.new(name: path, content: source_code)
69
69
 
70
70
  comments.each do |comment|
71
71
  src = comment.text.gsub(/\A#\s*/, '')
72
- location = AST::Location.new(buffer: buffer,
72
+ location = RBS::Location.new(buffer: buffer,
73
73
  start_pos: comment.location.expression.begin_pos + 1,
74
74
  end_pos: comment.location.expression.end_pos)
75
75
  annotation = AnnotationParser.new(factory: factory).parse(src, location: location)
@@ -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