steep 0.52.2 → 1.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c9d5eb873780b31a2431d2e6c4efdc558fbb3a4a42c08cbe2580bb191db5bc8
4
- data.tar.gz: dee794e9cb098309b9875396ed1dc9904a4c6882aeadb27c83c26604ebcbc87d
3
+ metadata.gz: 899bca0fd211114d52e015acc844ee4648eadb6b739af911f7ac73560cedef83
4
+ data.tar.gz: 2fe5b52342a6b932d5ce08f5b78c417e43cda38db378c35d1b31891ca7c79465
5
5
  SHA512:
6
- metadata.gz: ba05f88c28a78414ffcd41744558bdea978b72621f422571cb1450b7b0d56fb77c2b65329064226863db2517d4769a6f54e7c47ce92eef59d97f0403bb9e9425
7
- data.tar.gz: 6e260b48940214b7c0910bf2678c587b469e163e70265f197ef8fd17a1703a68fd9488c77c3d1f8254939c074281717593e49812875fb705615189cd3ec4cdf0
6
+ metadata.gz: 20e09462ddef35a3aa00a19d6f7cb86f2862c9ab700131bf439fc7750b088d43d1f2bb3380dfdf57a0c881d58c885b789d47eba35d7e146156809511e4e1c64c
7
+ data.tar.gz: dfa20a13b3b25575419ad1c9ac2417b1b873a0732eded5fa643e6e5768b644e27ea07c8d83bf9ae5e333b872d8f039aba111bee4d7ae2a48034dc06047265fee
data/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 1.0.0 (2022-05-20)
6
+
7
+ * Add special typing rule for `Array#compact` ([\#555](https://github.com/soutaro/steep/pull/555))
8
+ * Add custom method type of `#fetch` on tuples and records ([\#554](https://github.com/soutaro/steep/pull/554))
9
+ * Better `masgn` ([\#553](https://github.com/soutaro/steep/pull/553))
10
+ * Fix method parameter type checking ([\#552](https://github.com/soutaro/steep/pull/552))
11
+
5
12
  ## 0.52.2 (2022-05-02)
6
13
 
7
14
  * Handle class declaration with non-const super class ([\#546](https://github.com/soutaro/steep/pull/546))
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- steep (0.52.2)
4
+ steep (1.0.0)
5
5
  activesupport (>= 5.1)
6
6
  language_server-protocol (>= 3.15, < 4.0)
7
7
  listen (~> 3.0)
@@ -24,7 +24,7 @@ PATH
24
24
  GEM
25
25
  remote: https://rubygems.org/
26
26
  specs:
27
- activesupport (7.0.2.4)
27
+ activesupport (7.0.3)
28
28
  concurrent-ruby (~> 1.0, >= 1.0.2)
29
29
  i18n (>= 1.6, < 2)
30
30
  minitest (>= 5.1)
@@ -51,7 +51,7 @@ GEM
51
51
  rb-fsevent (0.11.1)
52
52
  rb-inotify (0.10.1)
53
53
  ffi (~> 1.0)
54
- rbs (2.3.2)
54
+ rbs (2.4.0)
55
55
  stackprof (0.2.19)
56
56
  terminal-table (3.0.2)
57
57
  unicode-display_width (>= 1.1.1, < 3)
@@ -674,6 +674,57 @@ module Steep
674
674
  )
675
675
  end
676
676
 
677
+ array_interface.methods[:fetch] = array_interface.methods[:fetch].yield_self do |fetch|
678
+ Interface::Interface::Entry.new(
679
+ method_types: type.types.flat_map.with_index {|elem_type, index|
680
+ [
681
+ Interface::MethodType.new(
682
+ type_params: [],
683
+ type: Interface::Function.new(
684
+ params: Interface::Function::Params.build(required: [AST::Types::Literal.new(value: index)]),
685
+ return_type: elem_type,
686
+ location: nil
687
+ ),
688
+ block: nil,
689
+ method_decls: Set[]
690
+ ),
691
+ Interface::MethodType.new(
692
+ type_params: [Interface::TypeParam.new(name: :T, upper_bound: nil, variance: :invariant, unchecked: false)],
693
+ type: Interface::Function.new(
694
+ params: Interface::Function::Params.build(
695
+ required: [
696
+ AST::Types::Literal.new(value: index),
697
+ AST::Types::Var.new(name: :T)
698
+ ]
699
+ ),
700
+ return_type: AST::Types::Union.build(types: [elem_type, AST::Types::Var.new(name: :T)]),
701
+ location: nil
702
+ ),
703
+ block: nil,
704
+ method_decls: Set[]
705
+ ),
706
+ Interface::MethodType.new(
707
+ type_params: [Interface::TypeParam.new(name: :T, upper_bound: nil, variance: :invariant, unchecked: false)],
708
+ type: Interface::Function.new(
709
+ params: Interface::Function::Params.build(required: [AST::Types::Literal.new(value: index)]),
710
+ return_type: AST::Types::Union.build(types: [elem_type, AST::Types::Var.new(name: :T)]),
711
+ location: nil
712
+ ),
713
+ block: Interface::Block.new(
714
+ type: Interface::Function.new(
715
+ params: Interface::Function::Params.build(required: [AST::Builtin::Integer.instance_type]),
716
+ return_type: AST::Types::Var.new(name: :T),
717
+ location: nil
718
+ ),
719
+ optional: false
720
+ ),
721
+ method_decls: Set[]
722
+ )
723
+ ]
724
+ } + fetch.method_types
725
+ )
726
+ end
727
+
677
728
  array_interface.methods[:first] = array_interface.methods[:first].yield_self do |first|
678
729
  Interface::Interface::Entry.new(
679
730
  method_types: [
@@ -712,11 +763,11 @@ module Steep
712
763
 
713
764
  when Record
714
765
  yield_self do
715
- key_type = type.elements.keys.map {|value| Literal.new(value: value, location: nil) }.yield_self do |types|
766
+ all_key_type = type.elements.keys.map {|value| Literal.new(value: value, location: nil) }.yield_self do |types|
716
767
  Union.build(types: types, location: nil)
717
768
  end
718
- value_type = Union.build(types: type.elements.values, location: nil)
719
- hash_type = Builtin::Hash.instance_type(key_type, value_type)
769
+ all_value_type = Union.build(types: type.elements.values, location: nil)
770
+ hash_type = Builtin::Hash.instance_type(all_key_type, all_value_type)
720
771
 
721
772
  interface(hash_type, private: private, self_type: self_type).tap do |hash_interface|
722
773
  hash_interface.methods[:[]] = hash_interface.methods[:[]].yield_self do |ref|
@@ -768,6 +819,54 @@ module Steep
768
819
  } + update.method_types
769
820
  )
770
821
  end
822
+
823
+ hash_interface.methods[:fetch] = hash_interface.methods[:fetch].yield_self do |update|
824
+ Interface::Interface::Entry.new(
825
+ method_types: type.elements.flat_map {|key_value, value_type|
826
+ key_type = Literal.new(value: key_value, location: nil)
827
+
828
+ [
829
+ Interface::MethodType.new(
830
+ type_params: [],
831
+ type: Interface::Function.new(
832
+ params: Interface::Function::Params.build(required: [key_type]),
833
+ return_type: value_type,
834
+ location: nil
835
+ ),
836
+ block: nil,
837
+ method_decls: Set[]
838
+ ),
839
+ Interface::MethodType.new(
840
+ type_params: [Interface::TypeParam.new(name: :T, upper_bound: nil, variance: :invariant, unchecked: false)],
841
+ type: Interface::Function.new(
842
+ params: Interface::Function::Params.build(required: [key_type, AST::Types::Var.new(name: :T)]),
843
+ return_type: AST::Types::Union.build(types: [value_type, AST::Types::Var.new(name: :T)]),
844
+ location: nil
845
+ ),
846
+ block: nil,
847
+ method_decls: Set[]
848
+ ),
849
+ Interface::MethodType.new(
850
+ type_params: [Interface::TypeParam.new(name: :T, upper_bound: nil, variance: :invariant, unchecked: false)],
851
+ type: Interface::Function.new(
852
+ params: Interface::Function::Params.build(required: [key_type]),
853
+ return_type: AST::Types::Union.build(types: [value_type, AST::Types::Var.new(name: :T)]),
854
+ location: nil
855
+ ),
856
+ block: Interface::Block.new(
857
+ type: Interface::Function.new(
858
+ params: Interface::Function::Params.build(required: [all_key_type]),
859
+ return_type: AST::Types::Var.new(name: :T),
860
+ location: nil
861
+ ),
862
+ optional: false
863
+ ),
864
+ method_decls: Set[]
865
+ )
866
+ ]
867
+ } + update.method_types
868
+ )
869
+ end
771
870
  end
772
871
  end
773
872
 
@@ -48,6 +48,18 @@ module Steep
48
48
  call = content.method_call
49
49
  builder.push do |s|
50
50
  case call
51
+ when TypeInference::MethodCall::Special
52
+ mt = call.actual_method_type.with(
53
+ type: call.actual_method_type.type.with(return_type: call.return_type)
54
+ )
55
+ s << <<-EOM
56
+ **💡 Custom typing rule applies**
57
+
58
+ ```rbs
59
+ #{mt.to_s}
60
+ ```
61
+
62
+ EOM
51
63
  when TypeInference::MethodCall::Typed
52
64
  mt = call.actual_method_type.with(
53
65
  type: call.actual_method_type.type.with(return_type: call.return_type)
data/lib/steep/source.rb CHANGED
@@ -406,8 +406,8 @@ module Steep
406
406
  Source.construct_mapping(node: node_, annotations: annotations, mapping: mapping)
407
407
 
408
408
  annotations.each do |annot|
409
- mapping[node] ||= []
410
- mapping[node] << annot
409
+ mapping[node_] ||= []
410
+ mapping[node_] << annot
411
411
  end
412
412
 
413
413
  Source.new(path: path, node: node_, mapping: mapping)
@@ -2662,11 +2662,13 @@ module Steep
2662
2662
  when :ivasgn
2663
2663
  _, constr = constr.ivasgn(assignment, element_type)
2664
2664
  when :splat
2665
- case assignment.children[0].type
2665
+ case assignment.children[0]&.type
2666
2666
  when :lvasgn
2667
2667
  _, constr = constr.lvasgn(assignment.children[0], unwrap_rhs_type)
2668
2668
  when :ivasgn
2669
2669
  _, constr = constr.ivasgn(assignment.children[0], unwrap_rhs_type)
2670
+ when nil
2671
+ # foo, * = bar
2670
2672
  else
2671
2673
  raise
2672
2674
  end
@@ -2684,7 +2686,31 @@ module Steep
2684
2686
  unless rhs_type.is_a?(AST::Types::Any)
2685
2687
  Steep.logger.error("Unsupported masgn rhs type: array or tuple is supported (#{rhs_type})")
2686
2688
  end
2687
- _, constr = constr.fallback_to_any(lhs)
2689
+
2690
+ untyped = AST::Builtin.any_type
2691
+
2692
+ constr = lhs.children.inject(constr) do |constr, assignment|
2693
+ case assignment.type
2694
+ when :lvasgn
2695
+ _, constr = constr.lvasgn(assignment, untyped)
2696
+ when :ivasgn
2697
+ _, constr = constr.ivasgn(assignment, untyped)
2698
+ when :splat
2699
+ case assignment.children[0]&.type
2700
+ when :lvasgn
2701
+ _, constr = constr.lvasgn(assignment.children[0], untyped)
2702
+ when :ivasgn
2703
+ _, constr = constr.ivasgn(assignment.children[0], untyped)
2704
+ when nil
2705
+ # foo, * = bar
2706
+ else
2707
+ raise
2708
+ end
2709
+ end
2710
+
2711
+ constr
2712
+ end
2713
+
2688
2714
  add_typing(node, type: rhs_type, constr: constr)
2689
2715
  end
2690
2716
  end
@@ -3114,13 +3140,61 @@ module Steep
3114
3140
  end
3115
3141
  end
3116
3142
 
3143
+ SPECIAL_METHOD_NAMES = {
3144
+ array_compact: Set[
3145
+ MethodName("::Array#compact"),
3146
+ MethodName("::Enumerable#compact")
3147
+ ]
3148
+ }
3149
+
3150
+ def try_special_method(node, receiver_type:, method_name:, method_type:, arguments:, block_params:, block_body:)
3151
+ decls = method_type.method_decls
3152
+
3153
+ case
3154
+ when decl = decls.find {|decl| SPECIAL_METHOD_NAMES[:array_compact].include?(decl.method_name) }
3155
+ if arguments.empty? && !block_params
3156
+ # compact
3157
+ return_type = method_type.type.return_type
3158
+ if AST::Builtin::Array.instance_type?(return_type)
3159
+ elem = return_type.args[0]
3160
+ type = AST::Builtin::Array.instance_type(unwrap(elem))
3161
+
3162
+ _, constr = add_typing(node, type: type)
3163
+ call = TypeInference::MethodCall::Special.new(
3164
+ node: node,
3165
+ context: constr.context.method_context,
3166
+ method_name: decl.method_name,
3167
+ receiver_type: receiver_type,
3168
+ actual_method_type: method_type.with(type: method_type.type.with(return_type: type)),
3169
+ return_type: type,
3170
+ method_decls: decls
3171
+ )
3172
+
3173
+ return [call, constr]
3174
+ end
3175
+ end
3176
+ end
3177
+
3178
+ nil
3179
+ end
3180
+
3117
3181
  def type_method_call(node, method_name:, receiver_type:, method:, arguments:, block_params:, block_body:, topdown_hint:)
3118
3182
  node_range = node.loc.expression.yield_self {|l| l.begin_pos..l.end_pos }
3119
3183
 
3120
3184
  results = method.method_types.map do |method_type|
3121
3185
  Steep.logger.tagged method_type.to_s do
3122
3186
  typing.new_child(node_range) do |child_typing|
3123
- self.with_new_typing(child_typing).try_method_type(
3187
+ constr = self.with_new_typing(child_typing)
3188
+
3189
+ constr.try_special_method(
3190
+ node,
3191
+ receiver_type: receiver_type,
3192
+ method_name: method_name,
3193
+ method_type: method_type,
3194
+ arguments: arguments,
3195
+ block_params: block_params,
3196
+ block_body: block_body
3197
+ ) || constr.try_method_type(
3124
3198
  node,
3125
3199
  receiver_type: receiver_type,
3126
3200
  method_name: method_name,
@@ -79,13 +79,16 @@ module Steep
79
79
  attr_reader :actual_method_type
80
80
  attr_reader :method_decls
81
81
 
82
- def initialize(node:, context:, method_name:, receiver_type:, actual_method_type:, method_decls:, return_type: actual_method_type.return_type)
82
+ def initialize(node:, context:, method_name:, receiver_type:, actual_method_type:, method_decls:, return_type:)
83
83
  super(node: node, context: context, method_name: method_name, receiver_type: receiver_type, return_type: return_type)
84
84
  @actual_method_type = actual_method_type
85
85
  @method_decls = method_decls
86
86
  end
87
87
  end
88
88
 
89
+ class Special < Typed
90
+ end
91
+
89
92
  class Untyped < Base
90
93
  def initialize(node:, context:, method_name:)
91
94
  super(node: node, context: context, method_name: method_name, receiver_type: AST::Types::Any.new, return_type: AST::Types::Any.new)
@@ -315,6 +315,7 @@ module Steep
315
315
  when Interface::Function::Params::PositionalParams::Rest
316
316
  rest_types << param.type
317
317
  positional_params = nil
318
+ args.shift
318
319
  break
319
320
  when nil
320
321
  has_error = true
data/lib/steep/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Steep
2
- VERSION = "0.52.2"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -0,0 +1,6 @@
1
+ target :test do
2
+ check "*.rb"
3
+ signature "*.rbs"
4
+
5
+ configure_code_diagnostics(Steep::Diagnostic::Ruby.all_error)
6
+ end
@@ -0,0 +1,2 @@
1
+ # @type var a: Array[Integer | bool]
2
+ a = [1, nil, true].compact
@@ -0,0 +1,5 @@
1
+ module Compact
2
+ class Foo
3
+ def compact: () -> Array[String | bool | nil]
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ # @type var y: Array[String | bool]
2
+ y = Compact::Foo.new.compact
@@ -0,0 +1,18 @@
1
+ ---
2
+ - file: b.rb
3
+ diagnostics:
4
+ - range:
5
+ start:
6
+ line: 2
7
+ character: 0
8
+ end:
9
+ line: 2
10
+ character: 28
11
+ severity: ERROR
12
+ message: |-
13
+ Cannot assign a value of type `::Array[(::String | bool | nil)]` to a variable of type `::Array[(::String | bool)]`
14
+ ::Array[(::String | bool | nil)] <: ::Array[(::String | bool)]
15
+ (::String | bool | nil) <: (::String | bool)
16
+ nil <: (::String | bool)
17
+ nil <: ::String
18
+ code: Ruby::IncompatibleAssignment
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: steep
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.52.2
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Soutaro Matsumoto
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-05-02 00:00:00.000000000 Z
11
+ date: 2022-05-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -315,6 +315,11 @@ files:
315
315
  - smoke/class/i.rb
316
316
  - smoke/class/i.rbs
317
317
  - smoke/class/test_expectations.yml
318
+ - smoke/compact/Steepfile
319
+ - smoke/compact/a.rb
320
+ - smoke/compact/a.rbs
321
+ - smoke/compact/b.rb
322
+ - smoke/compact/test_expectations.yml
318
323
  - smoke/const/Steepfile
319
324
  - smoke/const/a.rb
320
325
  - smoke/const/b.rb