steep 1.5.0.pre.4 → 1.5.0.pre.5

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: cfb1de89ec50042cf06306ab17717671e8b6a8451ea2885b4af8085226f7994e
4
- data.tar.gz: 1b67eb25821061afe42c8c4dd644b30370e866ac9ef8066cb77031799c981145
3
+ metadata.gz: ed79428764aa83fbd2e7f3c5a8e52ff863b4e65bef05aae43ba9ca4b7c9d2f4e
4
+ data.tar.gz: be79772ce19f152e3048ae565802640cae96f1bfca98adf60eab47a28ab3ccad
5
5
  SHA512:
6
- metadata.gz: 1f0b3400822be747c45ed1d5c2cdfbddf8c0fdf70963061b66308464a18a95e493cc94ad406bbc444f618f562e0f95f276600c27e3ec2269f01504727b0528c7
7
- data.tar.gz: 45b2a32802ea5ab504a8c3ee04f714a87b27227ffa66282d64243e3161aa0628001f1925e3ad7aaf09fcd0fbddc4def36e9b9940c76a87b195c2f51b19006bf9
6
+ metadata.gz: aa071be8eaf425a2a36ffed4c8d0a97446997f9cbf85c9dc4190b5bf26c9bf5f66c7106ab27b96378d05e246a6327689ef491dedbd16ad7cb9eb8d8079b3818d
7
+ data.tar.gz: d7f842d51c84a2f48bdfd79a07de3e6265dd746497aa9661871f12b062832c2a24f46b37ce1ddb614ac5ba1c65f4c408e9e1eafec27fd69d7158514fe77e0fec
data/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 1.5.0.pre.5 (2023-07-07)
6
+
7
+ ### Type checker core
8
+
9
+ * Unreachability improvements ([#845](https://github.com/soutaro/steep/pull/845))
10
+ * Fix type inference problem ([#843](https://github.com/soutaro/steep/pull/843))
11
+
5
12
  ## 1.5.0.pre.4 (2023-07-06)
6
13
 
7
14
  ### Type checker core
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- steep (1.5.0.pre.4)
4
+ steep (1.5.0.pre.5)
5
5
  activesupport (>= 5.1)
6
6
  concurrent-ruby (>= 1.1.10)
7
7
  csv (>= 3.0.9)
@@ -204,7 +204,7 @@ module Steep
204
204
  when Logic::Base
205
205
  RBS::Types::Bases::Bool.new(location: type.location)
206
206
  else
207
- __skip__ = raise "Unexpected type given: #{type} (#{type.class})"
207
+ raise "Unexpected type given: #{type} (#{type.class})"
208
208
  end
209
209
  end
210
210
 
@@ -767,6 +767,19 @@ module Steep
767
767
  end
768
768
  end
769
769
 
770
+ class UnreachableValueBranch < Base
771
+ attr_reader :type
772
+
773
+ def initialize(node:, type:, location: node.location.expression)
774
+ super(node: node, location: location)
775
+ @type = type
776
+ end
777
+
778
+ def header_line
779
+ "The branch may evaluate to a value of `#{type}` but unreachable"
780
+ end
781
+ end
782
+
770
783
  class UnexpectedSplat < Base
771
784
  attr_reader :type
772
785
 
@@ -968,7 +981,8 @@ module Steep
968
981
  {
969
982
  ImplicitBreakValueMismatch => :warning,
970
983
  FallbackAny => :information,
971
- UnreachableBranch => :warning,
984
+ UnreachableValueBranch => :warning,
985
+ UnreachableBranch => :information,
972
986
  UnknownConstant => :warning,
973
987
  MethodDefinitionMissing => :information,
974
988
  FalseAssertion => :information,
@@ -989,6 +1003,7 @@ module Steep
989
1003
  NoMethod => nil,
990
1004
  ImplicitBreakValueMismatch => nil,
991
1005
  FallbackAny => nil,
1006
+ UnreachableValueBranch => nil,
992
1007
  UnreachableBranch => nil,
993
1008
  UnknownConstant => nil,
994
1009
  MethodDefinitionMissing => nil,
@@ -1006,6 +1021,7 @@ module Steep
1006
1021
  NoMethod => nil,
1007
1022
  ImplicitBreakValueMismatch => nil,
1008
1023
  FallbackAny => nil,
1024
+ UnreachableValueBranch => nil,
1009
1025
  UnreachableBranch => nil,
1010
1026
  UnknownConstant => nil,
1011
1027
  MethodDefinitionMissing => nil,
@@ -1883,9 +1883,25 @@ module Steep
1883
1883
 
1884
1884
  if truthy.unreachable
1885
1885
  if true_clause
1886
+ _, _, _, loc = deconstruct_if_node!(node)
1887
+
1888
+ if loc.respond_to?(:keyword)
1889
+ condition_loc = loc #: NodeHelper::condition_loc
1890
+ case condition_loc.keyword.source
1891
+ when "if"
1892
+ location = condition_loc.begin || condition_loc.keyword
1893
+ when "unless"
1894
+ # `else` token always exists
1895
+ location = condition_loc.else || raise
1896
+ end
1897
+ else
1898
+ location = true_clause.loc.expression
1899
+ end
1900
+
1886
1901
  typing.add_error(
1887
1902
  Diagnostic::Ruby::UnreachableBranch.new(
1888
- node: true_clause || node
1903
+ node: true_clause,
1904
+ location: location || raise
1889
1905
  )
1890
1906
  )
1891
1907
  end
@@ -1893,9 +1909,25 @@ module Steep
1893
1909
 
1894
1910
  if falsy.unreachable
1895
1911
  if false_clause
1912
+ _, _, _, loc = deconstruct_if_node!(node)
1913
+
1914
+ if loc.respond_to?(:keyword)
1915
+ condition_loc = loc #: NodeHelper::condition_loc
1916
+ case condition_loc.keyword.source
1917
+ when "if"
1918
+ # `else` token always exists
1919
+ location = condition_loc.else || raise
1920
+ when "unless"
1921
+ location = condition_loc.begin || condition_loc.keyword
1922
+ end
1923
+ else
1924
+ location = false_clause.loc.expression
1925
+ end
1926
+
1896
1927
  typing.add_error(
1897
1928
  Diagnostic::Ruby::UnreachableBranch.new(
1898
- node: false_clause || node
1929
+ node: false_clause,
1930
+ location: location || raise
1899
1931
  )
1900
1932
  )
1901
1933
  end
@@ -1961,19 +1993,25 @@ module Steep
1961
1993
  next_branch_reachable &&= false_branch_reachable
1962
1994
  body_constr = when_constr.update_type_env {|env| env.join(*test_envs) }
1963
1995
 
1964
- if body
1965
- branch_results <<
1996
+ branch_result =
1997
+ if body
1966
1998
  body_constr
1967
1999
  .for_branch(body)
1968
2000
  .tap {|constr| typing.add_context_for_node(body, context: constr.context) }
1969
2001
  .synthesize(body, hint: hint)
1970
- else
1971
- branch_results << Pair.new(type: AST::Builtin.nil_type, constr: body_constr)
1972
- end
2002
+ else
2003
+ Pair.new(type: AST::Builtin.nil_type, constr: body_constr)
2004
+ end
1973
2005
 
1974
- unless branch_reachable
2006
+ branch_results << branch_result
2007
+
2008
+ if !branch_reachable && !branch_result.type.is_a?(AST::Types::Bot)
1975
2009
  typing.add_error(
1976
- Diagnostic::Ruby::UnreachableBranch.new(node: body || clause)
2010
+ Diagnostic::Ruby::UnreachableValueBranch.new(
2011
+ node: clause,
2012
+ type: branch_result.type,
2013
+ location: clause.location.keyword
2014
+ )
1977
2015
  )
1978
2016
  end
1979
2017
 
@@ -2004,9 +2042,14 @@ module Steep
2004
2042
  # `else` may present even if it's empty
2005
2043
  if loc.else
2006
2044
  if els
2007
- typing.add_error Diagnostic::Ruby::UnreachableBranch.new(node: els)
2008
- else
2009
- typing.add_error Diagnostic::Ruby::UnreachableBranch.new(node: node, location: loc.else)
2045
+ else_result or raise
2046
+ unless else_result.type.is_a?(AST::Types::Bot)
2047
+ typing.add_error Diagnostic::Ruby::UnreachableValueBranch.new(
2048
+ node: els,
2049
+ type: else_result.type,
2050
+ location: node.loc.else || raise
2051
+ )
2052
+ end
2010
2053
  end
2011
2054
  end
2012
2055
  else
@@ -2042,21 +2085,29 @@ module Steep
2042
2085
  branch_reachable ||= !truthy.unreachable
2043
2086
  end
2044
2087
 
2045
- if body
2046
- branch_results <<
2088
+ branch_result =
2089
+ if body
2047
2090
  when_clause_constr
2048
2091
  .for_branch(body)
2049
2092
  .update_type_env {|env| env.join(*body_envs) }
2050
2093
  .tap {|constr| typing.add_context_for_node(body, context: constr.context) }
2051
2094
  .synthesize(body, hint: hint)
2052
- else
2053
- branch_results << Pair.new(type: AST::Builtin.nil_type, constr: when_clause_constr)
2054
- end
2095
+ else
2096
+ Pair.new(type: AST::Builtin.nil_type, constr: when_clause_constr)
2097
+ end
2098
+
2099
+ branch_results << branch_result
2055
2100
 
2056
2101
  unless branch_reachable
2057
- typing.add_error(
2058
- Diagnostic::Ruby::UnreachableBranch.new(node: body || when_clause)
2059
- )
2102
+ unless branch_result.type.is_a?(AST::Types::Bot)
2103
+ typing.add_error(
2104
+ Diagnostic::Ruby::UnreachableValueBranch.new(
2105
+ node: when_clause,
2106
+ type: branch_result.type,
2107
+ location: when_clause.location.keyword || raise
2108
+ )
2109
+ )
2110
+ end
2060
2111
  end
2061
2112
  end
2062
2113
 
@@ -3913,13 +3964,15 @@ module Steep
3913
3964
  end
3914
3965
 
3915
3966
  if hint && !fvs.empty?
3916
- if check_relation(sub_type: method_type.type.return_type, super_type: hint, constraints: constraints).success?
3917
- method_type, solved, s = apply_solution(errors, node: node, method_type: method_type) do
3918
- constraints.solution(checker, variables: fvs, context: ccontext)
3967
+ if hint.free_variables.subset?(self_type.free_variables)
3968
+ if check_relation(sub_type: method_type.type.return_type, super_type: hint, constraints: constraints).success?
3969
+ method_type, solved, s = apply_solution(errors, node: node, method_type: method_type) do
3970
+ constraints.solution(checker, variables: fvs, context: ccontext)
3971
+ end
3919
3972
  end
3920
- end
3921
3973
 
3922
- method_type.block or raise
3974
+ method_type.block or raise
3975
+ end
3923
3976
  end
3924
3977
 
3925
3978
  # Method accepts block
@@ -126,7 +126,9 @@ module Steep
126
126
 
127
127
  def upper_bounds
128
128
  table.each_value.with_object({}) do |type_param, bounds|
129
- bounds[type_param.name] = type_param.upper_bound
129
+ if type_param.upper_bound
130
+ bounds[type_param.name] = type_param.upper_bound
131
+ end
130
132
  end
131
133
  end
132
134
 
data/lib/steep/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Steep
2
- VERSION = "1.5.0.pre.4"
2
+ VERSION = "1.5.0.pre.5"
3
3
  end
@@ -62,6 +62,11 @@ module Parser
62
62
  %a{pure} def end: () -> Source::Range?
63
63
  end
64
64
 
65
+ # ```ruby
66
+ # foo ? bar : baz
67
+ # # ^ question
68
+ # # ^ colon
69
+ # ```
65
70
  interface _Ternary
66
71
  %a{pure} def question: () -> Source::Range
67
72
 
@@ -479,6 +479,31 @@ module Steep
479
479
  def header_line: () -> String
480
480
  end
481
481
 
482
+ # The branch is unreachable, but not `bot` type
483
+ #
484
+ # We often have `else` branch to make the code more defensive:
485
+ #
486
+ # ```ruby
487
+ # case value
488
+ # when Integer
489
+ # # ...
490
+ # when String
491
+ # # ...
492
+ # else
493
+ # raise "Cannot happen!"
494
+ # end
495
+ # ```
496
+ #
497
+ # This diagnostic allows writing `raise` or `return`, by checking the type of the branch body is `bot` or not.
498
+ #
499
+ class UnreachableValueBranch < Base
500
+ attr_reader type: AST::Types::t
501
+
502
+ def initialize: (node: Parser::AST::Node, type: AST::Types::t, ?location: location) -> void
503
+
504
+ def header_line: () -> String
505
+ end
506
+
482
507
  class UnexpectedSplat < Base
483
508
  attr_reader type: untyped
484
509
 
@@ -120,18 +120,6 @@
120
120
  message: 'The method parameter has different kind from the declaration `(name:
121
121
  ::String, size: ::Integer) -> void`'
122
122
  code: Ruby::DifferentMethodParameterKind
123
- - file: else_on_exhaustive_case.rb
124
- diagnostics:
125
- - range:
126
- start:
127
- line: 11
128
- character: 2
129
- end:
130
- line: 11
131
- character: 26
132
- severity: ERROR
133
- message: The branch is unreachable
134
- code: Ruby::UnreachableBranch
135
123
  - file: incompatible_annotation.rb
136
124
  diagnostics:
137
125
  - range:
@@ -13,14 +13,14 @@
13
13
  code: Ruby::NoMethod
14
14
  - range:
15
15
  start:
16
- line: 12
17
- character: 2
16
+ line: 11
17
+ character: 0
18
18
  end:
19
- line: 12
20
- character: 10
19
+ line: 11
20
+ character: 4
21
21
  severity: ERROR
22
- message: The branch is unreachable
23
- code: Ruby::UnreachableBranch
22
+ message: The branch may evaluate to a value of `untyped` but unreachable
23
+ code: Ruby::UnreachableValueBranch
24
24
  - range:
25
25
  start:
26
26
  line: 12
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: 1.5.0.pre.4
4
+ version: 1.5.0.pre.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Soutaro Matsumoto
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-07-06 00:00:00.000000000 Z
11
+ date: 2023-07-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser