steep 1.10.0 → 2.1.0.dev.1

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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +84 -1
  3. data/CLAUDE.md +114 -0
  4. data/README.md +2 -2
  5. data/Rakefile +365 -150
  6. data/Steepfile +13 -13
  7. data/doc/release.md +264 -0
  8. data/lib/steep/annotation_parser.rb +5 -1
  9. data/lib/steep/annotations_helper.rb +12 -2
  10. data/lib/steep/ast/node/type_application.rb +22 -16
  11. data/lib/steep/ast/node/type_assertion.rb +7 -4
  12. data/lib/steep/ast/types/factory.rb +3 -2
  13. data/lib/steep/cli.rb +246 -2
  14. data/lib/steep/daemon/configuration.rb +19 -0
  15. data/lib/steep/daemon/server.rb +476 -0
  16. data/lib/steep/daemon.rb +201 -0
  17. data/lib/steep/diagnostic/ruby.rb +54 -12
  18. data/lib/steep/diagnostic/signature.rb +31 -8
  19. data/lib/steep/drivers/check.rb +301 -140
  20. data/lib/steep/drivers/print_project.rb +9 -10
  21. data/lib/steep/drivers/query.rb +102 -0
  22. data/lib/steep/drivers/start_server.rb +19 -0
  23. data/lib/steep/drivers/stop_server.rb +20 -0
  24. data/lib/steep/drivers/watch.rb +2 -2
  25. data/lib/steep/index/rbs_index.rb +38 -13
  26. data/lib/steep/index/signature_symbol_provider.rb +24 -3
  27. data/lib/steep/interface/builder.rb +48 -15
  28. data/lib/steep/interface/shape.rb +13 -5
  29. data/lib/steep/locator.rb +377 -0
  30. data/lib/steep/node_helper.rb +2 -2
  31. data/lib/steep/project/dsl.rb +26 -5
  32. data/lib/steep/project/group.rb +8 -2
  33. data/lib/steep/project/target.rb +16 -2
  34. data/lib/steep/project.rb +21 -2
  35. data/lib/steep/server/base_worker.rb +2 -2
  36. data/lib/steep/server/change_buffer.rb +2 -1
  37. data/lib/steep/server/custom_methods.rb +12 -0
  38. data/lib/steep/server/inline_source_change_detector.rb +94 -0
  39. data/lib/steep/server/interaction_worker.rb +51 -74
  40. data/lib/steep/server/lsp_formatter.rb +48 -12
  41. data/lib/steep/server/master.rb +100 -18
  42. data/lib/steep/server/target_group_files.rb +124 -151
  43. data/lib/steep/server/type_check_controller.rb +276 -123
  44. data/lib/steep/server/type_check_worker.rb +104 -3
  45. data/lib/steep/services/completion_provider/rbs.rb +74 -0
  46. data/lib/steep/services/completion_provider/ruby.rb +652 -0
  47. data/lib/steep/services/completion_provider/type_name.rb +243 -0
  48. data/lib/steep/services/completion_provider.rb +39 -662
  49. data/lib/steep/services/content_change.rb +14 -1
  50. data/lib/steep/services/file_loader.rb +4 -2
  51. data/lib/steep/services/goto_service.rb +271 -68
  52. data/lib/steep/services/hover_provider/content.rb +67 -0
  53. data/lib/steep/services/hover_provider/rbs.rb +8 -9
  54. data/lib/steep/services/hover_provider/ruby.rb +124 -65
  55. data/lib/steep/services/hover_provider/singleton_methods.rb +4 -0
  56. data/lib/steep/services/signature_help_provider.rb +1 -1
  57. data/lib/steep/services/signature_service.rb +129 -54
  58. data/lib/steep/services/type_check_service.rb +72 -27
  59. data/lib/steep/signature/validator.rb +30 -18
  60. data/lib/steep/source/ignore_ranges.rb +14 -4
  61. data/lib/steep/source.rb +21 -7
  62. data/lib/steep/tagged_logging.rb +39 -0
  63. data/lib/steep/type_construction.rb +111 -24
  64. data/lib/steep/type_inference/block_params.rb +7 -7
  65. data/lib/steep/type_inference/context.rb +4 -2
  66. data/lib/steep/type_inference/logic_type_interpreter.rb +24 -3
  67. data/lib/steep/type_inference/method_call.rb +4 -0
  68. data/lib/steep/type_inference/type_env.rb +1 -1
  69. data/lib/steep/typing.rb +3 -5
  70. data/lib/steep/version.rb +1 -1
  71. data/lib/steep.rb +42 -32
  72. data/manual/ruby-diagnostics.md +67 -0
  73. data/sample/Steepfile +1 -0
  74. data/sample/lib/conference.rb +1 -0
  75. data/sample/lib/deprecated.rb +6 -0
  76. data/sample/lib/inline.rb +43 -0
  77. data/sample/sig/generics.rbs +3 -0
  78. data/steep.gemspec +4 -5
  79. metadata +27 -26
  80. data/lib/steep/services/type_name_completion.rb +0 -236
@@ -49,7 +49,7 @@ module Steep
49
49
  end
50
50
 
51
51
  def type_name_resolver
52
- @type_name_resolver ||= RBS::Resolver::TypeNameResolver.new(env)
52
+ @type_name_resolver ||= RBS::Resolver::TypeNameResolver.build(env)
53
53
  end
54
54
 
55
55
  def validator
@@ -321,12 +321,12 @@ module Steep
321
321
  args: entry.type_params.map { AST::Types::Any.instance() }
322
322
  )
323
323
 
324
- entry.decls.each do |decl|
325
- ast = decl.decl
326
-
327
- unless AnnotationsHelper.deprecated_annotation?(ast.annotations)
328
- if location = ast.location
329
- validate_type_name_deprecation(name, location[:name])
324
+ entry.each_decl do |decl|
325
+ if decl.is_a?(RBS::AST::Declarations::Base)
326
+ unless AnnotationsHelper.deprecated_annotation?(decl.annotations)
327
+ if location = decl.location
328
+ validate_type_name_deprecation(name, location[:name])
329
+ end
330
330
  end
331
331
  end
332
332
  end
@@ -391,7 +391,7 @@ module Steep
391
391
  location =
392
392
  case ancestor.source
393
393
  when :super
394
- if (primary_decl = entry.primary.decl).is_a?(RBS::AST::Declarations::Class)
394
+ if (primary_decl = entry.primary_decl).is_a?(RBS::AST::Declarations::Class)
395
395
  primary_decl.super_class&.location
396
396
  end
397
397
  when nil
@@ -455,7 +455,13 @@ module Steep
455
455
  definition.class_variables.each do |name, var|
456
456
  if var.declared_in == definition.type_name
457
457
  if (parent = var.parent_variable) && var.declared_in != parent.declared_in
458
- class_var = entry.decls.flat_map {|decl| decl.decl.members }.find do |member|
458
+ members = entry.each_decl.flat_map do |decl|
459
+ case decl
460
+ when RBS::AST::Declarations::Class, RBS::AST::Declarations::Module
461
+ decl.members
462
+ end
463
+ end
464
+ class_var = members.find do |member|
459
465
  member.is_a?(RBS::AST::Members::ClassVariable) && member.name == name
460
466
  end
461
467
 
@@ -534,8 +540,10 @@ module Steep
534
540
  location =
535
541
  case ancestor.source
536
542
  when :super
537
- primary_decl = env.class_decls.fetch(name).primary.decl
538
- primary_decl.is_a?(RBS::AST::Declarations::Class) or raise
543
+ primary_decl = env.class_decls.fetch(name).primary_decl
544
+ unless primary_decl.is_a?(RBS::AST::Declarations::Class) || primary_decl.is_a?(RBS::AST::Ruby::Declarations::ClassDecl)
545
+ raise
546
+ end
539
547
  if super_class = primary_decl.super_class
540
548
  super_class.location
541
549
  else
@@ -644,13 +652,17 @@ module Steep
644
652
  end
645
653
 
646
654
  def validate_one_alias(name, entry = env.type_alias_decls.fetch(name))
647
- *, inner_most_outer_module = entry.outer
648
- if inner_most_outer_module
649
- class_type = AST::Types::Name::Singleton.new(name: inner_most_outer_module.name)
650
- instance_type = AST::Types::Name::Instance.new(
651
- name: inner_most_outer_module.name,
652
- args: inner_most_outer_module.type_params.map { AST::Types::Any.instance() },
653
- )
655
+ inner_most_outer_module_name = entry.context&.last
656
+
657
+ if inner_most_outer_module_name
658
+ inner_most_outer_module = env.module_class_entry(inner_most_outer_module_name, normalized: true)
659
+ if inner_most_outer_module
660
+ class_type = AST::Types::Name::Singleton.new(name: inner_most_outer_module.name)
661
+ instance_type = AST::Types::Name::Instance.new(
662
+ name: inner_most_outer_module.name,
663
+ args: inner_most_outer_module.type_params.map { AST::Types::Any.instance() },
664
+ )
665
+ end
654
666
  end
