rigortype 0.4.0 → 0.4.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 (102) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -3
  3. data/docs/handbook/08-understanding-errors.md +3 -2
  4. data/docs/install.md +87 -15
  5. data/docs/manual/02-cli-reference.md +2 -2
  6. data/docs/manual/03-configuration.md +5 -0
  7. data/docs/manual/08-skills.md +4 -3
  8. data/docs/manual/12-caching.md +43 -4
  9. data/docs/manual/14-rails-quickstart.md +19 -11
  10. data/lib/rigor/analysis/check_rules/source_arity.rb +163 -59
  11. data/lib/rigor/analysis/check_rules.rb +118 -87
  12. data/lib/rigor/analysis/dependency_recorder.rb +200 -18
  13. data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +266 -0
  14. data/lib/rigor/analysis/incremental_run_slot.rb +171 -0
  15. data/lib/rigor/analysis/incremental_session.rb +273 -49
  16. data/lib/rigor/analysis/plugin_fact_fingerprint.rb +24 -13
  17. data/lib/rigor/analysis/reachability/scan_cache.rb +2 -1
  18. data/lib/rigor/analysis/run_cache_key.rb +27 -8
  19. data/lib/rigor/analysis/run_stats.rb +2 -2
  20. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +1 -1
  21. data/lib/rigor/analysis/runner/pool_coordinator.rb +2 -2
  22. data/lib/rigor/analysis/runner/project_pre_passes.rb +21 -13
  23. data/lib/rigor/analysis/runner.rb +337 -46
  24. data/lib/rigor/analysis/shadow_harness.rb +27 -0
  25. data/lib/rigor/analysis/source_rbs_gate.rb +157 -0
  26. data/lib/rigor/analysis/template_unit_paths.rb +6 -4
  27. data/lib/rigor/analysis/template_units.rb +15 -5
  28. data/lib/rigor/analysis/worker_session.rb +4 -3
  29. data/lib/rigor/cache/descriptor.rb +40 -1
  30. data/lib/rigor/cache/file_digest.rb +28 -0
  31. data/lib/rigor/cache/incremental_snapshot.rb +70 -8
  32. data/lib/rigor/cache/store.rb +63 -3
  33. data/lib/rigor/cache/value_digest.rb +325 -0
  34. data/lib/rigor/cli/check_command.rb +83 -13
  35. data/lib/rigor/cli/doctor_command.rb +1 -1
  36. data/lib/rigor/cli/effects_report.rb +2 -2
  37. data/lib/rigor/cli/unused_command.rb +3 -3
  38. data/lib/rigor/cli.rb +1 -1
  39. data/lib/rigor/config_audit.rb +3 -2
  40. data/lib/rigor/config_path.rb +29 -0
  41. data/lib/rigor/configuration/severity_profile.rb +5 -0
  42. data/lib/rigor/configuration.rb +6 -5
  43. data/lib/rigor/effects/plugin_facts.rb +6 -6
  44. data/lib/rigor/environment/bundle_sig_discovery.rb +5 -3
  45. data/lib/rigor/environment/lockfile_resolver.rb +10 -1
  46. data/lib/rigor/environment/rbs_collection_discovery.rb +10 -1
  47. data/lib/rigor/environment/rbs_loader.rb +1 -1
  48. data/lib/rigor/environment/source_rbs_synthesis.rb +147 -0
  49. data/lib/rigor/environment.rb +13 -70
  50. data/lib/rigor/inference/block_parameter_binder.rb +29 -13
  51. data/lib/rigor/inference/captured_locals.rb +7 -1
  52. data/lib/rigor/inference/declaration_walk/context.rb +195 -0
  53. data/lib/rigor/inference/declaration_walk/errors.rb +18 -0
  54. data/lib/rigor/inference/declaration_walk/shadow.rb +147 -0
  55. data/lib/rigor/inference/declaration_walk/traversal.rb +441 -0
  56. data/lib/rigor/inference/declaration_walk.rb +7 -0
  57. data/lib/rigor/inference/def_node_resolver.rb +3 -2
  58. data/lib/rigor/inference/definer_resolution.rb +266 -0
  59. data/lib/rigor/inference/expression_typer.rb +136 -45
  60. data/lib/rigor/inference/external_ancestor_resolution.rb +13 -16
  61. data/lib/rigor/inference/guard_rebinding.rb +53 -12
  62. data/lib/rigor/inference/macro_block_self_type.rb +14 -2
  63. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +46 -26
  64. data/lib/rigor/inference/module_function_state.rb +213 -0
  65. data/lib/rigor/inference/multi_target_binder.rb +21 -16
  66. data/lib/rigor/inference/narrowing.rb +147 -17
  67. data/lib/rigor/inference/parameter_inference_collector.rb +10 -9
  68. data/lib/rigor/inference/scope_indexer/certainty.rb +176 -0
  69. data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +35 -0
  70. data/lib/rigor/inference/scope_indexer/def_nestings_collector.rb +47 -0
  71. data/lib/rigor/inference/scope_indexer/layered_def_nestings.rb +36 -0
  72. data/lib/rigor/inference/scope_indexer/member_layouts_collector.rb +61 -0
  73. data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +130 -0
  74. data/lib/rigor/inference/scope_indexer/superclasses_collector.rb +64 -0
  75. data/lib/rigor/inference/scope_indexer.rb +1443 -295
  76. data/lib/rigor/inference/singleton_hook_decline.rb +253 -0
  77. data/lib/rigor/inference/statement_evaluator.rb +80 -14
  78. data/lib/rigor/plugin/base.rb +28 -8
  79. data/lib/rigor/plugin/io_boundary.rb +82 -4
  80. data/lib/rigor/plugin/isolation.rb +1 -1
  81. data/lib/rigor/plugin/trust_policy.rb +3 -3
  82. data/lib/rigor/protection/discovery_seed.rb +18 -9
  83. data/lib/rigor/protection/mutation_cache.rb +3 -3
  84. data/lib/rigor/reflection/constant_ancestors.rb +96 -47
  85. data/lib/rigor/reflection/constant_path.rb +22 -7
  86. data/lib/rigor/reflection.rb +2 -3
  87. data/lib/rigor/scope/discovery_index.rb +218 -2
  88. data/lib/rigor/scope/resolution_chain/relevance.rb +165 -0
  89. data/lib/rigor/scope/resolution_chain.rb +884 -0
  90. data/lib/rigor/scope.rb +295 -322
  91. data/lib/rigor/sig_gen/alias_index.rb +1 -1
  92. data/lib/rigor/sig_gen/effect_annotation.rb +2 -2
  93. data/lib/rigor/sig_gen/generator.rb +22 -16
  94. data/lib/rigor/type/combinator.rb +25 -1
  95. data/lib/rigor/version.rb +1 -1
  96. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +10 -0
  97. data/sig/rigor/environment.rbs +2 -0
  98. data/sig/rigor/plugin/io_boundary.rbs +1 -0
  99. data/sig/rigor/scope.rbs +43 -1
  100. data/skills/rigor-project-init/SKILL.md +37 -11
  101. data/skills/rigor-project-init/references/02-configure.md +7 -2
  102. metadata +25 -1
