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

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: 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