655
667
 
656
668
  push_context(class_type: class_type, instance_type: instance_type, self_type: nil) do
@@ -21,7 +21,7 @@ module Steep
21
21
  last_start = ignore
22
22
  when AST::Ignore::IgnoreEnd
23
23
  if last_start
24
- ignored_ranges << (last_start.line..ignore.line)
24
+ ignored_ranges << [last_start, ignore]
25
25
  last_start = nil
26
26
  else
27
27
  error_ignores << ignore
@@ -40,18 +40,28 @@ module Steep
40
40
  end
41
41
  end
42
42
 
43
+ def each_ignore(&block)
44
+ if block
45
+ ignored_ranges.each(&block)
46
+ ignored_lines.each_value(&block)
47
+ else
48
+ enum_for(:each_ignore)
49
+ end
50
+ end
51
+
43
52
  def ignore?(start_line, end_line, code)
44
53
  if ignore = ignored_lines.fetch(start_line, nil)
45
- ignore_code?(ignore, code) and return true
54
+ ignore_code?(ignore, code) and return ignore
46
55
  end
47
56
 
48
57
  if start_line != end_line
49
58
  if ignore = ignored_lines.fetch(end_line, nil)
50
- ignore_code?(ignore, code) and return true
59
+ ignore_code?(ignore, code) and return ignore
51
60
  end
52
61
  end
53
62
 
54
- ignored_ranges.any? do |range|
63
+ ignored_ranges.find do |ignore_start, ignore_end|
64
+ range = ignore_start.line..ignore_end.line
55
65
  range.cover?(start_line) && range.cover?(end_line)
56
66
  end
57
67
  end
data/lib/steep/source.rb CHANGED
@@ -19,7 +19,7 @@ module Steep
19
19
  @ignores = ignores
20
20
  end
21
21
 
22
- class Builder < ::Parser::Builders::Default
22
+ class Builder < Prism::Translation::Parser::Builder
23
23
  def string_value(token)
24
24
  value(token)
25
25
  end
@@ -31,7 +31,7 @@ module Steep
31
31
  end
32
32
 
33
33
  def self.new_parser
34
- ::Parser::Ruby33.new(Builder.new).tap do |parser|
34
+ Prism::Translation::Parser34.new(Builder.new).tap do |parser|
35
35
  parser.diagnostics.all_errors_are_fatal = true
36
36
  parser.diagnostics.ignore_warnings = true
37
37
  end
@@ -257,7 +257,7 @@ module Steep
257
257
  annot.line or next
258
258
 
259
259
  case node.type
260
- when :def, :module, :class, :block, :numblock, :ensure, :defs, :resbody
260
+ when :def, :module, :class, :block, :numblock, :itblock, :ensure, :defs, :resbody
261
261
  location = node.loc
262
262
  location.line <= annot.line && annot.line < location.last_line
263
263
  else
@@ -319,6 +319,20 @@ module Steep
319
319
  end
320
320
  end
321
321
 
322
+ def each_block_annotation(node, &block)
323
+ if block
324
+ if annots = mapping.fetch(node, nil)
325
+ annots.each(&block)
326
+ end
327
+ else
328
+ enum_for :each_block_annotation, node
329
+ end
330
+ end
331
+
332
+ def find_block_node(nodes)
333
+ nodes.find { mapping.key?(_1) }
334
+ end
335
+
322
336
  def each_heredoc_node(node = self.node, parents = [], &block)
323
337
  if block
324
338
  return unless node
@@ -551,13 +565,13 @@ module Steep
551
565
  end
552
566
  ]
553
567
  )
554
- when :numblock
555
- send, size, body = node.children
568
+ when :numblock, :itblock
569
+ send, arg, body = node.children
556
570
  node = node.updated(
557
571
  nil,
558
572
  [
559
573
  map_child_node(send) {|child| insert_type_node(child, child_assertions) },
560
- size,
574
+ arg,
561
575
  insert_type_node(body, child_assertions)
562
576
  ]
563
577
  )
@@ -634,7 +648,7 @@ module Steep
634
648
  case node.type
635
649
  when :send, :csend
636
650
  node
637
- when :block, :numblock
651
+ when :block, :numblock, :itblock
638
652
  send = node.children[0]
639
653
  case send.type
640
654
  when :send, :csend