@@ -142,7 +142,7 @@ module Rigor
142
142
  buffer_name = decl.location&.buffer&.name
143
143
  return nil if buffer_name.nil?
144
144
 
145
- File.expand_path(buffer_name.to_s)
145
+ File.absolute_path(buffer_name.to_s)
146
146
  rescue StandardError
147
147
  nil
148
148
  end
@@ -182,8 +182,8 @@ module Rigor
182
182
  def relative_path(path)
183
183
  return nil if path.nil?
184
184
 
185
- expanded = File.expand_path(path.to_s)
186
- root = "#{File.expand_path(Dir.pwd)}#{File::SEPARATOR}"
185
+ expanded = File.absolute_path(path.to_s)
186
+ root = "#{File.absolute_path(Dir.pwd)}#{File::SEPARATOR}"
187
187
  expanded.start_with?(root) ? expanded.delete_prefix(root) : nil
188
188
  end
189
189
 
@@ -13,6 +13,7 @@ require_relative "../source/literals"
13
13
  require_relative "../source/node_children"
14
14
  require_relative "../inference/def_return_typer"
15
15
  require_relative "../inference/scope_indexer"
16
+ require_relative "../inference/module_function_state"
16
17
  require_relative "../inference/rbs_type_translator"
17
18
  require_relative "alias_index"
18
19
  require_relative "meta_class_shape"
@@ -464,7 +465,7 @@ module Rigor
464
465
  @class_superclasses = {}
465
466
  @meta_layouts = collect_meta_layouts(scope_index)
466
467
  register_meta_classes
467
- defs = collect_method_definitions(parse_result.value)
468
+ defs = collect_method_definitions(parse_result.value, scope_index)
468
469
  # Candidate construction freezes the per-file maps above (see {#build_candidate}), so every registration
469
470
  # pass has to be finished before the first `build_candidate` call. Meta members lead the RETURNED order —
470
471
  # a value class's members and constructors read first, ahead of the methods its block body defines.
@@ -497,7 +498,8 @@ module Rigor
497
498
  # - `@module_function_methods` records `(class_name, method_name)` pairs where a `module_function` (no
498
499
  # args) call preceded the `def` inside a module body. The renderer emits `def self?.name` for these, the
499
500
  # RBS spelling that matches the dual instance + singleton dispatch the runtime produces.
500
- def collect_method_definitions(root)
501
+ def collect_method_definitions(root, scope_index)
502
+ @declared_types = scope_index[root].declared_types
501
503
  out = []
502
504
  walk_defs(root, [], false, false, out)
503
505
  out
@@ -542,6 +544,11 @@ module Rigor
542
544
  # `prefix` stays the nesting the header is WRITTEN in for {#record_superclass}: `::` re-anchors the
543
545
  # declaration, not the superclass expression beside it, which Ruby still evaluates in the enclosing cref.
544
546
  def descend_into_namespace?(node, prefix, out)
547
+ # Issue #1518 — a `class self::X` / `module self::X` the indexer declined (`self` is opaque, as under
548
+ # `REGISTRY.first.class_eval`) names no class, so its body must not be written out under the lexical
549
+ # `X`. The indexer's identity table is the single source of that decision.
550
+ return true if declined_self_header?(node)
551
+
545
552
  child_prefix = Source::ConstantPath.declaration_prefix(prefix, node.constant_path)
