steep 0.52.2 → 1.0.0

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