@@ -0,0 +1,39 @@
1
+ module Steep
2
+ # A basic implementation of ActiveSupport::TaggedLogging.
3
+ # Might be able to be replaced by plain logger in the future.
4
+ # https://github.com/ruby/logger/pull/132
5
+ class TaggedLogging < Logger
6
+ def initialize(...)
7
+ super
8
+ self.formatter = proc do |severity, datetime, progname, msg|
9
+ # @type var severity: String
10
+ # @type var datetime: Time
11
+ # @type var progname: untyped
12
+ # @type var msg: untyped
13
+ # @type block: String
14
+ "#{datetime.strftime('%Y-%m-%d %H:%M:%S.%L')}: #{severity}: #{formatted_tags} #{msg}\n"
15
+ end
16
+ @thread_key = "steep_tagged_logging_tags:#{object_id}"
17
+ current_tags << "Steep #{VERSION}"
18
+ end
19
+
20
+ def tagged(tag)
21
+ current_tags << tag
22
+ yield
23
+ ensure
24
+ current_tags.pop
25
+ end
26
+
27
+ def current_tags
28
+ Thread.current[@thread_key] ||= []
29
+ end
30
+
31
+ def push_tags(*tags)
32
+ current_tags.concat(tags)
33
+ end
34
+
35
+ def formatted_tags
36
+ current_tags.map { |tag| "[#{tag}]" }.join(" ")
37
+ end
38
+ end
39
+ end
@@ -39,6 +39,10 @@ module Steep
39
39
 
40
40
  SPECIAL_LVAR_NAMES = Set[:_, :__any__, :__skip__]
41
41
 
42
+ # a synthetic variable name for anonymous block params (can't conflict with
43
+ # user variables since Ruby doesn't allow * in local variable names).
44
+ ANONYMOUS_BLOCK_PASSABLE_LVAR = :"*block"
45
+
42
46
  include ModuleHelper
43
47
 
44
48
  attr_reader :checker
@@ -212,13 +216,21 @@ module Steep
212
216
  TypeInference::MethodParams.empty(node: node)
213
217
  end
214
218
 
219
+ block_param_name = nil #: Symbol?
220
+ method_params.each_param do |param|
221
+ if param.is_a?(TypeInference::MethodParams::BlockParameter)
222
+ block_param_name = param.name || ANONYMOUS_BLOCK_PASSABLE_LVAR
223
+ end
224
+ end
225
+
215
226
  method_context = TypeInference::Context::MethodContext.new(
216
227
  name: method_name,
217
228
  method: definition && definition.methods[method_name],
218
229
  method_type: method_type,
219
230
  return_type: annots.return_type || method_type&.type&.return_type || AST::Builtin.any_type,
220
231
  super_method: super_method,
221
- forward_arg_type: method_params.forward_arg_type
232
+ forward_arg_type: method_params.forward_arg_type,
233
+ block_param_name: block_param_name
222
234
  )
223
235
 
224
236
  local_variable_types = method_params.each_param.with_object({}) do |param, hash| #$ Hash[Symbol, AST::Types::t]
@@ -226,6 +238,8 @@ module Steep
226
238
  unless SPECIAL_LVAR_NAMES.include?(param.name)
227
239
  hash[param.name] = param.var_type
228
240
  end
241
+ elsif param.is_a?(TypeInference::MethodParams::BlockParameter)
242
+ hash[ANONYMOUS_BLOCK_PASSABLE_LVAR] = param.var_type
229
243
  end
230
244
  end
231
245
  type_env = context.type_env.assign_local_variables(local_variable_types)
@@ -368,7 +382,7 @@ module Steep
368
382
  end
369
383
 
370
384
  if implement_module_name
371
- module_entry = checker.factory.definition_builder.env.normalized_module_entry(implement_module_name.name)
385
+ module_entry = checker.factory.definition_builder.env.module_entry(implement_module_name.name, normalized: true)
372
386
  if module_entry
373
387
  module_context = module_context.update(
374
388
  instance_type: AST::Types::Intersection.build(
@@ -397,7 +411,7 @@ module Steep
397
411
  ].compact
398
412
  )
399
413
  )
400
- elsif checker.factory.definition_builder.env.normalized_class_entry(implement_module_name.name)
414
+ elsif checker.factory.definition_builder.env.class_entry(implement_module_name.name, normalized:true)
401
415
  typing.add_error(
402
416
  Diagnostic::Ruby::ClassModuleMismatch.new(node: node, name: new_module_name)
403
417
  )