546
553
  return false unless child_prefix
547
554
 
@@ -552,6 +559,12 @@ module Rigor
552
559
  true
553
560
  end
554
561
 
562
+ def declined_self_header?(node)
563
+ path = node.constant_path
564
+ path.is_a?(Prism::ConstantPathNode) && path.parent.is_a?(Prism::SelfNode) &&
565
+ !@declared_types.key?(path)
566
+ end
567
+
555
568
  # ADR-14: a generated subclass declaration MUST carry its superclass, or the sidecar `sig/` misrepresents
556
569
  # the class (inherited members vanish → receiver dispatch degrades to `Dynamic`) and, worse, a nested
557
570
  # reference to an inherited type re-declares the class as a bare namespace on the RBS side and can
@@ -662,29 +675,22 @@ module Rigor
662
675
  walk_defs(namespace_node.body, prefix, false, false, out)
663
676
  end
664
677
 
678
+ # The directive's reach is {Inference::ModuleFunctionState.each_sig_gen_statement}.
665
679
  def walk_statements(stmts_node, prefix, in_singleton_class, module_function_active, out)
666
- stmts_node.body.each do |stmt|
667
- if module_function_directive?(stmt)
668
- module_function_active = true
669
- next
670
- end
671
- walk_defs(stmt, prefix, in_singleton_class, module_function_active, out)
680
+ Inference::ModuleFunctionState.each_sig_gen_statement(stmts_node.body,
681
+ module_function_active) do |stmt, active|
682
+ walk_defs(stmt, prefix, in_singleton_class, active, out)
672
683
  end
673
684
  end
674
685
 
675
- def module_function_directive?(node)
676
- return false unless node.is_a?(Prism::CallNode)
677
- return false unless node.name == :module_function && node.receiver.nil?
678
-
679
- (node.arguments&.arguments || []).empty?
680
- end
681
-
682
686
  def collect_def_node(node, prefix, in_singleton_class, module_function_active, out)
683
687
  return if prefix.empty?
684
688
 
685
689
  kind = node.receiver.is_a?(Prism::SelfNode) || in_singleton_class ? :singleton : :instance
686
690
  class_name = prefix.join("::")
687
- @module_function_methods << [class_name, node.name] if module_function_active && kind == :instance
691
+ if Inference::ModuleFunctionState.sig_gen_module_function?(module_function_active, kind)
692
+ @module_function_methods << [class_name, node.name]
693
+ end
688
694
  out << [node, class_name, kind]
689
695
  end
690
696
 
@@ -991,13 +991,37 @@ module Rigor
991
991
  end
992
992
  end
993
993
 
994
+ # The order a new union or intersection keeps: by each member's `describe(:short)`, which is
995
+ # observable in messages and in cache fingerprints, so it must not move. Two members, the common
996
+ # case, compare directly instead of paying `sort_by`'s work arrays; `sort_by` keeps an equal pair in
997
+ # place as well ([#1505](https://github.com/rigortype/rigor/issues/1505)).
994
998
  def sort_members(members)
995
- members.sort_by { |m| m.describe(:short) }
999
+ if members.size == 2
1000
+ first, second = members
1001
+ return sort_key(second) < sort_key(first) ? [second, first] : members
1002
+ end
1003
+
1004
+ members.sort_by { |member| sort_key(member) }
1005
+ end
1006
+
1007
+ # A member's `describe(:short)`, rendered at most once per type instance between garbage collections.
1008
+ # No type carrier has a method that changes it once built (a built type is frozen; one `Marshal.load`
1009
+ # restores is not, but nothing mutates it either), so a memoised key cannot go stale. The memo is weak
1010
+ # in both key and value: it keeps neither the type nor the string alive, and an entry whose string
1011
+ # nothing else holds is dropped at the next GC. It is keyed by identity on purpose: `Constant[0.0]` and
1012
+ # `Constant[-0.0]` are equal and hash alike, but render differently. A non-main Ractor may not read the
1013
+ # module's ivar, so a pool worker renders the key every time, as every run did before.
1014
+ def sort_key(member)
1015
+ return member.describe(:short) unless Ractor.main?
1016
+
1017
+ @sort_keys[member] ||= member.describe(:short)
996
1018
  end
997
1019
  end
998
1020
 
999
1021
  # Eager-allocated at load time; see `untyped` method comment above.
1000
1022
  @untyped = Dynamic.new(Top.instance)
1023
+ # The main Ractor's {sort_key} memo, allocated here for the same reason.
1024
+ @sort_keys = ObjectSpace::WeakMap.new
1001
1025
 
1002
1026
  NIL_CONSTANT = Constant.new(nil)
1003
1027
  TRUE_CONSTANT = Constant.new(true)
data/lib/rigor/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rigor
4
- VERSION = "0.4.0"
4
+ VERSION = "0.4.1"
5
5
  end
@@ -137,6 +137,16 @@ module Rigor
137
137
  @exceptions = config["exceptions"] || []
138
138
  end
139
139
 
