steep 1.5.0.pre.4 → 1.5.0.pre.6

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: d00242c259b7c8d2a4a0f7022874ac5bb5f08491ab3edade6165ff1a9fbe89a7
4
+ data.tar.gz: e02d896359c4eb88c730601320bb9137342c19b9ad1956d4ac417ffa5926d74a
5
5
  SHA512:
6
- metadata.gz: 1f0b3400822be747c45ed1d5c2cdfbddf8c0fdf70963061b66308464a18a95e493cc94ad406bbc444f618f562e0f95f276600c27e3ec2269f01504727b0528c7
7
- data.tar.gz: 45b2a32802ea5ab504a8c3ee04f714a87b27227ffa66282d64243e3161aa0628001f1925e3ad7aaf09fcd0fbddc4def36e9b9940c76a87b195c2f51b19006bf9
6
+ metadata.gz: 5be83d84d984d215342c030e9ea590c419a1e12963361266f4bc57e2b2ceec110a3f08313754b2f46a274fc8b0d2acca497c71f2eff24c3fd4505cb1ff2ac5a7
7
+ data.tar.gz: 7a30b064aa7ec6e27e3811e93b1ddf7ff4824cdeb5dfe70075f34311f7e22016bf4f3a2b107307a5247c54532451a25155fc2ba277842fec85f71aa23fdaced1
data/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 1.5.0.pre.6 (2023-07-11)
6
+
7
+ ### Type checker core
8
+
9
+ * Report RBS validation errors in Ruby code ([#859](https://github.com/soutaro/steep/pull/859))
10
+ * Fix proc type assignment ([#858](https://github.com/soutaro/steep/pull/858))
11
+ * Report `UnexpectedKeywordArgument` even if no keyword param is accepted ([#856](https://github.com/soutaro/steep/pull/856))
12
+ * Unfold type alias on unwrap optional ([#855](https://github.com/soutaro/steep/pull/855))
13
+
14
+ ### Language server
15
+
16
+ * Keyword completion in block call ([#865](https://github.com/soutaro/steep/pull/865))
17
+ * Indicate the current or next argument on signature help ([#850](https://github.com/soutaro/steep/pull/850))
18
+ * Support completion for keyword arguments ([#851](https://github.com/soutaro/steep/pull/851))
19
+ * Let hover show the type of method call node ([#864](https://github.com/soutaro/steep/pull/864))
20
+ * Fix UnknownNodeError in SignatureHelp ([#863](https://github.com/soutaro/steep/pull/863))
21
+ * hover: Fix NoMethodError on generating hover for not supported files ([#853](https://github.com/soutaro/steep/pull/853))
22
+
23
+ ## 1.5.0.pre.5 (2023-07-07)
24
+
25
+ ### Type checker core
26
+
27
+ * Unreachability improvements ([#845](https://github.com/soutaro/steep/pull/845))
28
+ * Fix type inference problem ([#843](https://github.com/soutaro/steep/pull/843))
29
+
5
30
  ## 1.5.0.pre.4 (2023-07-06)
6
31
 
7
32
  ### 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.6)
5
5
  activesupport (>= 5.1)
6
6
  concurrent-ruby (>= 1.1.10)
7
7
  csv (>= 3.0.9)
@@ -37,10 +37,12 @@ module Steep
37
37
  ty = ty.map_type_name {|name| resolver.resolve(name, context: context) || name.absolute! }
38
38
 
39
39
  validator = Signature::Validator.new(checker: subtyping)
40
- validator.validate_type(ty)
40
+ validator.rescue_validation_errors do
41
+ validator.validate_type(ty)
42
+ end
41
43
 
42
44
  if validator.has_error?
43
- return
45
+ return validator.each_error
44
46
  end
45
47
 
46
48
  ty = subtyping.factory.type(ty)
@@ -58,7 +60,7 @@ module Steep
58
60
 
59
61
  def types?(context, subtyping, type_vars)
60
62
  case types = types(context, subtyping, type_vars)
61
- when RBS::ParsingError
63
+ when RBS::ParsingError, Enumerator
62
64
  nil
63
65
  else
64
66
  types
@@ -22,10 +22,14 @@ module Steep
22
22
  ty = ty.map_type_name {|name| resolver.resolve(name, context: context) || name.absolute! }
23
23
 
24
24
  validator = Signature::Validator.new(checker: subtyping)
25
- validator.validate_type(ty)
25
+ validator.rescue_validation_errors do
26
+ validator.validate_type(ty)
27
+ end
26
28
 
27
29
  unless validator.has_error?
28
30
  subtyping.factory.type(ty)
31
+ else
32
+ validator.each_error.to_a
29
33
  end
30
34
  else
31
35
  nil
@@ -37,7 +41,7 @@ module Steep
37
41
  def type_syntax?
38
42
  RBS::Parser.parse_type(type_location.buffer, range: type_location.range, variables: [], require_eof: true)
39
43
  true
40
- rescue::RBS::ParsingError
44
+ rescue ::RBS::ParsingError
41
45
  false
42
46
  end
43
47
 
@@ -45,7 +49,7 @@ module Steep
45
49
  type = type(context, subtyping, type_vars)
46
50
 
47
51
  case type
48
- when RBS::ParsingError, nil
52
+ when RBS::ParsingError, nil, Array
49
53
  nil
50
54
  else
51
55
  type
@@ -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
 
@@ -401,6 +401,13 @@ module Steep
401
401
  end
402
402
  when AST::Types::Nil
403
403
  nil
404
+ when AST::Types::Name::Alias
405
+ type_ = expand_alias(type)
406
+ if type_ == type
407
+ type_
408
+ else
409
+ unwrap_optional(type_)
410
+ end
404
411
  else
405
412
  type
406
413
  end
@@ -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
 
@@ -951,6 +964,19 @@ module Steep
951
964
  end
952
965
  end
953
966
 
967
+ class RBSError < Base
968
+ attr_reader :error
969
+
970
+ def initialize(error:, node:, location:)
971
+ @error = error
972
+ super(node: node, location: location)
973
+ end
974
+
975
+ def header_line
976
+ error.header_line
977
+ end
978
+ end
979
+
954
980
  ALL = ObjectSpace.each_object(Class).with_object([]) do |klass, array|
955
981
  if klass < Base
956
982
  array << klass
@@ -968,7 +994,8 @@ module Steep
968
994
  {
969
995
  ImplicitBreakValueMismatch => :warning,
970
996
  FallbackAny => :information,
971
- UnreachableBranch => :warning,
997
+ UnreachableValueBranch => :warning,
998
+ UnreachableBranch => :information,
972
999
  UnknownConstant => :warning,
973
1000
  MethodDefinitionMissing => :information,
974
1001
  FalseAssertion => :information,
@@ -989,6 +1016,7 @@ module Steep
989
1016
  NoMethod => nil,
990
1017
  ImplicitBreakValueMismatch => nil,
991
1018
  FallbackAny => nil,
1019
+ UnreachableValueBranch => nil,
992
1020
  UnreachableBranch => nil,
993
1021
  UnknownConstant => nil,
994
1022
  MethodDefinitionMissing => nil,
@@ -1006,6 +1034,7 @@ module Steep
1006
1034
  NoMethod => nil,
1007
1035
  ImplicitBreakValueMismatch => nil,
1008
1036
  FallbackAny => nil,
1037
+ UnreachableValueBranch => nil,
1009
1038
  UnreachableBranch => nil,
1010
1039
  UnknownConstant => nil,
1011
1040
  MethodDefinitionMissing => nil,
@@ -217,5 +217,28 @@ module Steep
217
217
  false
218
218
  end
219
219
  end
220
+
221
+ def deconstruct_sendish_and_block_nodes(*nodes)
222
+ send_node, block_node = nodes.take(2)
223
+
224
+ if send_node
225
+ case send_node.type
226
+ when :send, :csend, :super
227
+ if block_node
228
+ case block_node.type
229
+ when :block, :numblock
230
+ if send_node.equal?(block_node.children[0])
231
+ return [send_node, block_node]
232
+ end
233
+ end
234
+ end
235
+
236
+ [send_node, nil]
237
+ when :zsuper
238
+ # zsuper doesn't receive block
239
+ [send_node, nil]
240
+ end
241
+ end
242
+ end
220
243
  end
221
244
  end
@@ -361,6 +361,17 @@ module Steep
361
361
  new_text: item.identifier.to_s
362
362
  )
363
363
  )
364
+ when Services::CompletionProvider::KeywordArgumentItem
365
+ LSP::Interface::CompletionItem.new(
366
+ label: item.identifier.to_s,
367
+ kind: LSP::Constant::CompletionItemKind::FIELD,
368
+ label_details: LSP::Interface::CompletionItemLabelDetails.new(description: 'Keyword argument'),
369
+ documentation: LSPFormatter.markup_content { LSPFormatter.format_completion_docs(item) },
370
+ text_edit: LSP::Interface::TextEdit.new(
371
+ range: range,
372
+ new_text: item.identifier.to_s
373
+ )
374
+ )
364
375
  when Services::CompletionProvider::TypeNameItem
365
376
  kind =
366
377
  case
@@ -397,6 +408,8 @@ module Steep
397
408
  signatures = items.map do |item|
398
409
  LSP::Interface::SignatureInformation.new(
399
410
  label: "(#{item.method_type.type.param_to_s})",
411
+ parameters: item.parameters.map { |param| LSP::Interface::ParameterInformation.new(label: param)},
412
+ active_parameter: item.active_parameter,
400
413
  documentation: item.comment&.yield_self do |comment|
401
414
  LSP::Interface::MarkupContent.new(
402
415
  kind: LSP::Constant::MarkupKind::MARKDOWN,
@@ -28,7 +28,16 @@ module Steep
28
28
 
29
29
  case call
30
30
  when TypeInference::MethodCall::Typed
31
+ io.puts <<~MD
32
+ ```rbs
33
+ #{call.actual_method_type.type.return_type}
34
+ ```
35
+
36
+ ----
37
+ MD
38
+
31
39
  method_types = call.method_decls.map(&:method_type)
40
+
32
41
  if call.is_a?(TypeInference::MethodCall::Special)
33
42
  method_types = [
34
43
  call.actual_method_type.with(
@@ -250,6 +259,10 @@ module Steep
250
259
  end
251
260
 
252
261
  io.string
262
+ when Services::CompletionProvider::KeywordArgumentItem
263
+ <<~MD
264
+ **Keyword argument**: `#{item.identifier}`
265
+ MD
253
266
  end
254
267
  end
255
268
 
@@ -1,6 +1,8 @@
1
1
  module Steep
2
2
  module Services
3
3
  class CompletionProvider
4
+ include NodeHelper
5
+
4
6
  Position = _ = Struct.new(:line, :column, keyword_init: true) do
5
7
  # @implements Position
6
8
  def -(size)
@@ -11,6 +13,7 @@ module Steep
11
13
  Range = _ = Struct.new(:start, :end, keyword_init: true)
12
14
 
13
15
  InstanceVariableItem = _ = Struct.new(:identifier, :range, :type, keyword_init: true)
16
+ KeywordArgumentItem = _ = Struct.new(:identifier, :range, keyword_init: true)
14
17
  LocalVariableItem = _ = Struct.new(:identifier, :range, :type, keyword_init: true)
15
18
  ConstantItem = _ = Struct.new(:env, :identifier, :range, :type, :full_name, keyword_init: true) do
16
19
  # @implements ConstantItem
@@ -267,7 +270,9 @@ module Steep
267
270
  []
268
271
  end
269
272
  else
270
- []
273
+ items = [] #: Array[item]
274
+ items_for_following_keyword_arguments(source_text, index: index, line: line, column: column, items: items)
275
+ items
271
276
  end
272
277
  end
273
278
  end
@@ -302,10 +307,10 @@ module Steep
302
307
  end
303
308
 
304
309
  def items_for_trigger(position:)
305
- node, *_parents = source.find_nodes(line: position.line, column: position.column)
310
+ node, *parents = source.find_nodes(line: position.line, column: position.column)
306
311
  node ||= source.node
307
312
 
308
- return [] unless node
313
+ return [] unless node && parents
309
314
 
310
315
  items = [] #: Array[item]
311
316
 
@@ -319,6 +324,16 @@ module Steep
319
324
  method_items_for_receiver_type(context.self_type, include_private: true, prefix: prefix, position: position, items: items)
320
325
  local_variable_items_for_context(context, position: position, prefix: prefix, items: items)
321
326
 
327
+ if (send_node, block_node = deconstruct_sendish_and_block_nodes(*parents))
328
+ keyword_argument_items_for_method(
329
+ call_node: block_node || send_node,
330
+ send_node: send_node,
331
+ position: position,
332
+ prefix: prefix,
333
+ items: items
334
+ )
335
+ end
336
+
322
337
  when node.type == :lvar && at_end?(position, of: node.loc)
323
338
  # foo ← (lvar)
324
339
  local_variable_items_for_context(context, position: position, prefix: node.children[0].to_s, items: items)
@@ -527,6 +542,37 @@ module Steep
527
542
  items
528
543
  end
529
544
 
545
+ def items_for_following_keyword_arguments(text, index:, line:, column:, items:)
546
+ return if text[index - 1] !~ /[a-zA-Z0-9]/
547
+
548
+ text = text.dup
549
+ argname = [] #: Array[String]
550
+ while text[index - 1] =~ /[a-zA-Z0-9]/
551
+ argname.unshift(text[index - 1] || '')
552
+ source_text[index - 1] = " "
553
+ index -= 1
554
+ end
555
+
556
+ begin
557
+ type_check!(source_text, line: line, column: column)
558
+ rescue Parser::SyntaxError
559
+ return
560
+ end
561
+
562
+ if nodes = source.find_nodes(line: line, column: column)
563
+ if (send_node, block_node = deconstruct_sendish_and_block_nodes(*nodes))
564
+ position = Position.new(line: line, column: column)
565
+ keyword_argument_items_for_method(
566
+ call_node: block_node || send_node,
567
+ send_node: send_node,
568
+ position: position,
569
+ prefix: argname.join,
570
+ items: items
571
+ )
572
+ end
573
+ end
574
+ end
575
+
530
576
  def method_items_for_receiver_type(type, include_private:, prefix:, position:, items:)
531
577
  range = range_for(position, prefix: prefix)
532
578
  context = typing.context_at(line: position.line, column: position.column)
@@ -641,6 +687,42 @@ module Steep
641
687
  end
642
688
  end
643
689
 
690
+ def keyword_argument_items_for_method(call_node:, send_node:, position:, prefix:, items:)
691
+ receiver_node, method_name, argument_nodes = deconstruct_send_node!(send_node)
692
+
693
+ call = typing.call_of(node: call_node)
694
+
695
+ case call
696
+ when TypeInference::MethodCall::Typed, TypeInference::MethodCall::Error
697
+ type = call.receiver_type
698
+ shape = subtyping.builder.shape(
699
+ type,
700
+ public_only: !!receiver_node,
701
+ config: Interface::Builder::Config.new(self_type: type, class_type: nil, instance_type: nil, variable_bounds: {})
702
+ )
703
+ if shape
704
+ if method = shape.methods[call.method_name]
705
+ method.method_types.each.with_index do |method_type, i|
706
+ defn = method_type.method_decls.to_a[0]&.method_def
707
+ if defn
708
+ range = range_for(position, prefix: prefix)
709
+ kwargs = argument_nodes.find { |arg| arg.type == :kwargs }&.children || []
710
+ used_kwargs = kwargs.filter_map { |arg| arg.type == :pair && arg.children.first.children.first }
711
+
712
+ kwargs = defn.type.type.required_keywords.keys + defn.type.type.optional_keywords.keys
713
+ kwargs.each do |name|
714
+ if name.to_s.start_with?(prefix) && !used_kwargs.include?(name)
715
+ items << KeywordArgumentItem.new(identifier: "#{name}:", range: range)
716
+ end
717
+ end
718
+ end
719
+ end
720
+ end
721
+ end
722
+ end
723
+ end
724
+
725
+
644
726
  def index_for(string, line:, column:)
645
727
  index = 0
646
728
 
@@ -110,7 +110,7 @@ module Steep
110
110
  end
111
111
 
112
112
  def content_for(target:, path:, line:, column:)
113
- file = service.source_files[path]
113
+ file = service.source_files[path] or return
114
114
  typing = typecheck(target, path: path, content: file.content, line: line, column: column) or return
115
115
  node, *parents = typing.source.find_nodes(line: line, column: column)
116
116
 
@@ -3,7 +3,21 @@ module Steep
3
3
  class SignatureHelpProvider
4
4
  MethodCall = TypeInference::MethodCall
5
5
 
6
- Item = _ = Struct.new(:method_type, :comment)
6
+ Item = _ = Struct.new(:method_type, :comment, :active_parameter) do
7
+ # @implements Item
8
+
9
+ def parameters
10
+ arguments = [] #: Array[String]
11
+ arguments.push(*method_type.type.required_positionals.map(&:to_s))
12
+ arguments.push(*method_type.type.optional_positionals.map {|p| "?#{p}"})
13
+ arguments.push("*#{self.method_type.type.rest_positionals}") if method_type.type.rest_positionals
14
+ arguments.push(*method_type.type.trailing_positionals.map(&:to_s))
15
+ arguments.push(*method_type.type.required_keywords.map {|name, param| "#{name}: #{param}" })
16
+ arguments.push(*method_type.type.optional_keywords.map {|name, param| "?#{name}: #{param}" })
17
+ arguments.push("**#{method_type.type.rest_keywords}") if method_type.type.rest_keywords
18
+ arguments
19
+ end
20
+ end
7
21
 
8
22
  attr_reader :source, :path, :subtyping, :typing, :buffer
9
23
 
@@ -23,12 +37,14 @@ module Steep
23
37
  return unless nodes
24
38
 
25
39
  typing = type_check!(line: line, column: column)
40
+ argument_nodes = [] #: Array[Parser::AST::Node]
26
41
 
27
42
  while true
28
43
  node = nodes.shift()
29
44
  parent = nodes.first
30
45
 
31
46
  node or return
47
+ argument_nodes << node
32
48
 
33
49
  if node.type == :send || node.type == :csend
34
50
  pos = buffer.loc_to_pos([line, column])
@@ -40,12 +56,13 @@ module Steep
40
56
  # Given position is between open/close parens of args of send node
41
57
 
42
58
  if parent && (parent.type == :block || parent.type == :numblock)
43
- send_node = parent.children[0]
59
+ send_node = parent
44
60
  else
45
61
  send_node = node
46
62
  end
47
63
 
48
- return signature_help_for(send_node, typing)
64
+ last_argument_nodes = last_argument_nodes_for(argument_nodes: argument_nodes, line: line, column: column)
65
+ return signature_help_for(send_node, argument_nodes, last_argument_nodes, typing)
49
66
  end
50
67
  end
51
68
  end
@@ -58,7 +75,24 @@ module Steep
58
75
  TypeCheckService.type_check(source: source, subtyping: subtyping, constant_resolver: resolver)
59
76
  end
60
77
 
61
- def signature_help_for(node, typing)
78
+ def last_argument_nodes_for(argument_nodes:, line:, column:)
79
+ return unless argument_nodes.last.children[2] # No arguments
80
+ return argument_nodes if argument_nodes.size > 1 # Cursor is on the last argument
81
+
82
+ pos = buffer.loc_to_pos([line, column])
83
+
84
+ while true
85
+ pos -= 1
86
+ line, column = buffer.pos_to_loc(pos)
87
+ nodes = source.find_nodes(line: line, column: column)
88
+ return unless nodes
89
+
90
+ index = nodes.index { |n| n.type == :send || n.type == :csend }
91
+ return nodes[..index] if index.to_i > 0
92
+ end
93
+ end
94
+
95
+ def signature_help_for(node, argument, last_argument, typing)
62
96
  call = typing.call_of(node: node)
63
97
  context = typing.context_at(line: node.loc.expression.line, column: node.loc.expression.column)
64
98
 
@@ -82,7 +116,8 @@ module Steep
82
116
  method.method_types.each.with_index do |method_type, i|
83
117
  defn = method_type.method_decls.to_a[0]&.method_def
84
118
 
85
- items << Item.new(subtyping.factory.method_type_1(method_type), defn&.comment)
119
+ active_parameter = active_parameter_for(defn&.type, argument, last_argument, node)
120
+ items << Item.new(subtyping.factory.method_type_1(method_type), defn&.comment, active_parameter)
86
121
 
87
122
  if call.is_a?(MethodCall::Typed)
88
123
  if method_type.method_decls.intersect?(call.method_decls)
@@ -98,6 +133,66 @@ module Steep
98
133
 
99
134
  [items, index]
100
135
  end
136
+
137
+ def active_parameter_for(method_type, argument_nodes, last_argument_nodes, node)
138
+ return unless method_type
139
+
140
+ positionals = method_type.type.required_positionals.size + method_type.type.optional_positionals.size + (method_type.type.rest_positionals ? 1 : 0) + method_type.type.trailing_positionals.size
141
+
142
+ if argument_nodes.size == 1
143
+ # Cursor is not on the argument (maybe on comma after argument)
144
+ return 0 if last_argument_nodes.nil? # No arguments
145
+
146
+ case last_argument_nodes[-2].type
147
+ when :splat
148
+ method_type.type.required_positionals.size + method_type.type.optional_positionals.size + 1 if method_type.type.rest_positionals
149
+ when :kwargs
150
+ case last_argument_nodes[-3].type
151
+ when :pair
152
+ argname = last_argument_nodes[-3].children.first.children.first
153
+ if method_type.type.required_keywords[argname]
154
+ positionals + method_type.type.required_keywords.keys.index(argname).to_i + 1
155
+ elsif method_type.type.optional_keywords[argname]
156
+ positionals + method_type.type.required_keywords.size + method_type.type.optional_keywords.keys.index(argname).to_i + 1
157
+ elsif method_type.type.rest_keywords
158
+ positionals + method_type.type.required_keywords.size + method_type.type.optional_keywords.size
159
+ end
160
+ when :kwsplat
161
+ positionals + method_type.type.required_keywords.size + method_type.type.optional_keywords.size if method_type.type.rest_keywords
162
+ end
163
+ else
164
+ pos = node.children[2...].index { |c| c.location == last_argument_nodes[-2].location }.to_i
165
+ if method_type.type.rest_positionals
166
+ [pos + 1, positionals - 1].min
167
+ else
168
+ [pos + 1, positionals].min
169
+ end
170
+ end
171
+ else
172
+ # Cursor is on the argument
173
+ case argument_nodes[-2].type
174
+ when :splat
175
+ method_type.type.required_positionals.size + method_type.type.optional_positionals.size if method_type.type.rest_positionals
176
+ when :kwargs
177
+ case argument_nodes[-3].type
178
+ when :pair
179
+ argname = argument_nodes[-3].children.first.children.first
180
+ if method_type.type.required_keywords[argname]
181
+ positionals + method_type.type.required_keywords.keys.index(argname).to_i
182
+ elsif method_type.type.optional_keywords[argname]
183
+ positionals + method_type.type.required_keywords.size + method_type.type.optional_keywords.keys.index(argname).to_i
184
+ elsif method_type.type.rest_keywords
185
+ positionals + method_type.type.required_keywords.size + method_type.type.optional_keywords.size
186
+ end
187
+ when :kwsplat
188
+ positionals + method_type.type.required_keywords.size + method_type.type.optional_keywords.size if method_type.type.rest_keywords
189
+ end
190
+ else
191
+ pos = node.children[2...].index { |c| c.location == argument_nodes[-2].location }.to_i
192
+ [pos, positionals - 1].min
193
+ end
194
+ end
195
+ end
101
196
  end
102
197
  end
103
198
  end
@@ -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
@@ -1918,10 +1950,6 @@ module Steep
1918
1950
  branch_results = [] #: Array[Pair]
1919
1951
 
1920
1952
  cond_type, constr = constr.synthesize(cond)
1921
- _, cond_vars = interpreter.decompose_value(cond)
1922
- SPECIAL_LVAR_NAMES.each do |name|
1923
- cond_vars.delete(name)
1924
- end
1925
1953
 
1926
1954
  var_name = :"_a[#{SecureRandom.alphanumeric(4)}]"
1927
1955
  var_cond, value_node = transform_condition_node(cond, var_name)
@@ -1961,19 +1989,25 @@ module Steep
1961
1989
  next_branch_reachable &&= false_branch_reachable
1962
1990
  body_constr = when_constr.update_type_env {|env| env.join(*test_envs) }
1963
1991
 
1964
- if body
1965
- branch_results <<
1992
+ branch_result =
1993
+ if body
1966
1994
  body_constr
1967
1995
  .for_branch(body)
1968
1996
  .tap {|constr| typing.add_context_for_node(body, context: constr.context) }
1969
1997
  .synthesize(body, hint: hint)
1970
- else
1971
- branch_results << Pair.new(type: AST::Builtin.nil_type, constr: body_constr)
1972
- end
1998
+ else
1999
+ Pair.new(type: AST::Builtin.nil_type, constr: body_constr)
2000
+ end
1973
2001
 
1974
- unless branch_reachable
2002
+ branch_results << branch_result
2003
+
2004
+ if !branch_reachable && !branch_result.type.is_a?(AST::Types::Bot)
1975
2005
  typing.add_error(
1976
- Diagnostic::Ruby::UnreachableBranch.new(node: body || clause)
2006
+ Diagnostic::Ruby::UnreachableValueBranch.new(
2007
+ node: clause,
2008
+ type: branch_result.type,
2009
+ location: clause.location.keyword
2010
+ )
1977
2011
  )
1978
2012
  end
1979
2013
 
@@ -2004,9 +2038,14 @@ module Steep
2004
2038
  # `else` may present even if it's empty
2005
2039
  if loc.else
2006
2040
  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)
2041
+ else_result or raise
2042
+ unless else_result.type.is_a?(AST::Types::Bot)
2043
+ typing.add_error Diagnostic::Ruby::UnreachableValueBranch.new(
2044
+ node: els,
2045
+ type: else_result.type,
2046
+ location: node.loc.else || raise
2047
+ )
2048
+ end
2010
2049
  end
2011
2050
  end
2012
2051
  else
@@ -2042,21 +2081,29 @@ module Steep
2042
2081
  branch_reachable ||= !truthy.unreachable
2043
2082
  end
2044
2083
 
2045
- if body
2046
- branch_results <<
2084
+ branch_result =
2085
+ if body
2047
2086
  when_clause_constr
2048
2087
  .for_branch(body)
2049
2088
  .update_type_env {|env| env.join(*body_envs) }
2050
2089
  .tap {|constr| typing.add_context_for_node(body, context: constr.context) }
2051
2090
  .synthesize(body, hint: hint)
2052
- else
2053
- branch_results << Pair.new(type: AST::Builtin.nil_type, constr: when_clause_constr)
2054
- end
2091
+ else
2092
+ Pair.new(type: AST::Builtin.nil_type, constr: when_clause_constr)
2093
+ end
2094
+
2095
+ branch_results << branch_result
2055
2096
 
2056
2097
  unless branch_reachable
2057
- typing.add_error(
2058
- Diagnostic::Ruby::UnreachableBranch.new(node: body || when_clause)
2059
- )
2098
+ unless branch_result.type.is_a?(AST::Types::Bot)
2099
+ typing.add_error(
2100
+ Diagnostic::Ruby::UnreachableValueBranch.new(
2101
+ node: when_clause,
2102
+ type: branch_result.type,
2103
+ location: when_clause.location.keyword || raise
2104
+ )
2105
+ )
2106
+ end
2060
2107
  end
2061
2108
  end
2062
2109
 
@@ -2570,7 +2617,26 @@ module Steep
2570
2617
  # @type var as_type: AST::Node::TypeAssertion
2571
2618
  asserted_node, as_type = node.children
2572
2619
 
2573
- if type = as_type.type?(module_context.nesting, checker, [])
2620
+ type = as_type.type(module_context.nesting, checker, [])
2621
+
2622
+ case type
2623
+ when Array
2624
+ type.each do |error|
2625
+ typing.add_error(
2626
+ Diagnostic::Ruby::RBSError.new(
2627
+ error: error,
2628
+ node: node,
2629
+ location: error.location || raise
2630
+ )
2631
+ )
2632
+ end
2633
+
2634
+ synthesize(asserted_node, hint: hint)
2635
+
2636
+ when nil, RBS::ParsingError
2637
+ synthesize(asserted_node, hint: hint)
2638
+
2639
+ else
2574
2640
  actual_type, constr = synthesize(asserted_node, hint: type)
2575
2641
 
2576
2642
  if no_subtyping?(sub_type: type, super_type: actual_type) && no_subtyping?(sub_type: actual_type, super_type: type)
@@ -2584,22 +2650,34 @@ module Steep
2584
2650
  end
2585
2651
 
2586
2652
  constr.add_typing(node, type: type)
2587
- else
2588
- synthesize(asserted_node, hint: hint)
2589
2653
  end
2590
2654
  end
2591
2655
 
2592
- when :block, :numblock, :send, :csend
2593
- synthesize_sendish(node, hint: hint, tapp: nil)
2594
-
2595
2656
  when :tapp
2596
2657
  yield_self do
2658
+ # @type var tapp: AST::Node::TypeApplication
2597
2659
  sendish, tapp = node.children
2660
+
2661
+ if (array = tapp.types(module_context.nesting, checker, [])).is_a?(Enumerator)
2662
+ array.each do |error|
2663
+ typing.add_error(
2664
+ Diagnostic::Ruby::RBSError.new(
2665
+ error: error,
2666
+ node: node,
2667
+ location: error.location || raise
2668
+ )
2669
+ )
2670
+ end
2671
+ end
2672
+
2598
2673
  type, constr = synthesize_sendish(sendish, hint: hint, tapp: tapp)
2599
2674
 
2600
2675
  constr.add_typing(node, type: type)
2601
2676
  end
2602
2677
 
2678
+ when :block, :numblock, :send, :csend
2679
+ synthesize_sendish(node, hint: hint, tapp: nil)
2680
+
2603
2681
  when :forwarded_args, :forward_arg
2604
2682
  add_typing(node, type: AST::Builtin.any_type)
2605
2683
 
@@ -3026,7 +3104,8 @@ module Steep
3026
3104
  type_hint = deep_expand_alias(type_hint) || type_hint
3027
3105
 
3028
3106
  procs = flatten_union(type_hint).select do |type|
3029
- check_relation(sub_type: type, super_type: AST::Builtin::Proc.instance_type).success?
3107
+ check_relation(sub_type: type, super_type: AST::Builtin::Proc.instance_type).success? &&
3108
+ !type.is_a?(AST::Types::Any)
3030
3109
  end
3031
3110
 
3032
3111
  proc_instances, proc_types = procs.partition {|type| AST::Builtin::Proc.instance_type?(type) }
@@ -3913,13 +3992,15 @@ module Steep
3913
3992
  end
3914
3993
 
3915
3994
  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)
3995
+ if hint.free_variables.subset?(self_type.free_variables)
3996
+ if check_relation(sub_type: method_type.type.return_type, super_type: hint, constraints: constraints).success?
3997
+ method_type, solved, s = apply_solution(errors, node: node, method_type: method_type) do
3998
+ constraints.solution(checker, variables: fvs, context: ccontext)
3999
+ end
3919
4000
  end
3920
- end
3921
4001
 
3922
- method_type.block or raise
4002
+ method_type.block or raise
4003
+ end
3923
4004
  end
3924
4005
 
3925
4006
  # 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
 
@@ -681,7 +681,15 @@ module Steep
681
681
  when KeywordArgs::MissingKeyword
682
682
  missing_keywords.push(*error.keywords.to_a)
683
683
  when PositionalArgs::UnexpectedArg
684
- diagnostics << Diagnostic::Ruby::UnexpectedPositionalArgument.new(node: error.node, params: params)
684
+ if error.node.type == :kwargs
685
+ error.node.children.each do |kwarg|
686
+ if kwarg.type == :pair
687
+ diagnostics << Diagnostic::Ruby::UnexpectedKeywordArgument.new(node: kwarg, params: params)
688
+ end
689
+ end
690
+ else
691
+ diagnostics << Diagnostic::Ruby::UnexpectedPositionalArgument.new(node: error.node, params: params)
692
+ end
685
693
  when PositionalArgs::MissingArg
686
694
  diagnostics << Diagnostic::Ruby::InsufficientPositionalArguments.new(node: node, params: params)
687
695
  end
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.6"
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
 
@@ -12,7 +12,7 @@ module Steep
12
12
 
13
13
  def initialize: (RBS::Location[untyped, untyped]) -> void
14
14
 
15
- def types: (RBS::Resolver::context, Subtyping::Check, Array[Symbol] type_vars) -> (Array[Types::t] | RBS::ParsingError | nil)
15
+ def types: (RBS::Resolver::context, Subtyping::Check, Array[Symbol] type_vars) -> (Array[Types::t] | RBS::ParsingError | Enumerator[Diagnostic::Signature::Base, void] | nil)
16
16
 
17
17
  def types?: (RBS::Resolver::context, Subtyping::Check, Array[Symbol] type_vars) -> Array[Types::t]?
18
18
 
@@ -10,7 +10,7 @@ module Steep
10
10
 
11
11
  def initialize: (RBS::Location[untyped, untyped]) -> void
12
12
 
13
- def type: (RBS::Resolver::context, Subtyping::Check, Array[Symbol] type_vars) -> (Types::t | RBS::ParsingError | nil)
13
+ def type: (RBS::Resolver::context, Subtyping::Check, Array[Symbol] type_vars) -> (Types::t | RBS::ParsingError | Array[Diagnostic::Signature::Base] | nil)
14
14
 
15
15
  def type?: (RBS::Resolver::context, Subtyping::Check, Array[Symbol] type_vars) -> Types::t?
16
16
 
@@ -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
 
@@ -597,6 +622,14 @@ module Steep
597
622
  def initialize: (hint_type: AST::Types::t, node: Parser::AST::Node) -> void
598
623
  end
599
624
 
625
+ # RBS embedded in the Ruby code has validation error
626
+ #
627
+ class RBSError < Base
628
+ attr_reader error: Signature::Base
629
+
630
+ def initialize: (error: Signature::Base, node: Parser::AST::Node, location: location) -> void
631
+ end
632
+
600
633
  # Argument forwarding `...` cannot be done safely, because of
601
634
  #
602
635
  # 1. The arguments are incompatible, or
@@ -65,5 +65,14 @@ module Steep
65
65
  def deconstruct_send_node!: (Node) -> [Node?, Symbol, Array[Node], send_loc]
66
66
 
67
67
  def test_send_node: (Node) { (Node?, Symbol, Array[Node], send_loc) -> bool } -> bool
68
+
69
+ # Deconstruct sendish node and it's associated block node
70
+ #
71
+ # Receives a sequence of node tree where the leaf node comes first.
72
+ # If the first node is `send`, `csend`, `super`, or `zsuper`, it is the sendish node.
73
+ #
74
+ # If the next node is a `block` or `numblock` that is associated to the *sendish node*, it is the block node.
75
+ #
76
+ def deconstruct_sendish_and_block_nodes: (*Parser::AST::Node) -> [Parser::AST::Node, Parser::AST::Node?]?
68
77
  end
69
78
  end
@@ -4,6 +4,8 @@ use Steep::TypeInference::MethodCall
4
4
  module Steep
5
5
  module Services
6
6
  class CompletionProvider
7
+ include NodeHelper
8
+
7
9
  # Cursor position
8
10
  class Position
9
11
  attr_reader line: Integer
@@ -33,6 +35,14 @@ module Steep
33
35
  def initialize: (identifier: Symbol, range: Range, type: AST::Types::t) -> void
34
36
  end
35
37
 
38
+ class KeywordArgumentItem
39
+ attr_reader identifier: String
40
+
41
+ attr_reader range: Range
42
+
43
+ def initialize: (identifier: String, range: Range) -> void
44
+ end
45
+
36
46
  class LocalVariableItem
37
47
  attr_reader identifier: Symbol
38
48
 
@@ -169,6 +179,7 @@ module Steep
169
179
  end
170
180
 
171
181
  type item = InstanceVariableItem
182
+ | KeywordArgumentItem
172
183
  | LocalVariableItem
173
184
  | ConstantItem
174
185
  | SimpleMethodNameItem
@@ -216,6 +227,8 @@ module Steep
216
227
 
217
228
  def items_for_rbs: (position: Position, buffer: RBS::Buffer) -> Array[item]
218
229
 
230
+ def items_for_following_keyword_arguments: (String text, index: Integer, line: Integer, column: Integer, items: Array[item]) -> void
231
+
219
232
  def method_items_for_receiver_type: (AST::Types::t, include_private: bool, prefix: String, position: Position, items: Array[item]) -> void
220
233
 
221
234
  def word_name?: (String name) -> bool
@@ -226,12 +239,13 @@ module Steep
226
239
 
227
240
  def instance_variable_items_for_context: (TypeInference::Context context, position: Position, prefix: String, items: Array[item]) -> void
228
241
 
242
+ def keyword_argument_items_for_method: (call_node: Parser::AST::Node, send_node: Parser::AST::Node, position: Position, prefix: String, items: Array[item]) -> void
243
+
229
244
  def index_for: (String, line: Integer, column: Integer) -> Integer
230
245
 
231
246
  def disallowed_method?: (Symbol name) -> bool
232
247
 
233
248
  def unwrap_optional: (AST::Types::t) -> AST::Types::t
234
-
235
249
  end
236
250
  end
237
251
  end
@@ -10,7 +10,11 @@ module Steep
10
10
 
11
11
  attr_reader comment: RBS::AST::Comment?
12
12
 
13
- def initialize: (RBS::MethodType, RBS::AST::Comment?) -> void
13
+ attr_reader active_parameter: Integer?
14
+
15
+ def initialize: (RBS::MethodType, RBS::AST::Comment?, Integer?) -> void
16
+
17
+ def parameters: () -> Array[String]
14
18
  end
15
19
 
16
20
  attr_reader source: Source
@@ -31,7 +35,13 @@ module Steep
31
35
 
32
36
  private
33
37
 
34
- def signature_help_for: (Parser::AST::Node, Typing) -> [Array[Item], Integer?]?
38
+ def active_parameter_for: (RBS::MethodType?, Array[Parser::AST::Node], Array[Parser::AST::Node]?, Parser::AST::Node) -> Integer?
39
+
40
+ def arguments_for: (RBS::MethodType) -> Array[String]
41
+
42
+ def last_argument_nodes_for: (argument_nodes: Array[Parser::AST::Node], line: Integer, column: Integer) -> Array[Parser::AST::Node]?
43
+
44
+ def signature_help_for: (Parser::AST::Node, Array[Parser::AST::Node], Array[Parser::AST::Node]?, Typing) -> [Array[Item], Integer?]?
35
45
 
36
46
  def type_check!: (line: Integer, column: Integer) -> Typing
37
47
  end
@@ -38,7 +38,7 @@ module Steep
38
38
 
39
39
  def validate_type_application: (RBS::Types::t) -> void
40
40
 
41
- def validate_type: (RBS::Types::t `type`) -> untyped
41
+ def validate_type: (RBS::Types::t `type`) -> void
42
42
 
43
43
  def ancestor_to_type: (RBS::Definition::Ancestor::t ancestor) -> (AST::Types::Name::Interface | AST::Types::Name::Instance)
44
44
 
@@ -69,7 +69,7 @@ module Steep
69
69
  def validate_one_alias: (RBS::TypeName name, ?RBS::Environment::TypeAliasEntry entry) -> void
70
70
 
71
71
  def validate_one_class_decl: (RBS::TypeName) -> void
72
-
72
+
73
73
  def validate_one_class_alias: (RBS::TypeName, RBS::Environment::ClassAliasEntry | RBS::Environment::ModuleAliasEntry) -> void
74
74
 
75
75
  def validate_alias: () -> void
@@ -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.6
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-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser