steep 1.10.0 → 2.0.0

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +84 -1
  3. data/CLAUDE.md +114 -0
  4. data/README.md +1 -1
  5. data/Rakefile +15 -3
  6. data/Steepfile +13 -13
  7. data/lib/steep/annotation_parser.rb +5 -1
  8. data/lib/steep/annotations_helper.rb +12 -2
  9. data/lib/steep/ast/node/type_application.rb +22 -16
  10. data/lib/steep/ast/node/type_assertion.rb +7 -4
  11. data/lib/steep/ast/types/factory.rb +3 -2
  12. data/lib/steep/cli.rb +246 -2
  13. data/lib/steep/daemon/configuration.rb +19 -0
  14. data/lib/steep/daemon/server.rb +476 -0
  15. data/lib/steep/daemon.rb +201 -0
  16. data/lib/steep/diagnostic/ruby.rb +50 -8
  17. data/lib/steep/diagnostic/signature.rb +31 -8
  18. data/lib/steep/drivers/check.rb +301 -140
  19. data/lib/steep/drivers/print_project.rb +9 -10
  20. data/lib/steep/drivers/query.rb +102 -0
  21. data/lib/steep/drivers/start_server.rb +19 -0
  22. data/lib/steep/drivers/stop_server.rb +20 -0
  23. data/lib/steep/drivers/watch.rb +2 -2
  24. data/lib/steep/index/rbs_index.rb +38 -13
  25. data/lib/steep/index/signature_symbol_provider.rb +24 -3
  26. data/lib/steep/interface/builder.rb +48 -15
  27. data/lib/steep/interface/shape.rb +13 -5
  28. data/lib/steep/locator.rb +377 -0
  29. data/lib/steep/project/dsl.rb +26 -5
  30. data/lib/steep/project/group.rb +8 -2
  31. data/lib/steep/project/target.rb +16 -2
  32. data/lib/steep/project.rb +21 -2
  33. data/lib/steep/server/base_worker.rb +2 -2
  34. data/lib/steep/server/change_buffer.rb +2 -1
  35. data/lib/steep/server/custom_methods.rb +12 -0
  36. data/lib/steep/server/inline_source_change_detector.rb +94 -0
  37. data/lib/steep/server/interaction_worker.rb +51 -74
  38. data/lib/steep/server/lsp_formatter.rb +48 -12
  39. data/lib/steep/server/master.rb +100 -18
  40. data/lib/steep/server/target_group_files.rb +124 -151
  41. data/lib/steep/server/type_check_controller.rb +276 -123
  42. data/lib/steep/server/type_check_worker.rb +104 -3
  43. data/lib/steep/services/completion_provider/rbs.rb +74 -0
  44. data/lib/steep/services/completion_provider/ruby.rb +652 -0
  45. data/lib/steep/services/completion_provider/type_name.rb +243 -0
  46. data/lib/steep/services/completion_provider.rb +39 -662
  47. data/lib/steep/services/content_change.rb +14 -1
  48. data/lib/steep/services/file_loader.rb +4 -2
  49. data/lib/steep/services/goto_service.rb +271 -68
  50. data/lib/steep/services/hover_provider/content.rb +67 -0
  51. data/lib/steep/services/hover_provider/rbs.rb +8 -9
  52. data/lib/steep/services/hover_provider/ruby.rb +123 -64
  53. data/lib/steep/services/hover_provider/singleton_methods.rb +4 -0
  54. data/lib/steep/services/signature_service.rb +129 -54
  55. data/lib/steep/services/type_check_service.rb +72 -27
  56. data/lib/steep/signature/validator.rb +30 -18
  57. data/lib/steep/source/ignore_ranges.rb +14 -4
  58. data/lib/steep/source.rb +16 -2
  59. data/lib/steep/tagged_logging.rb +39 -0
  60. data/lib/steep/type_construction.rb +94 -21
  61. data/lib/steep/type_inference/block_params.rb +7 -7
  62. data/lib/steep/type_inference/context.rb +4 -2
  63. data/lib/steep/type_inference/logic_type_interpreter.rb +21 -3
  64. data/lib/steep/type_inference/method_call.rb +4 -0
  65. data/lib/steep/type_inference/type_env.rb +1 -1
  66. data/lib/steep/typing.rb +0 -2
  67. data/lib/steep/version.rb +1 -1
  68. data/lib/steep.rb +42 -32
  69. data/manual/ruby-diagnostics.md +67 -0
  70. data/sample/Steepfile +1 -0
  71. data/sample/lib/conference.rb +1 -0
  72. data/sample/lib/deprecated.rb +6 -0
  73. data/sample/lib/inline.rb +43 -0
  74. data/sample/sig/generics.rbs +3 -0
  75. data/steep.gemspec +4 -5
  76. metadata +26 -26
  77. 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::Parser33.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
@@ -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
@@ -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,
@@ -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))
@@ -3225,6 +3251,8 @@ module Steep
3225
3251
  MethodName("::Hash#[]")
3226
3252
  ]
3227
3253
 
3254
+ KERNEL_BLOCK_GIVEN = MethodName("::Kernel#block_given?")
3255
+
3228
3256
  def pure_send?(call, receiver, arguments)
3229
3257
  return false unless call.node.type == :send || call.node.type == :csend
3230
3258
  return false unless call.pure? || KNOWN_PURE_METHODS.superset?(Set.new(call.method_decls.map(&:method_name)))
@@ -3364,6 +3392,34 @@ module Steep
3364
3392
  )
3365
3393
  end
3366
3394
 
3395
+ # Handle block_given? type narrowing
3396
+ if method_name == :block_given? && call.is_a?(TypeInference::MethodCall::Typed)
3397
+ if call.method_decls.all? { _1.method_name == KERNEL_BLOCK_GIVEN }
3398
+ if (block_var = constr.method_context&.block_param_name)
3399
+ if (var_type = constr.context.type_env[block_var])
3400
+ unwrapped = constr.checker.factory.unwrap_optional(var_type)
3401
+
3402
+ if unwrapped
3403
+ truthy_env = constr.context.type_env.refine_types(
3404
+ local_variable_types: { block_var => unwrapped }
3405
+ )
3406
+ falsy_env = constr.context.type_env.refine_types(
3407
+ local_variable_types: { block_var => AST::Builtin.nil_type }
3408
+ )
3409
+
3410
+ env_type = AST::Types::Logic::Env.new(
3411
+ truthy: truthy_env,
3412
+ falsy: falsy_env,
3413
+ type: call.return_type
3414
+ )
3415
+
3416
+ call = call.with_return_type(env_type)
3417
+ end
3418
+ end
3419
+ end
3420
+ end
3421
+ end
3422
+
3367
3423
  constr.add_call(call)
3368
3424
  else
3369
3425
  skips = [] #: Array[Parser::AST::Node?]
@@ -4710,8 +4766,15 @@ module Steep
4710
4766
 
4711
4767
  def validate_method_definitions(node, module_name)
4712
4768
  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 }
4769
+ module_entry = checker.factory.env.module_class_entry(module_name_1, normalized: true) or raise
4770
+ member_decl_count = module_entry.each_decl.count do |decl|
4771
+ case decl
4772
+ when RBS::AST::Declarations::Base
4773
+ decl.each_member.count > 0
4774
+ else
4775
+ false
4776
+ end
4777
+ end
4715
4778
 
4716
4779
  return unless member_decl_count == 1
4717
4780
 
@@ -4878,7 +4941,7 @@ module Steep
4878
4941
  else
4879
4942
  literal_type = AST::Types::Literal.new(value: literal)
4880
4943
  if check_relation(sub_type: literal_type, super_type: hint).success?
4881
- hint
4944
+ unwrap(hint)
4882
4945
  end
4883
4946
  end
4884
4947
  end
@@ -4887,7 +4950,7 @@ module Steep
4887
4950
  def to_instance_type(type, args: nil)
4888
4951
  args = args || case type
4889
4952
  when AST::Types::Name::Singleton
4890
- decl = checker.factory.env.normalized_module_class_entry(type.name) or raise
4953
+ decl = checker.factory.env.module_class_entry(type.name, normalized: true) or raise
4891
4954
  decl.type_params.each.map { AST::Builtin.any_type }
4892
4955
  else
4893
4956
  raise "unexpected type to to_instance_type: #{type}"
@@ -5295,9 +5358,19 @@ module Steep
5295
5358
  annotations =
5296
5359
  case entry
5297
5360
  when RBS::Environment::ModuleEntry, RBS::Environment::ClassEntry
5298
- entry.decls.flat_map { _1.decl.annotations }
5361
+ entry.each_decl.flat_map do |decl|
5362
+ if decl.is_a?(RBS::AST::Declarations::Base)
5363
+ decl.annotations
5364
+ else
5365
+ []
5366
+ end
5367
+ end
5299
5368
  when RBS::Environment::ConstantEntry, RBS::Environment::ClassAliasEntry, RBS::Environment::ModuleAliasEntry
5300
- entry.decl.annotations
5369
+ if entry.decl.is_a?(RBS::AST::Declarations::Base)
5370
+ entry.decl.annotations
5371
+ else
5372
+ [] #: Array[RBS::AST::Annotation]
5373
+ end
5301
5374
  end
5302
5375
 
5303
5376
  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,9 @@ 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 === (for_receiver: false), non-literal types can match the literal in truthy branch
568
+ # For == (for_receiver: true), non-literal types only go to falsy branch
569
+ true_types << AST::Types::Literal.new(value: value_node.children[0]) unless for_receiver
552
570
  false_types << type
553
571
  end
554
572
  end
@@ -42,6 +42,8 @@ module Steep
42
42
  end
43
43
 
44
44
  TopLevelContext = _ = Class.new() do
45
+ # @implements TopLevelContext
46
+
45
47
  def to_s
46
48
  "@<main>"
47
49
  end
@@ -58,6 +60,8 @@ module Steep
58
60
  end
59
61
 
60
62
  UnknownContext = _ = Class.new() do
63
+ # @implements UnknownContext
64
+
61
65
  def to_s
62
66
  "@<unknown>"
63
67
  end
@@ -150,7 +150,7 @@ module Steep
150
150
  pure_call_updates = pure_node_invalidation(invalidated_nodes)
151
151
 
152
152
  pure_call_types.each do |node, type|
153
- call, _ = pure_call_updates[node]
153
+ call, _ = pure_call_updates.fetch(node)
154
154
  pure_call_updates[node] = [call, type]
155
155
  end
156
156
 
data/lib/steep/typing.rb CHANGED
@@ -208,8 +208,6 @@ module Steep
208
208
  end
209
209
 
210
210
  def type_of(node:)
211
- raise "`nil` given to `Typing#type_of(node:)`" unless node
212
-
213
211
  type = typing[node]
214
212
 
215
213
  if type
data/lib/steep/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Steep
2
- VERSION = "1.10.0"
2
+ VERSION = "2.0.0"
3
3
  end