140
+ # ADR-88 channel (c) — the detected `:ffx` / `:ffi` target decides whether the `plugin.ffi.ffx.unsupported-*`
141
+ # rules fire, and {TargetDetector} reads `<root>/Gemfile.lock` and `ext/**/extconf.rb` directly, not the
142
+ # lockfile `bundler.lockfile:` resolves to — so neither the `--incremental` snapshot's gem-set digest nor an
143
+ # edit to an analysed file covers a change in them. Declaring the target makes a flip send the warm
144
+ # snapshot cold. Everything else this plugin contributes is per-file or comes from the `ffi_catalog`
145
+ # producer (channel b).
146
+ def incremental_state_fingerprint
147
+ "target:#{@target || :ffi}"
148
+ end
149
+
140
150
  private
141
151
 
142
152
  # Every discovered name in both `dynamic_return receivers:` kinds (#701), for the two rules whose
@@ -20,6 +20,8 @@ module Rigor
20
20
 
21
21
  def self.default: () -> Environment
22
22
  def self.for_project: (?root: String, ?libraries: Array[String], ?signature_paths: Array[String | _ToPath]?, ?cache_store: untyped?, ?plugin_registry: untyped?, ?dependency_source_index: untyped?, ?rbs_extended_reporter: untyped?, ?boundary_cross_reporter: untyped?, ?source_rbs_synthesis_reporter: untyped?, ?bundler_bundle_path: String?, ?bundler_auto_detect: bool, ?synthetic_method_index: untyped?, ?project_patched_methods: untyped?) -> Environment
23
+ # authored: the return is the `signature_paths:` argument's type (authored intent) or the default [Pathname]; sig-gen types the `||` fallback untyped.
24
+ def self.project_signature_roots: (Array[String | _ToPath]? signature_paths, ?root: String) -> Array[String | _ToPath]
23
25
 
24
26
  def initialize: (?class_registry: ClassRegistry, ?rbs_loader: RbsLoader?, ?plugin_registry: untyped?, ?dependency_source_index: untyped?, ?rbs_extended_reporter: untyped?, ?boundary_cross_reporter: untyped?, ?source_rbs_synthesis_reporter: untyped?, ?synthetic_method_index: untyped?, ?project_patched_methods: untyped?) -> void
25
27
  def rbs_extended_reporter: () -> untyped?
@@ -10,4 +10,5 @@ class Rigor::Plugin::IoBoundary
10
10
  def open_url: (untyped url) -> String
11
11
  # Returns a frozen `Rigor::Cache::Descriptor` (no RBS yet -> untyped).
12
12
  def cache_descriptor: () -> untyped
13
+ def replay: (untyped descriptor) -> nil
13
14
  end
data/sig/rigor/scope.rbs CHANGED
@@ -37,6 +37,9 @@ module Rigor
37
37
  # Issue #1429 — the pre-guard type of each global or constant a guard narrowed.
38
38
  # sig-gen gap: #1154 — a reader over an ivar `initialize` assigns from a parameter.
39
39
  attr_reader guard_records: Hash[[Symbol, Symbol | String], Type::t]
40
+ # Issue #1446 — the classes a class guard narrowed a `bot` receiver to.
41
+ # sig-gen gap: #1154 — a reader over an ivar `initialize` assigns from a parameter.
42
+ attr_reader bot_guard_classes: Hash[[Symbol, Symbol | String], Array[String]]
40
43
 
41
44
  # ADR-53 Track A — the seed-time discovery tables live on the
42
45
  # DiscoveryIndex; Scope keeps per-table readers as delegates.
@@ -154,10 +157,38 @@ module Rigor
154
157
  # `untyped`: the value is an `Inference::LastLine::SelfEvidence` (or nil), and that class carries no signature,
155
158
  # as `Inference::MatchRebinding::Frame` carries none for `match_frame`.
156
159
  attr_reader implicit_self_evidence: untyped
160
+ # sig-gen gap: #1150 — `Data.define` members carry no type, as for every other row in this class.
161
+ attr_reader possible_discovered_methods: Hash[String, Hash[Symbol, Symbol]]
162
+ # sig-gen gap: #1150 — `Data.define` members carry no type, as for every other row in this class.
163
+ attr_reader possible_discovered_deferred_ranges: Hash[String, Array[[Integer, Integer, Symbol?, Symbol?, String?]]]
164
+ # sig-gen gap: #1150 — `Data.define` members carry no type, as for every other row in this class.
165
+ attr_reader contested_discovered_def_nodes: Set[Array[untyped]]
166
+ # sig-gen gap: #1150 — `Data.define` members carry no type, as for every other row in this class.
167
+ attr_reader contested_discovered_singleton_def_nodes: Set[Array[untyped]]
168
+ # sig-gen gap: #1150 — `Data.define` members carry no type, as for every other row in this class.
169
+ attr_reader contested_discovered_method_visibilities: Set[Array[untyped]]
170
+ # sig-gen gap: #1150 — `Data.define` members carry no type, as for every other row in this class.
171
+ attr_reader contested_discovered_parameter_envelopes: Set[Array[untyped]]
157
172
 
158
173
  EMPTY: DiscoveryIndex
174
+ # authored: ADR-119 WD1 — hand-written beside the `Data.define` rows above (#1150).
175
+ # authored: ADR-119 WD1 sibling pairing; no inferred signature exists for it (#1150).
176
+ SIBLINGS: Hash[Symbol, Symbol]
177
+ # authored: ADR-119 WD1 sibling pairing; no inferred signature exists for it (#1150).
178
+ EMPTY_SIBLINGS: Hash[Symbol, untyped]
179
+
180
+ # authored: ADR-119 WD1 sibling pairing; no inferred signature exists for it (#1150).
181
+ def self.empty_value: (Symbol name) -> untyped
182
+ # authored: ADR-119 WD1 sibling pairing; no inferred signature exists for it (#1150).
183
+ def self.empty_siblings: () -> Hash[Symbol, untyped]
184
+ # authored: ADR-119 WD1 sibling pairing; no inferred signature exists for it (#1150).
185
+ def self.compact_pairs: (Hash[Symbol, untyped] tables) -> Hash[Symbol, untyped]
186
+ # authored: ADR-119 WD1 sibling pairing; no inferred signature exists for it (#1150).
187
+ def self.paired?: (Symbol name) -> bool
188
+ # authored: ADR-119 WD1 sibling pairing; no inferred signature exists for it (#1150).
189
+ def sibling_tables: () -> Hash[Symbol, untyped]
159
190
 
160
- def with: (?declared_types: Hash[untyped, Type::t], ?class_ivars: Hash[String, Hash[Symbol, Type::t]], ?class_cvars: Hash[String, Hash[Symbol, Type::t]], ?program_globals: Hash[Symbol, Type::t], ?program_global_seeds: Hash[Symbol, Type::t], ?discovered_classes: Hash[String, Type::Singleton], ?in_source_constants: Hash[String, Type::t], ?discovered_methods: Hash[String, Hash[Symbol, Symbol]], ?discovered_def_nodes: Hash[String, Hash[Symbol, untyped]], ?discovered_def_nestings: Hash[untyped, Array[String]], ?discovered_singleton_def_nodes: Hash[String, Hash[Symbol, untyped]], ?discovered_def_sources: Hash[String, Hash[Symbol, String]], ?discovered_singleton_def_sources: Hash[String, Hash[Symbol, String]], ?discovered_method_visibilities: Hash[String, Hash[Symbol, Symbol]], ?discovered_parameter_envelopes: Hash[String, Hash[untyped, untyped]], ?discovered_superclasses: Hash[String, String], ?discovered_includes: Hash[String, Array[String]], ?discovered_prepends: Hash[String, Array[String]], ?discovered_extends: Hash[String, Array[String]], ?discovered_class_sources: Hash[String, Set[String]], ?constant_sources: Hash[String, Set[String]], ?constant_writers: Hash[String, Array[String]], ?constant_shadowers: Hash[String, Array[String]], ?published_constant_names: Set[String], ?local_constant_names: Set[String], ?published_constant_alias_names: Set[String], ?published_constant_ivars: Hash[String, Set[Symbol]], ?data_member_layouts: Hash[String, Array[Symbol]], ?struct_member_layouts: Hash[String, { members: Array[Symbol], keyword_init: bool }], ?param_inferred_types: Hash[[String, Symbol, Symbol], Hash[Symbol, Type::t]], ?discovered_deferred_ranges: Hash[String, Array[[Integer, Integer, Symbol?, Symbol?, String?]]], ?discovered_refinements: Hash[String, Hash[Symbol, Array[String]]], ?discovered_global_write_census: Set[Array[untyped]], ?run_generation: Object?, ?patched_line_readers: Set[Symbol], ?clears_last_status: bool, ?defines_case_equality: bool, ?implicit_self_evidence: untyped) -> DiscoveryIndex
191
+ def with: (?declared_types: Hash[untyped, Type::t], ?class_ivars: Hash[String, Hash[Symbol, Type::t]], ?class_cvars: Hash[String, Hash[Symbol, Type::t]], ?program_globals: Hash[Symbol, Type::t], ?program_global_seeds: Hash[Symbol, Type::t], ?discovered_classes: Hash[String, Type::Singleton], ?in_source_constants: Hash[String, Type::t], ?discovered_methods: Hash[String, Hash[Symbol, Symbol]], ?discovered_def_nodes: Hash[String, Hash[Symbol, untyped]], ?discovered_def_nestings: Hash[untyped, Array[String]], ?discovered_singleton_def_nodes: Hash[String, Hash[Symbol, untyped]], ?discovered_def_sources: Hash[String, Hash[Symbol, String]], ?discovered_singleton_def_sources: Hash[String, Hash[Symbol, String]], ?discovered_method_visibilities: Hash[String, Hash[Symbol, Symbol]], ?discovered_parameter_envelopes: Hash[String, Hash[untyped, untyped]], ?discovered_superclasses: Hash[String, String], ?discovered_includes: Hash[String, Array[String]], ?discovered_prepends: Hash[String, Array[String]], ?discovered_extends: Hash[String, Array[String]], ?discovered_class_sources: Hash[String, Set[String]], ?constant_sources: Hash[String, Set[String]], ?constant_writers: Hash[String, Array[String]], ?constant_shadowers: Hash[String, Array[String]], ?published_constant_names: Set[String], ?local_constant_names: Set[String], ?published_constant_alias_names: Set[String], ?published_constant_ivars: Hash[String, Set[Symbol]], ?data_member_layouts: Hash[String, Array[Symbol]], ?struct_member_layouts: Hash[String, { members: Array[Symbol], keyword_init: bool }], ?param_inferred_types: Hash[[String, Symbol, Symbol], Hash[Symbol, Type::t]], ?discovered_deferred_ranges: Hash[String, Array[[Integer, Integer, Symbol?, Symbol?, String?]]], ?discovered_refinements: Hash[String, Hash[Symbol, Array[String]]], ?discovered_global_write_census: Set[Array[untyped]], ?run_generation: Object?, ?patched_line_readers: Set[Symbol], ?clears_last_status: bool, ?defines_case_equality: bool, ?implicit_self_evidence: untyped, ?possible_discovered_methods: Hash[String, Hash[Symbol, Symbol]], ?possible_discovered_deferred_ranges: Hash[String, Array[[Integer, Integer, Symbol?, Symbol?, String?]]], ?contested_discovered_def_nodes: Set[Array[untyped]], ?contested_discovered_singleton_def_nodes: Set[Array[untyped]], ?contested_discovered_method_visibilities: Set[Array[untyped]], ?contested_discovered_parameter_envelopes: Set[Array[untyped]]) -> DiscoveryIndex
161
192
  end
162
193
 
163
194
  class IndexedKey
@@ -206,6 +237,15 @@ module Rigor
206
237
  # Issue #1429 — `rigor sig-gen` output; the parameters are not inferred here, as for `with_mutated_local`.
207
238
  def with_guarded_global: (untyped name, untyped type, untyped pre_guard, ?record: untyped) -> Scope
208
239
  def with_constant_narrowing: (untyped key, untyped type, untyped pre_guard) -> Scope
240
+ # Issue #1446 — `rigor sig-gen` output.
241
+ def with_guarded_ivar: (untyped name, untyped type, untyped pre_guard) -> Scope
242
+ def without_ivar_guard: (untyped name) -> Scope
243
+ # sig-gen gap: #1154 — the table reader over an ivar `initialize` assigns from a parameter reaches `untyped`.
244
+ def guard_narrowed_ivar?: (untyped name) -> bool
245
+ # Issue #1446 — `rigor sig-gen` output.
246
+ def with_bot_guard_classes: (untyped kind, untyped name, untyped class_names) -> Scope
247
+ # sig-gen gap: #1154 — the table reader over an ivar `initialize` assigns from a parameter reaches `untyped`.
248
+ def bot_guard_classes_for: (untyped kind, untyped name) -> Array[String]?
209
249
  def without_constant_narrowing: (untyped key) -> Scope
210
250
  def guard_narrowed?: () -> bool
211
251
  def forget_guard_narrowings: () -> Scope
@@ -259,6 +299,8 @@ module Rigor
259
299
  def class_ivars_for: (String | Symbol? class_name) -> Hash[Symbol, Type::t]
260
300
  def class_cvars_for: (String | Symbol? class_name) -> Hash[Symbol, Type::t]
261
301
  def discovered_method?: (String | Symbol class_name, String | Symbol method_name, Symbol kind) -> bool
302
+ # authored: the slot is a Prism::DefNode or a DefHandle until ADR-119 C1d/C2 decide its type.
303
+ def def_node_slot: (untyped class_name, untyped method_name, ?Symbol kind) -> untyped
262
304
  def user_def_for: (String | Symbol class_name, String | Symbol method_name) -> ::Prism::DefNode?
263
305
  def user_def_through_ancestors: (String | Symbol class_name, String | Symbol method_name, ?name_memo: Hash[untyped, untyped]) -> ([::Prism::DefNode, String] | [nil, nil])
264
306
  def discovered_method_through_ancestors?: (String | Symbol? class_name, String | Symbol method_name, Symbol kind, ?name_memo: Hash[untyped, untyped]) -> bool
@@ -88,7 +88,7 @@ diagnostics.
88
88
  | Today's diagnostics | Snapshotted into `.rigor-baseline.yml`; suppressed as long as the count does not grow. | All surfaced; every one is fixed or consciously suppressed. |
89
89
  | Hard-to-fix diagnostics | Left in the baseline. The project **trusts its test / spec suite** to cover runtime correctness for those sites — the static `T | nil` reading is worst-case-sound, the suite proves the worst case is not hit. | Fixed, or annotated `# rigor:disable <rule>` with an author-intent reason at the specific line. No blanket suppression. |
90
90
  | `severity_profile` | `lenient` (or `balanced` for a small project). | `strict`. |
91
- | Best for | Mature codebases; incremental adoption; teams that want the regression guard without a big upfront fix. | New / small projects; libraries; teams that want the maximum guarantee and have the budget to reach zero. |
91
+ | Best for | **Every project by default** — mature or brand-new, large or small. | Users fluent in both type theory and RBS who opt in and will resolve Rigor's inference gaps themselves. |
92
92
  | New diagnostics later | Surface immediately — anything beyond the baseline envelope is a regression. | Surface immediately — there is no envelope; every diagnostic is live. |
93
93
 
94
94
  Both modes give the same core guarantee: **a change that introduces
@@ -97,12 +97,35 @@ diagnostics that exist *today*. Acknowledge mode parenthesises them
97
97
  behind a baseline and leans on the test suite; strict mode refuses to
98
98
  parenthesise anything.
99
99
 
100
- If the project's first `rigor check` reports more than ~100 errors,
101
- recommend acknowledge mode as the default and let the user override.
102
- Below that, either mode is reasonable — ask.
103
-
104
- Note the ordering: the error count that feeds this recommendation (and
105
- the `severity_profile` choice in Phase 4) is only *measured* in
100
+ **Recommend acknowledge mode for every project, including new and
101
+ small ones.** Rigor's inference still has gaps, so even a fresh
102
+ project meets diagnostics on correct code. Under strict mode each one
103
+ needs a `sig/` fix, an RBS workaround, or a reasoned
104
+ `# rigor:disable` — work that takes type theory and RBS. Without that
105
+ knowledge, users rewrite correct code for the tool or scatter
106
+ suppressions. Acknowledge mode keeps the same regression guard with an
107
+ exit: a new diagnostic still fails the check, and once it is confirmed
108
+ as an inference gap, the baseline can absorb it (report the false
109
+ positive upstream). `rigor baseline regenerate` rewrites the baseline
110
+ from every live diagnostic, so run it only when a whole-project
111
+ `rigor check` (no path arguments) shows nothing but confirmed false
112
+ positives; otherwise it silences a real
113
+ bug alongside them.
114
+
115
+ Present strict mode as an opt-in for users who know type theory and
116
+ RBS, want the zero-diagnostic gate, and accept resolving inference
117
+ gaps themselves — never as the recommendation, and never because the
118
+ project is new, small, or a library. If the user picks it, tell them
119
+ how to move to acknowledge mode later: optionally relax
120
+ `severity_profile`, re-run triage, then run Phase 7 (generate a
121
+ baseline and wire `baseline:`). The profile change comes first because
122
+ it changes which rules fire.
123
+
124
+ For a large first run (more than ~100 errors), acknowledge mode also
125
+ suggests `severity_profile: lenient`; see Phase 4.
126
+
127
+ Note the ordering: the error count that feeds the `severity_profile`
128
+ choice in Phase 4 is only *measured* in
106
129
  Phase 6's triage run. Any profile written in Phase 4 is therefore
107
130
  **provisional** — Phase 6 revisits it against the measured count (see
108
131
  [`references/02-configure.md`](references/02-configure.md)
@@ -111,12 +134,15 @@ Phase 6's triage run. Any profile written in Phase 4 is therefore
111
134
  ## Non-interactive / agent-driven runs
112
135
 
113
136
  This skill has several ask-the-user points (the mode choice, the RBS
114
- collection install, the final gitignore-and-commit confirmation). When
115
- the user delegated the onboarding up front — including the adoption
116
- mode — those points resolve without blocking:
137
+ collection install, the final gitignore-and-commit confirmation). A
138
+ *delegation* is the user asking, before the run, for the onboarding to
139
+ proceed without these questions ("set it up, don't ask me"). An
140
+ ordinary request to set Rigor up is not one: ask as usual. Under a
141
+ delegation those points resolve without blocking:
117
142
 
118
143
  - The **mode** the user pre-declared is settled; record it and skip the
119
- Phase 2 presentation.
144
+ Phase 2 presentation. If the delegation names no mode, choose
145
+ acknowledge mode and say so in the report — never strict.
120
146
  - Low-risk, reversible, in-repo confirmations (`rbs collection
121
147
  install`, the `.gitignore` addition) are covered by the delegation —
122
148
  act, and note each self-answer in your report.
@@ -138,14 +138,19 @@ entirely — including a `sig/` that `rigor sig-gen --write` created.
138
138
  List only directories that contain `.rbs` files or subdirectories of
139
139
  them. Rigor walks each path recursively.
140
140
 
141
- A strict-mode plain-Ruby gem is shorter:
141
+ A plain-Ruby gem is shorter. The `balanced` default needs no
142
+ `severity_profile:` key:
142
143
 
143
144
  ```yaml
144
145
  paths:
145
146
  - lib
146
147
  test_paths:
147
148
  - test
148
- severity_profile: strict
149
+
150
+ # Phase 7 (acknowledge mode) appends this line after generating the
151
+ # baseline. Strict mode (opt-in) sets `severity_profile: strict` and
152
+ # leaves it out.
153
+ # baseline: .rigor-baseline.yml
149
154
  ```
150
155
 
151
156
  ### Test roots — write `test_paths:` explicitly
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rigortype
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rigor contributors
@@ -460,6 +460,8 @@ files:
460
460
  - lib/rigor/analysis/erb_template_detector.rb
461
461
  - lib/rigor/analysis/fact_store.rb
462
462
  - lib/rigor/analysis/incremental.rb
463
+ - lib/rigor/analysis/incremental_run_slot.rb
464
+ - lib/rigor/analysis/incremental_run_slot/write_guard.rb
463
465
  - lib/rigor/analysis/incremental_session.rb
464
466
  - lib/rigor/analysis/path_expansion.rb
465
467
  - lib/rigor/analysis/plugin_fact_fingerprint.rb