@@ -489,8 +503,8 @@ module Steep
489
503
  module_context = module_context.update(instance_definition: nil, module_definition: nil)
490
504
  end
491
505
 
492
- if !checker.factory.definition_builder.env.normalized_class_entry(implement_module_name.name) &&
493
- checker.factory.definition_builder.env.normalized_module_entry(implement_module_name.name)
506
+ if !checker.factory.definition_builder.env.class_entry(implement_module_name.name, normalized: true) &&
507
+ checker.factory.definition_builder.env.module_entry(implement_module_name.name, normalized: true)
494
508
  typing.add_error(
495
509
  Diagnostic::Ruby::ClassModuleMismatch.new(node: node, name: new_class_name)
496
510
  )
@@ -1355,7 +1369,7 @@ module Steep
1355
1369
  yield_self do
1356
1370
  var = node.children[0]
1357
1371
 
1358
- if SPECIAL_LVAR_NAMES.include?(var)
1372
+ if !var || SPECIAL_LVAR_NAMES.include?(var)
1359
1373
  return add_typing(node, type: AST::Builtin.any_type)
1360
1374
  end
1361
1375
 
@@ -1376,7 +1390,7 @@ module Steep
1376
1390
  yield_self do
1377
1391
  var = node.children[0]
1378
1392
 
1379
- if SPECIAL_LVAR_NAMES.include?(var)
1393
+ if !var || SPECIAL_LVAR_NAMES.include?(var)
1380
1394
  return add_typing(node, type: AST::Builtin.any_type)
1381
1395
  end
1382
1396
 
@@ -1442,7 +1456,7 @@ module Steep
1442
1456
 
1443
1457
  case
1444
1458
  when hint && check_relation(sub_type: ty, super_type: hint).success? && !hint.is_a?(AST::Types::Any) && !hint.is_a?(AST::Types::Top)
1445
- add_typing(node, type: hint)
1459
+ add_typing(node, type: unwrap(hint))
1446
1460
  when condition
1447
1461
  add_typing(node, type: ty)
1448
1462
  else
@@ -1670,7 +1684,7 @@ module Steep
1670
1684
  end
1671
1685
 
1672
1686
  when :yield
1673
- if method_context && method_context.method_type
1687
+ if method_context && (method_type = method_context.method_type)
1674
1688
  if block_type = method_context.block_type
1675
1689
  if block_type.type.params
1676
1690
  type = AST::Types::Proc.new(
@@ -1701,9 +1715,12 @@ module Steep
1701
1715
  end
1702
1716
 
1703
1717
  add_typing(node, type: block_type.type.return_type)
1704
- else
1718
+ elsif method_type.type.params
1705
1719
  typing.add_error(Diagnostic::Ruby::UnexpectedYield.new(node: node))
1706
1720
  fallback_to_any node
1721
+ else
1722
+ constr = type_check_untyped_args(node.children)
1723
+ add_typing(node, type: AST::Builtin.any_type)
1707
1724
  end
1708
1725
  else
1709
1726
  fallback_to_any node
@@ -1733,7 +1750,7 @@ module Steep
1733
1750
  if hint
1734
1751
  array = AST::Builtin::Array.instance_type(AST::Builtin.any_type)
1735
1752
  if check_relation(sub_type: array, super_type: hint).success?
1736
- add_typing node, type: hint
1753
+ add_typing node, type: unwrap(hint)
1737
1754
  else
1738
1755
  add_typing node, type: array
1739
1756
  end
@@ -1745,13 +1762,18 @@ module Steep
1745
1762
  if hint
1746
1763
  tuples = select_flatten_types(hint) {|type| type.is_a?(AST::Types::Tuple) } #: Array[AST::Types::Tuple]
1747
1764
  unless tuples.empty?
1765
+ fallback_pair = nil #: Pair?
1748
1766
  tuples.each do |tuple|
1749
1767
  typing.new_child() do |child_typing|
1750
1768
  if pair = with_new_typing(child_typing).try_tuple_type(node, tuple)
1751
- return pair.with(constr: pair.constr.save_typing)
1769
+ if pair.constr.check_relation(sub_type: pair.type, super_type: tuple).success?
1770
+ return pair.with(constr: pair.constr.save_typing)
1771
+ end
1772
+ fallback_pair ||= pair.with(constr: pair.constr.save_typing)
1752
1773
  end
1753
1774
  end
1754
1775
  end
1776
+ return fallback_pair if fallback_pair
1755
1777
  end
1756
1778
  end
1757
1779
 
@@ -2192,7 +2214,10 @@ module Steep
2192
2214
  yield_self do
2193
2215
  body, ensure_body = node.children
2194
2216
  body_type = synthesize(body).type if body
2195
- synthesize(ensure_body) if ensure_body
2217
+ if ensure_body
2218
+ ensure_constr = for_branch(node)
2219
+ ensure_constr.synthesize(ensure_body)
2220
+ end
2196
2221
  if body_type
2197
2222
  add_typing(node, type: body_type)
2198
2223
  else
@@ -2505,7 +2530,9 @@ module Steep
2505
2530
  end
2506
2531
  else
2507
2532
  # Anonymous block_pass only happens inside method definition
2508
- if block_type = method_context!.block_type
2533
+ if (type = context.type_env[ANONYMOUS_BLOCK_PASSABLE_LVAR])
2534
+ # Use type from type_env (may have been narrowed by block_given?)
2535
+ elsif block_type = method_context!.block_type
2509
2536
  type = AST::Types::Proc.new(
2510
2537
  type: block_type.type,
2511
2538
  block: nil,
@@ -2669,7 +2696,7 @@ module Steep
2669
2696
  constr.add_typing(node, type: type)
2670
2697
  end
2671
2698
 
2672
- when :block, :numblock, :send, :csend
2699
+ when :block, :numblock, :itblock, :send, :csend
2673
2700
  synthesize_sendish(node, hint: hint, tapp: nil)
2674
2701
 
2675
2702
  when :forwarded_args, :forward_arg
@@ -2687,7 +2714,6 @@ module Steep
2687
2714
  end
2688
2715
  end
2689
2716
  rescue RBS::BaseError => exn
2690
- Steep.logger.warn("hello")
2691
2717
  Steep.logger.warn { "Unexpected RBS error: #{exn.message}" }
2692
2718
  exn.backtrace&.each {|loc| Steep.logger.warn " #{loc}" }
2693
2719
  typing.add_error(Diagnostic::Ruby::UnexpectedError.new(node: node, error: exn))
@@ -2746,6 +2772,20 @@ module Steep
2746
2772
 
2747
2773
  params = Parser::AST::Node.new(:args, arg_nodes)
2748
2774
 
2775
+ if send_node.type == :lambda
2776
+ # @type var node: Parser::AST::Node & Parser::AST::_BlockNode
2777
+ type_lambda(node, params_node: params, body_node: body, type_hint: hint)
2778
+ else
2779
+ type_send(node, send_node: send_node, block_params: params, block_body: body, unwrap: send_node.type == :csend, tapp: tapp, hint: hint)
2780
+ end
2781
+ end
2782
+ when :itblock
2783
+ yield_self do
2784
+ send_node, _name, body = node.children
2785
+
2786
+ arg_nodes = [Parser::AST::Node.new(:procarg0, [:it])]
2787
+ params = Parser::AST::Node.new(:args, arg_nodes)
2788
+
2749
2789
  if send_node.type == :lambda
2750
2790
  # @type var node: Parser::AST::Node & Parser::AST::_BlockNode
2751
2791
  type_lambda(node, params_node: params, body_node: body, type_hint: hint)
@@ -3225,6 +3265,8 @@ module Steep
3225
3265
  MethodName("::Hash#[]")
3226
3266
  ]
3227
3267
 
3268
+ KERNEL_BLOCK_GIVEN = MethodName("::Kernel#block_given?")
3269
+
3228
3270
  def pure_send?(call, receiver, arguments)
3229
3271
  return false unless call.node.type == :send || call.node.type == :csend
3230
3272
  return false unless call.pure? || KNOWN_PURE_METHODS.superset?(Set.new(call.method_decls.map(&:method_name)))
@@ -3318,7 +3360,7 @@ module Steep
3318
3360
  end
3319
3361
  end
3320
3362
 
3321
- if node.type == :csend || ((node.type == :block || node.type == :numblock) && node.children[0].type == :csend)
3363
+ if node.type == :csend || ((node.type == :block || node.type == :numblock || node.type == :itblock) && node.children[0].type == :csend)
3322
3364
  optional_type = AST::Types::Union.build(types: [call.return_type, AST::Builtin.nil_type])
3323
3365
  call = call.with_return_type(optional_type)
3324
3366
  end
@@ -3364,6 +3406,34 @@ module Steep
3364
3406
  )
3365
3407
  end
3366
3408
 