@@ -487,6 +489,8 @@ files:
487
489
  - lib/rigor/analysis/runner/run_snapshots.rb
488
490
  - lib/rigor/analysis/self_call_resolution_recorder.rb
489
491
  - lib/rigor/analysis/severity_stamp.rb
492
+ - lib/rigor/analysis/shadow_harness.rb
493
+ - lib/rigor/analysis/source_rbs_gate.rb
490
494
  - lib/rigor/analysis/template_unit_collector.rb
491
495
  - lib/rigor/analysis/template_unit_paths.rb
492
496
  - lib/rigor/analysis/template_unit_positions.rb
@@ -514,6 +518,7 @@ files:
514
518
  - lib/rigor/cache/rbs_environment_marshal_patch.rb
515
519
  - lib/rigor/cache/rbs_known_class_names.rb
516
520
  - lib/rigor/cache/store.rb
521
+ - lib/rigor/cache/value_digest.rb
517
522
  - lib/rigor/ci_detector.rb
518
523
  - lib/rigor/cli.rb
519
524
  - lib/rigor/cli/annotate_command.rb
@@ -574,6 +579,7 @@ files:
574
579
  - lib/rigor/cli/unused_command.rb
575
580
  - lib/rigor/cli/upgrade_command.rb
576
581
  - lib/rigor/config_audit.rb
582
+ - lib/rigor/config_path.rb
577
583
  - lib/rigor/configuration.rb
578
584
  - lib/rigor/configuration/dependencies.rb
579
585
  - lib/rigor/configuration/severity_profile.rb
@@ -638,6 +644,7 @@ files:
638
644
  - lib/rigor/environment/rbs_loader.rb
639
645
  - lib/rigor/environment/reflection.rb
640
646
  - lib/rigor/environment/reporters.rb
647
+ - lib/rigor/environment/source_rbs_synthesis.rb
641
648
  - lib/rigor/flow_contribution.rb
642
649
  - lib/rigor/flow_contribution/conflict.rb
643
650
  - lib/rigor/flow_contribution/element.rb
@@ -677,10 +684,16 @@ files:
677
684
  - lib/rigor/inference/closure_escape_analyzer.rb
678
685
  - lib/rigor/inference/content_join.rb
679
686
  - lib/rigor/inference/coverage_scanner.rb
687
+ - lib/rigor/inference/declaration_walk.rb
688
+ - lib/rigor/inference/declaration_walk/context.rb
689
+ - lib/rigor/inference/declaration_walk/errors.rb
690
+ - lib/rigor/inference/declaration_walk/shadow.rb
691
+ - lib/rigor/inference/declaration_walk/traversal.rb
680
692
  - lib/rigor/inference/def_handle.rb
681
693
  - lib/rigor/inference/def_node_resolver.rb
682
694
  - lib/rigor/inference/def_return_typer.rb
683
695
  - lib/rigor/inference/define_method_block_self.rb
696
+ - lib/rigor/inference/definer_resolution.rb
684
697
  - lib/rigor/inference/dynamic_origin.rb
685
698
  - lib/rigor/inference/element_read_widening.rb
686
699
  - lib/rigor/inference/error_info.rb
@@ -751,6 +764,7 @@ files:
751
764
  - lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb
752
765
  - lib/rigor/inference/method_dispatcher/uri_folding.rb
753
766
  - lib/rigor/inference/method_parameter_binder.rb
767
+ - lib/rigor/inference/module_function_state.rb
754
768
  - lib/rigor/inference/multi_target_binder.rb
755
769
  - lib/rigor/inference/mutation_rejoin.rb
756
770
  - lib/rigor/inference/mutation_widening.rb
@@ -775,6 +789,14 @@ files:
775
789
  - lib/rigor/inference/return_barrier.rb
776
790
  - lib/rigor/inference/rewrite_mutation.rb
777
791
  - lib/rigor/inference/scope_indexer.rb
792
+ - lib/rigor/inference/scope_indexer/certainty.rb
793
+ - lib/rigor/inference/scope_indexer/class_cvars_collector.rb
794
+ - lib/rigor/inference/scope_indexer/def_nestings_collector.rb
795
+ - lib/rigor/inference/scope_indexer/layered_def_nestings.rb
796
+ - lib/rigor/inference/scope_indexer/member_layouts_collector.rb
797
+ - lib/rigor/inference/scope_indexer/mixin_accumulator.rb
798
+ - lib/rigor/inference/scope_indexer/superclasses_collector.rb
799
+ - lib/rigor/inference/singleton_hook_decline.rb
778
800
  - lib/rigor/inference/singleton_object_constant.rb
779
801
  - lib/rigor/inference/statement_evaluator.rb
780
802
  - lib/rigor/inference/stored_block_call.rb
@@ -868,6 +890,8 @@ files:
868
890
  - lib/rigor/runtime/jit.rb
869
891
  - lib/rigor/scope.rb
870
892
  - lib/rigor/scope/discovery_index.rb
893
+ - lib/rigor/scope/resolution_chain.rb
894
+ - lib/rigor/scope/resolution_chain/relevance.rb
871
895
  - lib/rigor/sig_gen.rb
872
896
  - lib/rigor/sig_gen/alias_index.rb
873
897
  - lib/rigor/sig_gen/classification.rb