3409
+ # Handle block_given? type narrowing
3410
+ if method_name == :block_given? && call.is_a?(TypeInference::MethodCall::Typed)
3411
+ if call.method_decls.all? { _1.method_name == KERNEL_BLOCK_GIVEN }
3412
+ if (block_var = constr.method_context&.block_param_name)
3413
+ if (var_type = constr.context.type_env[block_var])
3414
+ unwrapped = constr.checker.factory.unwrap_optional(var_type)
3415
+
3416
+ if unwrapped
3417
+ truthy_env = constr.context.type_env.refine_types(
3418
+ local_variable_types: { block_var => unwrapped }
3419
+ )
3420
+ falsy_env = constr.context.type_env.refine_types(
3421
+ local_variable_types: { block_var => AST::Builtin.nil_type }
3422
+ )
3423
+
3424
+ env_type = AST::Types::Logic::Env.new(
3425
+ truthy: truthy_env,
3426
+ falsy: falsy_env,
3427
+ type: call.return_type
3428
+ )
3429
+
3430
+ call = call.with_return_type(env_type)
3431
+ end
3432
+ end
3433
+ end
3434
+ end
3435
+ end
3436
+
3367
3437
  constr.add_call(call)
3368
3438
  else
3369
3439
  skips = [] #: Array[Parser::AST::Node?]
@@ -3453,7 +3523,7 @@ module Steep
3453
3523
 
3454
3524
  when AST::Types::Any
3455
3525
  case node.type
3456
- when :block, :numblock
3526
+ when :block, :numblock, :itblock
3457
3527
  # @type var node: Parser::AST::Node & Parser::AST::_BlockNode
3458
3528
  block_annotations = source.annotations(block: node, factory: checker.factory, context: nesting)
3459
3529
  block_params or raise
@@ -4710,8 +4780,15 @@ module Steep
4710
4780
 
4711
4781
  def validate_method_definitions(node, module_name)
4712
4782
  module_name_1 = module_name.name
4713
- module_entry = checker.factory.env.normalized_module_class_entry(module_name_1) or raise
4714
- member_decl_count = module_entry.decls.count {|d| d.decl.each_member.count > 0 }
4783
+ module_entry = checker.factory.env.module_class_entry(module_name_1, normalized: true) or raise
4784
+ member_decl_count = module_entry.each_decl.count do |decl|
4785
+ case decl
4786
+ when RBS::AST::Declarations::Base
4787
+ decl.each_member.count > 0
4788
+ else
4789
+ false
4790
+ end
4791
+ end
4715
4792
 
4716
4793
  return unless member_decl_count == 1
4717
4794
 
@@ -4878,7 +4955,7 @@ module Steep
4878
4955
  else
4879
4956
  literal_type = AST::Types::Literal.new(value: literal)
4880
4957
  if check_relation(sub_type: literal_type, super_type: hint).success?
4881
- hint
4958
+ unwrap(hint)
4882
4959
  end
4883
4960
  end
4884
4961
  end
@@ -4887,7 +4964,7 @@ module Steep
4887
4964
  def to_instance_type(type, args: nil)
4888
4965
  args = args || case type
4889
4966
  when AST::Types::Name::Singleton
4890
- decl = checker.factory.env.normalized_module_class_entry(type.name) or raise
4967
+ decl = checker.factory.env.module_class_entry(type.name, normalized: true) or raise
4891
4968
  decl.type_params.each.map { AST::Builtin.any_type }
4892
4969
  else
4893
4970
  raise "unexpected type to to_instance_type: #{type}"
@@ -5295,9 +5372,19 @@ module Steep
5295
5372
  annotations =
5296
5373
  case entry
5297
5374
  when RBS::Environment::ModuleEntry, RBS::Environment::ClassEntry
5298
- entry.decls.flat_map { _1.decl.annotations }
5375
+ entry.each_decl.flat_map do |decl|
5376
+ if decl.is_a?(RBS::AST::Declarations::Base)
5377
+ decl.annotations
5378
+ else
5379
+ []
5380
+ end
5381
+ end
5299
5382
  when RBS::Environment::ConstantEntry, RBS::Environment::ClassAliasEntry, RBS::Environment::ModuleAliasEntry
5300
- entry.decl.annotations
5383
+ if entry.decl.is_a?(RBS::AST::Declarations::Base)
5384
+ entry.decl.annotations
5385
+ else
5386
+ [] #: Array[RBS::AST::Annotation]
5387
+ end
5301
5388
  end
5302
5389
 
5303
5390
  if annotations
@@ -101,13 +101,13 @@ module Steep
101
101
  end
102
102
 
103
103
  def params
104
- [].tap do |params|
105
- params.push(*leading_params)
106
- params.push(*optional_params)
107
- params.push rest_param if rest_param
108
- params.push(*trailing_params)
109
- params.push(block_param) if block_param
110
- end
104
+ params = [] #: Array[Param | MultipleParam]
105
+ params.push(*leading_params)
106
+ params.push(*optional_params)
107
+ params.push rest_param if rest_param
108
+ params.push(*trailing_params)
109
+ params.push(block_param) if block_param
110
+ params
111
111
  end
112
112
 
113
113
  def self.from_node(node, annotations:)
@@ -8,14 +8,16 @@ module Steep
8
8
  attr_reader :return_type
9
9
  attr_reader :super_method
10
10
  attr_reader :forward_arg_type
11
+ attr_reader :block_param_name
11
12
 
12
- def initialize(name:, method:, method_type:, return_type:, super_method:, forward_arg_type:)
13
+ def initialize(name:, method:, method_type:, return_type:, super_method:, forward_arg_type:, block_param_name: nil)
13
14
  @name = name
14
15
  @method = method
15
16
  @return_type = return_type
16
17
  @method_type = method_type
17
18
  @super_method = super_method
18
19
  @forward_arg_type = forward_arg_type
20
+ @block_param_name = block_param_name
19
21
  end
20
22
 
21
23
  def block_type
@@ -174,7 +176,7 @@ module Steep
174
176
  block_context: block_context,
175
177
  break_context: break_context,
176
178
  module_context: module_context,
177
- self_type: self_type,
179
+ self_type: self_type, # steep:ignore ArgumentTypeMismatch
178
180
  type_env: type_env,
179
181
  call_context: call_context,
180
182
  variable_context: variable_context
@@ -344,6 +344,22 @@ module Steep
344
344
  falsy_result = Result.new(type: FALSE, env: falsy_env, unreachable: false)
345
345
  falsy_result.unreachable! unless falsy_type
346
346
 
347
+ [truthy_result, falsy_result]
348
+ elsif (truthy_types, falsy_types = literal_var_type_case_select(arg, receiver_type, for_receiver: true))
349
+ # Handle literal equality: receiver == literal_value
350
+ truthy_env, falsy_env = refine_node_type(
351
+ env: env,
352
+ node: receiver,
353
+ truthy_type: truthy_types.empty? ? BOT : AST::Types::Union.build(types: truthy_types),
354
+ falsy_type: falsy_types.empty? ? BOT : AST::Types::Union.build(types: falsy_types)
355
+ )
356
+
357
+ truthy_result = Result.new(type: TRUE, env: truthy_env, unreachable: false)
358
+ truthy_result.unreachable! if truthy_types.empty?
359
+
360
+ falsy_result = Result.new(type: FALSE, env: falsy_env, unreachable: false)
361
+ falsy_result.unreachable! if falsy_types.empty?
362
+
347
363
  [truthy_result, falsy_result]
348
364
  end
349
365
  end
@@ -493,7 +509,7 @@ module Steep
493
509
  end
494
510
  end
495
511
 
496
- def literal_var_type_case_select(value_node, arg_type)
512
+ def literal_var_type_case_select(value_node, arg_type, for_receiver: false)
497
513
  case arg_type
498
514
  when AST::Types::Union
499
515
  # @type var truthy_types: Array[AST::Types::t]
@@ -502,7 +518,7 @@ module Steep
502
518
  falsy_types = []
503
519
 
504
520
  arg_type.types.each do |type|
505
- if (ts, fs = literal_var_type_case_select(value_node, type))
521
+ if (ts, fs = literal_var_type_case_select(value_node, type, for_receiver: for_receiver))
506
522
  truthy_types.push(*ts)
507
523
  falsy_types.push(*fs)
508
524
  else
@@ -548,7 +564,12 @@ module Steep
548
564
  false_types << type
549
565
  end
550
566
  else
551
- true_types << AST::Types::Literal.new(value: value_node.children[0])
567
+ # For `==`, narrow only when the literal can be a value of `type`.
568
+ # e.g. `Symbol == :fatal` can be true, but `SomeClass == :fatal` cannot.
569
+ literal_type = AST::Types::Literal.new(value: value_node.children[0])
570
+ if !for_receiver || subtyping?(sub_type: literal_type, super_type: type)
571
+ true_types << literal_type
572
+ end
552
573
  false_types << type
553
574
  end
554
575
  end