rigortype 0.4.3 → 0.4.5

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 (172) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -5
  3. data/data/core_overlay/ostruct.rbs +40 -0
  4. data/docs/handbook/11-sig-gen.md +104 -15
  5. data/docs/manual/02-cli-reference.md +43 -12
  6. data/docs/manual/03-configuration.md +20 -7
  7. data/docs/manual/04-diagnostics.md +56 -3
  8. data/docs/manual/05-inspecting-types.md +2 -2
  9. data/docs/manual/06-baseline.md +1 -1
  10. data/docs/manual/07-plugins.md +3 -2
  11. data/docs/manual/08-skills.md +7 -0
  12. data/docs/manual/10-mcp-server.md +40 -4
  13. data/docs/manual/18-removing-dead-code.md +23 -2
  14. data/docs/manual/plugins/rigor-rails-routes.md +2 -2
  15. data/docs/manual/plugins/rigor-rspec.md +101 -1
  16. data/docs/manual/plugins/rigor-shoulda-matchers.md +4 -1
  17. data/exe/rigor +12 -17
  18. data/lib/rigor/analysis/check_rules/clause_coverage.rb +134 -0
  19. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +111 -7
  20. data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +190 -0
  21. data/lib/rigor/analysis/check_rules/rule_ids.rb +60 -1
  22. data/lib/rigor/analysis/check_rules/source_arity.rb +32 -3
  23. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +13 -4
  24. data/lib/rigor/analysis/check_rules.rb +274 -51
  25. data/lib/rigor/analysis/dependency_source_inference/walker.rb +2 -1
  26. data/lib/rigor/analysis/incremental.rb +61 -0
  27. data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +36 -18
  28. data/lib/rigor/analysis/incremental_session.rb +270 -28
  29. data/lib/rigor/analysis/path_expansion.rb +3 -1
  30. data/lib/rigor/analysis/reachability/graph.rb +111 -14
  31. data/lib/rigor/analysis/reachability/scan.rb +305 -23
  32. data/lib/rigor/analysis/rule_catalog.rb +22 -4
  33. data/lib/rigor/analysis/run_cache_key.rb +31 -7
  34. data/lib/rigor/analysis/run_cache_probe.rb +5 -3
  35. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +48 -12
  36. data/lib/rigor/analysis/runner/pool_coordinator.rb +179 -31
  37. data/lib/rigor/analysis/runner/project_pre_passes.rb +29 -4
  38. data/lib/rigor/analysis/runner.rb +102 -72
  39. data/lib/rigor/analysis/severity_stamp.rb +45 -1
  40. data/lib/rigor/analysis/worker_session.rb +69 -2
  41. data/lib/rigor/cache/descriptor.rb +79 -12
  42. data/lib/rigor/cache/engine_source.rb +3 -2
  43. data/lib/rigor/cache/incremental_snapshot.rb +39 -5
  44. data/lib/rigor/cache/rbs_descriptor.rb +2 -1
  45. data/lib/rigor/cache/store.rb +4 -3
  46. data/lib/rigor/cli/command.rb +3 -1
  47. data/lib/rigor/cli/docs_command.rb +3 -2
  48. data/lib/rigor/cli/doctor_command.rb +1 -1
  49. data/lib/rigor/cli/explain_command.rb +3 -2
  50. data/lib/rigor/cli/mcp_command.rb +1 -1
  51. data/lib/rigor/cli/plugin_command.rb +2 -1
  52. data/lib/rigor/cli/plugins_command.rb +2 -1
  53. data/lib/rigor/cli/sig_gen_command.rb +32 -11
  54. data/lib/rigor/cli/skill_command.rb +2 -1
  55. data/lib/rigor/cli/skill_describe.rb +3 -2
  56. data/lib/rigor/cli/unused_command.rb +10 -7
  57. data/lib/rigor/cli.rb +3 -2
  58. data/lib/rigor/config_audit.rb +163 -22
  59. data/lib/rigor/configuration/dependencies.rb +19 -11
  60. data/lib/rigor/configuration/severity_profile.rb +20 -2
  61. data/lib/rigor/configuration.rb +29 -3
  62. data/lib/rigor/effects/signature_sources.rb +3 -1
  63. data/lib/rigor/environment/bundle_sig_discovery.rb +3 -2
  64. data/lib/rigor/environment/gated_signature_guard.rb +2 -1
  65. data/lib/rigor/environment/gated_signature_plan.rb +2 -1
  66. data/lib/rigor/environment/installed_gem_set.rb +2 -1
  67. data/lib/rigor/environment/missing_gem_constant_index.rb +4 -3
  68. data/lib/rigor/environment/rbs_loader.rb +67 -11
  69. data/lib/rigor/inference/block_call_timing.rb +12 -1
  70. data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
  71. data/lib/rigor/inference/captured_locals.rb +67 -13
  72. data/lib/rigor/inference/clause_widening.rb +90 -0
  73. data/lib/rigor/inference/definer_resolution.rb +101 -25
  74. data/lib/rigor/inference/expression_typer.rb +94 -9
  75. data/lib/rigor/inference/guard_rebinding.rb +7 -2
  76. data/lib/rigor/inference/intervening_ivar_reset.rb +303 -0
  77. data/lib/rigor/inference/last_line/self_evidence.rb +1 -1
  78. data/lib/rigor/inference/macro_block_self_type.rb +20 -6
  79. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +170 -35
  80. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +50 -2
  81. data/lib/rigor/inference/method_dispatcher.rb +67 -3
  82. data/lib/rigor/inference/module_function_state.rb +213 -51
  83. data/lib/rigor/inference/narrowing.rb +235 -75
  84. data/lib/rigor/inference/parameter_inference_collector.rb +4 -26
  85. data/lib/rigor/inference/project_patched_methods.rb +168 -4
  86. data/lib/rigor/inference/project_patched_mixins.rb +162 -0
  87. data/lib/rigor/inference/project_patched_scanner.rb +51 -12
  88. data/lib/rigor/inference/project_patched_visibility.rb +214 -0
  89. data/lib/rigor/inference/project_seed_tables.rb +120 -0
  90. data/lib/rigor/inference/refined_dispatch/constructed_class.rb +113 -0
  91. data/lib/rigor/inference/refined_dispatch/own_method.rb +41 -12
  92. data/lib/rigor/inference/reflective_ivar_write.rb +116 -0
  93. data/lib/rigor/inference/repeated_or_writes.rb +414 -1
  94. data/lib/rigor/inference/return_barrier.rb +16 -0
  95. data/lib/rigor/inference/scope_indexer.rb +1061 -122
  96. data/lib/rigor/inference/self_call_ivar_writes.rb +346 -0
  97. data/lib/rigor/inference/singleton_external_ahead.rb +64 -0
  98. data/lib/rigor/inference/singleton_hook_decline.rb +7 -0
  99. data/lib/rigor/inference/statement_evaluator.rb +512 -150
  100. data/lib/rigor/inference/unthreaded_rebinds.rb +3 -1
  101. data/lib/rigor/lens/anchor.rb +3 -3
  102. data/lib/rigor/lens/contract_reader.rb +60 -2
  103. data/lib/rigor/lens/document.rb +4 -3
  104. data/lib/rigor/lens/file_reader.rb +452 -73
  105. data/lib/rigor/lens/members.rb +81 -17
  106. data/lib/rigor/lens/run.rb +73 -4
  107. data/lib/rigor/lens.rb +2 -1
  108. data/lib/rigor/mcp/server.rb +63 -22
  109. data/lib/rigor/plugin/base.rb +3 -2
  110. data/lib/rigor/plugin/box_probe.rb +154 -7
  111. data/lib/rigor/plugin/io_boundary.rb +42 -6
  112. data/lib/rigor/plugin/isolation.rb +4 -3
  113. data/lib/rigor/project_environment.rb +6 -3
  114. data/lib/rigor/protection/discovery_seed.rb +9 -36
  115. data/lib/rigor/protection/mutation_cache.rb +8 -2
  116. data/lib/rigor/rooted_glob.rb +27 -0
  117. data/lib/rigor/scope/discovery_index.rb +5 -0
  118. data/lib/rigor/scope/resolution_chain/relevance.rb +14 -0
  119. data/lib/rigor/scope/resolution_chain.rb +40 -5
  120. data/lib/rigor/scope.rb +56 -8
  121. data/lib/rigor/sig_gen/alias_index.rb +2 -1
  122. data/lib/rigor/sig_gen/classification.rb +4 -1
  123. data/lib/rigor/sig_gen/generator.rb +167 -22
  124. data/lib/rigor/sig_gen/layout_index.rb +40 -1
  125. data/lib/rigor/sig_gen/method_candidate.rb +8 -0
  126. data/lib/rigor/sig_gen/observation_collector.rb +137 -11
  127. data/lib/rigor/sig_gen/path_mapper.rb +18 -2
  128. data/lib/rigor/sig_gen/renderer.rb +13 -155
  129. data/lib/rigor/sig_gen/skip_reason_catalog.rb +11 -0
  130. data/lib/rigor/sig_gen/write_reporter.rb +203 -0
  131. data/lib/rigor/sig_gen/write_result.rb +3 -1
  132. data/lib/rigor/sig_gen/writer.rb +50 -35
  133. data/lib/rigor/signature_path_audit.rb +4 -2
  134. data/lib/rigor/type/constant.rb +10 -1
  135. data/lib/rigor/version.rb +1 -1
  136. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +1 -1
  137. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +1 -1
  138. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +1 -1
  139. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +6 -1
  140. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +1 -1
  141. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +1 -1
  142. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +4 -3
  143. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +1 -1
  144. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +1 -1
  145. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +1 -1
  146. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -1
  147. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +5 -1
  148. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +1 -1
  149. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -1
  150. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
  151. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +57 -6
  152. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/target_detector.rb +1 -1
  153. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +1 -1
  154. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +5 -1
  155. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +1 -1
  156. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +1 -1
  157. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +7 -2
  158. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -5
  159. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +18 -17
  160. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -1
  161. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +155 -12
  162. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +10 -8
  163. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +1 -1
  164. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +4 -2
  165. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +2 -2
  166. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +2 -2
  167. data/sig/rigor/plugin/io_boundary.rbs +5 -2
  168. data/sig/rigor/rooted_glob.rbs +7 -0
  169. data/sig/rigor/scope.rbs +4 -3
  170. data/skills/rigor-doctor/references/01-checks.md +23 -11
  171. data/skills/rigor-type-oracle/references/01-oracle-commands.md +2 -1
  172. metadata +16 -1
@@ -18,7 +18,8 @@ module Rigor
18
18
  # Nominal[T] uniformly, so the distinction with `be_a` is observed at the carrier level but not the
19
19
  # runtime).
20
20
  # - `be_nil` — narrows `x` to `Constant<nil>`.
21
- # - `eq(LITERAL)` for a literal-value argument — narrows `x` to `Constant<literal>`.
21
+ # - `eq(LITERAL)` / `eql(LITERAL)` for a literal-value argument — meets `x` with `Constant<literal>`
22
+ # (see "Equality is the local's own method" below).
22
23
  #
23
24
  # The matchers below this floor (`be_truthy` / `be_falsey` / `be_within` / `be > / < / >=` /
24
25
  # `include` / `start_with` / `match(regex)` / `raise_error` / receive-style mocks) require either
@@ -35,16 +36,47 @@ module Rigor
35
36
  # fall through.
36
37
  # 3. The matcher is one of the recognised forms above (`be_a` / `be_kind_of` / `be_instance_of` /
37
38
  # `be_an_instance_of` / `be_nil` / `eq(literal)` / `eql(literal)` / `match(/regex/)`).
39
+ #
40
+ # ## Equality is the local's own method
41
+ #
42
+ # `expect(x).to eq(lit)` passes when `x == lit` and `eql(lit)` when `x.eql?(lit)`: the local is the
43
+ # receiver, so its own `==` / `eql?` decides, and a class with a custom `==` can equal even a `nil`,
44
+ # boolean or Symbol literal. A positive equality fact therefore never replaces the local's type (issue
45
+ # #1807). It is a meet with the local's current type, member by member:
46
+ #
47
+ # - a member of the literal's own core class (`Integer`, `Float`, `String`, `Symbol`, `NilClass`,
48
+ # `TrueClass`, `FalseClass`) narrows to the literal, a refinement of that class (`non-empty-string`,
49
+ # `decimal-int-string`, `non-zero-int`) included; a `Constant` member is kept when it equals the
50
+ # literal and dropped otherwise; a `Float` member meeting `0.0` (which `-0.0` also equals) is kept;
51
+ # - a core numeric member of another class (`Integer`, `Float`, `Rational`, `Complex`) is kept under
52
+ # `eq` (`1.0 == 1`) and dropped under `eql` (`1.0.eql?(1)` is false);
53
+ # - any other core member (`nil` against `9`, a `String` against `:a`) is dropped: built-in equality
54
+ # across those classes is false;
55
+ # - a member of a core class is kept unnarrowed when the project's source defines the operator on that
56
+ # class or on a core ancestor up to the one that owns the operator at runtime (`class Symbol; def
57
+ # ==`, `class Numeric; def eql?` for an `Integer`, `class Object; def ==` for `nil`): its own method
58
+ # decides.
59
+ #
60
+ # When any member is `Dynamic` or of any other class (a user class, `Object`, `Numeric`, `BigDecimal`),
61
+ # the analyzer emits no fact and the local keeps its type. It also emits none when the project's source
62
+ # defines the operator on the literal's class or those ancestors of it — a core `==` calls back into the
63
+ # literal's (`Integer#==` runs `other == self` for a non-numeric argument, `String#==` for one that
64
+ # responds to `to_str`), so a custom one there can make any member equal — and for an unbound local,
65
+ # when no member survives, and without a scope. A reopening that defines other methods only
66
+ # (`class String; def shout`) does not count.
67
+ #
68
+ # The negative forms (`not_to` / `to_not`) still emit a negative `Constant` fact, which the engine's fact
69
+ # narrowing does not act on, so the local keeps its type there too.
38
70
  module MatcherAnalyzer
39
71
  module_function
40
72
 
41
73
  # @param call_node — the call whose contribution we're computing. Returns nil when
42
74
  # the call shape does not match `expect(local).to matcher`.
43
- # @param environment — the surrounding environment used to resolve a
44
- # matcher's class-name argument to a `Type::Nominal`. When nil, class-name resolution falls back
45
- # to a bare `Nominal[<name>]` carrier (sound — the receiver constant may be a user class not in
46
- # RBS).
47
- def contribution_for(call_node, environment:)
75
+ # @param scope — the scope at the call, or nil. Its environment resolves a matcher's class-name
76
+ # argument to a `Type::Nominal` (without one, a bare `Nominal[<name>]` carrier — sound, the receiver
77
+ # constant may be a user class not in RBS); the local's current type in it is what a positive
78
+ # `eq` / `eql` fact meets (without one, no such fact).
79
+ def contribution_for(call_node, scope:)
48
80
  verb = assertion_verb(call_node)
49
81
  return nil if verb.nil?
50
82
  return nil unless call_node.receiver.is_a?(Prism::CallNode)
@@ -58,14 +90,15 @@ module Rigor
58
90
  matcher = matcher_call(call_node)
59
91
  return nil if matcher.nil?
60
92
 
61
- narrowed_type = narrowed_type_for(matcher, environment: environment)
93
+ negative = verb == :negative
94
+ narrowed_type = narrowed_type_for(matcher, target_local, negative: negative, scope: scope)
62
95
  return nil if narrowed_type.nil?
63
96
 
64
97
  fact = Rigor::FlowContribution::Fact.new(
65
98
  target_kind: :local,
66
99
  target_name: target_local,
67
100
  type: narrowed_type,
68
- negative: verb == :negative
101
+ negative: negative
69
102
  )
70
103
  Rigor::FlowContribution.new(post_return_facts: [fact])
71
104
  end
@@ -105,7 +138,8 @@ module Rigor
105
138
  # narrowed type. Returns nil when the matcher is
106
139
  # unrecognised or its argument shape does not match the
107
140
  # supported envelope.
108
- def narrowed_type_for(matcher, environment:)
141
+ def narrowed_type_for(matcher, target_local, negative:, scope:)
142
+ environment = scope&.environment
109
143
  return nil unless matcher.is_a?(Prism::CallNode) && matcher.receiver.nil?
110
144
 
111
145
  case matcher.name
@@ -116,7 +150,7 @@ module Rigor
116
150
 
117
151
  Rigor::Type::Combinator.constant_of(nil)
118
152
  when :eq, :eql
119
- constant_type_for_literal_arg(matcher)
153
+ equality_type_for_literal_arg(matcher, target_local, negative: negative, scope: scope)
120
154
  when :match
121
155
  # `match(/regex/)` narrows x to String. `match("...")` or `match(arbitrary_object)` falls
122
156
  # through — the broader matcher dispatch needs the receiver to be a String, but we can only
@@ -140,14 +174,123 @@ module Rigor
140
174
  end
141
175
  end
142
176
 
143
- def constant_type_for_literal_arg(matcher)
177
+ def equality_type_for_literal_arg(matcher, target_local, negative:, scope:)
144
178
  args = matcher.arguments&.arguments || []
145
179
  return nil unless args.size == 1
146
180
 
147
181
  literal_value = literal_value_for(args.first)
148
182
  return nil if literal_value.equal?(NO_LITERAL)
183
+ return Rigor::Type::Combinator.constant_of(literal_value) if negative
184
+ return nil if scope.nil?
185
+
186
+ operator = matcher.name == :eql ? :eql? : :==
187
+ equality_meet(scope.local(target_local), literal_value, operator, scope)
188
+ end
189
+
190
+ # --- equality meet (`eq` / `eql`; see "Equality is the local's own method") ---
191
+
192
+ # Core classes whose `==` / `eql?` Rigor can evaluate against a literal: across any two of them the
193
+ # answer is built in, unless the project defines the operator on one of them.
194
+ EQUALITY_CORE_CLASSES = [Integer, Float, Rational, Complex, String, Symbol, NilClass, TrueClass,
195
+ FalseClass].freeze
196
+ Ractor.make_shareable(EQUALITY_CORE_CLASSES)
197
+ private_constant :EQUALITY_CORE_CLASSES
198
+
199
+ NUMERIC_CLASS_NAMES = %w[Integer Float Rational Complex].freeze
200
+ Ractor.make_shareable(NUMERIC_CLASS_NAMES)
201
+ private_constant :NUMERIC_CLASS_NAMES
202
+
203
+ # For each core class and operator, the class and its ancestors up to and including the one that owns
204
+ # the operator at runtime (`Integer#eql?` is `Numeric`'s, `nil == x` is `BasicObject`'s). A project
205
+ # definition on any of them is the method the member runs; one above the owner is shadowed by it.
206
+ OPERATOR_OWNER_CHAINS = EQUALITY_CORE_CLASSES.each_with_object({}) do |klass, chains|
207
+ %i[== eql?].each do |operator|
208
+ ancestors = klass.ancestors
209
+ owner_index = ancestors.index(klass.instance_method(operator).owner)
210
+ chains[[klass.name, operator]] = ancestors[0..owner_index].map(&:name).freeze
211
+ end
212
+ end.freeze
213
+ Ractor.make_shareable(OPERATOR_OWNER_CHAINS)
214
+ private_constant :OPERATOR_OWNER_CHAINS
215
+
216
+ # Marks a member whose equality with the literal cannot be decided, so the whole meet gives up.
217
+ UNDECIDED = Object.new.freeze
218
+ private_constant :UNDECIDED
219
+
220
+ # The members of `current` that `x <operator> literal` admits, narrowed where the member's class is
221
+ # the literal's, or nil when any member is undecided or none survives. An unbound local (nil) is one
222
+ # undecided member.
223
+ def equality_meet(current, literal, operator, scope)
224
+ return nil if project_defines?(literal.class.name, operator, scope)
225
+
226
+ members = current.is_a?(Rigor::Type::Union) ? current.members : [current]
227
+ kept = []
228
+ members.each do |member|
229
+ met = meet_member(member, literal, operator, scope)
230
+ return nil if met.equal?(UNDECIDED)
149
231
 
150
- Rigor::Type::Combinator.constant_of(literal_value)
232
+ kept << met unless met.nil?
233
+ end
234
+ return nil if kept.empty?
235
+
236
+ Rigor::Type::Combinator.union(*kept)
237
+ end
238
+
239
+ # One member's part of the meet: the member itself or its narrowing, nil when no value of it equals
240
+ # the literal, or UNDECIDED.
241
+ def meet_member(member, literal, operator, scope)
242
+ class_name = member_class_name(member)
243
+ return UNDECIDED if class_name.nil?
244
+ return member if project_defines?(class_name, operator, scope)
245
+
246
+ case member
247
+ when Rigor::Type::Constant then member.value.public_send(operator, literal) ? member : nil
248
+ when Rigor::Type::IntegerRange
249
+ literal.is_a?(Integer) && !member.covers?(literal) ? nil : meet_class(member, class_name, literal, operator)
250
+ else meet_class(member, class_name, literal, operator)
251
+ end
252
+ end
253
+
254
+ # The core class a member's values belong to, or nil for a member that is not wholly of one. A
255
+ # refinement (`Difference`, `Refined`) belongs to its base's class, an `Intersection` to the class all
256
+ # its members share.
257
+ def member_class_name(member)
258
+ case member
259
+ when Rigor::Type::Difference, Rigor::Type::Refined then member_class_name(member.base)
260
+ when Rigor::Type::Intersection
261
+ names = member.members.map { |part| member_class_name(part) }.uniq
262
+ names.size == 1 ? names.first : nil
263
+ when Rigor::Type::FloatRange then "Float"
264
+ when Rigor::Type::Constant
265
+ klass = EQUALITY_CORE_CLASSES.find { |core| member.value.instance_of?(core) }
266
+ klass&.name
267
+ when Rigor::Type::IntegerRange then "Integer"
268
+ when Rigor::Type::Nominal
269
+ name = member.class_name
270
+ member.type_args.empty? && EQUALITY_CORE_CLASSES.any? { |core| core.name == name } ? name : nil
271
+ end
272
+ end
273
+
274
+ def meet_class(member, class_name, literal, operator)
275
+ literal_class = literal.class.name
276
+ if class_name == literal_class
277
+ literal.is_a?(Float) && literal.zero? ? member : Rigor::Type::Combinator.constant_of(literal)
278
+ elsif NUMERIC_CLASS_NAMES.include?(class_name) && NUMERIC_CLASS_NAMES.include?(literal_class)
279
+ operator == :== ? member : nil
280
+ end
281
+ end
282
+
283
+ # Whether the project's source defines `operator` on the core class `class_name` or on a core ancestor
284
+ # up to the operator's runtime owner (OPERATOR_OWNER_CHAINS): a reopening's `def`, an included or
285
+ # prepended project module, a `define_method`. The def-node walk sees other files' `def`s and records
286
+ # the dependency on them; the discovery table adds `define_method`. An RBS declaration is not
287
+ # consulted: once the project reopens `String` for anything, `String#==`'s declaring owner is a
288
+ # project class, which says nothing about who wrote `==`.
289
+ def project_defines?(class_name, operator, scope)
290
+ OPERATOR_OWNER_CHAINS.fetch([class_name, operator]).any? do |name|
291
+ !scope.user_def_through_ancestors(name, operator).first.nil? ||
292
+ scope.discovered_method_through_ancestors?(name, operator, :instance)
293
+ end
151
294
  end
152
295
 
153
296
  def string_type_for_regex_arg(matcher)
@@ -86,7 +86,7 @@ module Rigor
86
86
  # ADR-37 slice 2 — matcher narrowing (`expect(x).to be_a(T)` → `post_return_facts` on `x`),
87
87
  # method-gated by the engine on the expectation verbs.
88
88
  narrowing_facts methods: %i[to not_to to_not] do |call_node, scope|
89
- MatcherAnalyzer.contribution_for(call_node, environment: scope&.environment)&.post_return_facts
89
+ MatcherAnalyzer.contribution_for(call_node, scope: scope)&.post_return_facts
90
90
  end
91
91
 
92
92
  # ADR-52 slice 5a — binds local reads in `it` / spec bodies to their `let(:name) { ... }` block's
@@ -98,13 +98,15 @@ module Rigor
98
98
  let_binding_return_type(call_node, scope)
99
99
  end
100
100
 
101
- # ADR-88 WD1 — both contributions are per-file: the matcher narrowing facts come from the call's own AST,
102
- # and the `let`-binding return types come from each spec file's OWN `describe`/`let` structure (`let` is
103
- # file-scoped). A spec file's edit changes its own content (re-analysed already). The one cross-file input
104
- # — the optional factorybot `:factory_index` fact this plugin consumes — is itself an ADR-9 publication
105
- # captured in the incremental fact-surface digest via factorybot's own channel, so it need not appear
106
- # here. A stable sentinel declares "no OWN cross-file fact surface", keeping the plugin
107
- # incremental-capable; `--verify-incremental` backstops the claim.
101
+ # ADR-88 WD1 — both contributions are per-file: the matcher narrowing facts come from the call's own AST
102
+ # and, for `eq` / `eql`, from the local's current type and the scope's project discovery (whether the
103
+ # project defines `==` / `eql?` on a core class); the `let`-binding return types come from each spec
104
+ # file's OWN `describe`/`let` structure (`let` is file-scoped). A spec file's edit changes its own
105
+ # content (re-analysed already). The one cross-file input — the optional factorybot `:factory_index`
106
+ # fact this plugin consumes — is itself an ADR-9 publication captured in the incremental fact-surface
107
+ # digest via factorybot's own channel, so it need not appear here. A stable sentinel declares "no OWN
108
+ # cross-file fact surface", keeping the plugin incremental-capable; `--verify-incremental` backstops
109
+ # the claim.
108
110
  def incremental_state_fingerprint
109
111
  "per-file-lets"
110
112
  end
@@ -92,7 +92,7 @@ module Rigor
92
92
  # ADR-45 WD1b (#613) — boundary-probed: a root that appears later invalidates the warm run.
93
93
  next [] unless @io_boundary.directory?(absolute)
94
94
 
95
- Dir.glob(File.join(absolute, "**", "*.rb"))
95
+ Dir.glob("**/*.rb", base: absolute).map { |path| File.join(absolute, path) }
96
96
  end
97
97
  end
98
98
 
@@ -29,7 +29,9 @@ module Rigor
29
29
  # ## Reach
30
30
  #
31
31
  # All nine class-level HTTP verb methods Sinatra exposes: `get`, `post`, `put`, `delete`, `head`,
32
- # `options`, `patch`, `link`, `unlink` (`lib/sinatra/base.rb:1531-1553`). Both modular-style subclasses
32
+ # `options`, `patch`, `link`, `unlink` (`lib/sinatra/base.rb:1531-1553`), plus the `before` / `after`
33
+ # filters (`add_filter`) and the `error` / `not_found` handlers, whose blocks `compile!` turns into
34
+ # instance methods through the same `generate_method` (issue #1768). Both modular-style subclasses
33
35
  # of `Sinatra::Base` and `Sinatra::Application` (classic top-level style, when used via `class App <
34
36
  # Sinatra::Application`) match because the receiver constraint accepts every subclass.
35
37
  #
@@ -57,7 +59,7 @@ module Rigor
57
59
  block_as_methods: [
58
60
  Rigor::Plugin::Macro::BlockAsMethod.new(
59
61
  receiver_constraint: "Sinatra::Base",
60
- method_names: %i[get post put delete head options patch link unlink]
62
+ method_names: %i[get post put delete head options patch link unlink before after error not_found]
61
63
  )
62
64
  ]
63
65
  )
@@ -504,8 +504,8 @@ module Rigor
504
504
  # must not vary by machine / run. `Dir.glob` sorts its results by default on Ruby 3.0+, so the fold
505
505
  # order is already stable (the slice-1 comment's "filesystem order" caveat predates that default);
506
506
  # this walk relies on it rather than re-sorting.
507
- Dir.glob(File.join(absolute, "**", "*#{ext}")).each do |path|
508
- harvest_file(canonicalize(path), catalog, sigil_by_path, parse_errors_by_path)
507
+ Dir.glob("**/*#{ext}", base: absolute).each do |relative|
508
+ harvest_file(canonicalize(File.join(absolute, relative)), catalog, sigil_by_path, parse_errors_by_path)
509
509
  end
510
510
  end
511
511
  elsif io_boundary.file?(absolute) && extensions.any? { |ext| absolute.end_with?(ext) }
@@ -63,7 +63,7 @@ module Rigor
63
63
  @project_paths.flat_map do |entry|
64
64
  absolute = File.expand_path(entry)
65
65
  if @io_boundary.directory?(absolute)
66
- Dir.glob(File.join(absolute, "**", "*.rb"))
66
+ Dir.glob("**/*.rb", base: absolute).map { |path| File.join(absolute, path) }
67
67
  elsif absolute.end_with?(".rb") && @io_boundary.file?(absolute)
68
68
  [absolute]
69
69
  else
@@ -78,7 +78,7 @@ module Rigor
78
78
  # ADR-45 WD1b (#613) — boundary-probed: a root that appears later invalidates the warm run.
79
79
  next [] unless @io_boundary.directory?(absolute)
80
80
 
81
- Dir.glob(File.join(absolute, "**", "*.rb"))
81
+ Dir.glob("**/*.rb", base: absolute).map { |path| File.join(absolute, path) }
82
82
  end
83
83
  end
84
84
  end
@@ -4,11 +4,14 @@ class Rigor::Plugin::IoBoundary
4
4
  def file?: (untyped) -> bool
5
5
  def directory?: (untyped) -> bool
6
6
  def list_directory: (untyped) -> Array[String]
7
+ def list_directory_kinds: (untyped) -> Hash[String, untyped]
7
8
  # Records a `:names` glob row (membership only); a caller needing a match's content `read_file`s it. A `..`
8
9
  # segment, a brace or a backslash in `pattern` raises `AccessDeniedError`.
9
10
  def glob: (untyped root, untyped pattern) -> Array[String]
10
- # sig-gen gap: #1156 — the generator pins the empty-summary literal (`count: 0, first_path: nil`); the
11
- # record's fields are the running count and the first refused path, nil when nothing was refused.
11
+ # sig-gen gap: #1154 — the proposal leaves both string fields untyped. `nearest_root` comes from `@policy`, an
12
+ # ivar assigned from an `initialize` parameter (the #1154 shape); `first_path` comes from `@first_refused_path`,
13
+ # written from `record_refusal(path)`'s parameter outside `initialize`, a case #1154 as titled does not yet cover.
14
+ # The record's fields are the running count and the first refused path, nil when nothing was refused.
12
15
  def refusal_summary: () -> { count: Integer, first_path: String?, nearest_root: String? }?
13
16
  def open_url: (untyped url) -> String
14
17
  # Returns a frozen `Rigor::Cache::Descriptor` (no RBS yet -> untyped).
@@ -0,0 +1,7 @@
1
+ module Rigor
2
+ module RootedGlob
3
+ METACHARACTERS: Regexp
4
+
5
+ def self?.glob: (untyped, untyped) -> Array[String]
6
+ end
7
+ end
data/sig/rigor/scope.rbs CHANGED
@@ -61,7 +61,7 @@ module Rigor
61
61
  def discovered_singleton_def_nodes: () -> Hash[String, Hash[Symbol, untyped]]
62
62
  def discovered_def_sources: () -> Hash[String, Hash[Symbol, String]]
63
63
  def discovered_singleton_def_sources: () -> Hash[String, Hash[Symbol, String]]
64
- def discovered_method_visibilities: () -> Hash[String, Hash[Symbol, Symbol]]
64
+ def discovered_method_visibilities: () -> Hash[String, Hash[Symbol | [:singleton, Symbol], Symbol]]
65
65
  # sig-gen gap: #1148 — sig-gen skips an endless-def reader (untyped return); the row is hand-written.
66
66
  def discovered_parameter_envelopes: () -> Hash[String, Hash[untyped, untyped]]
67
67
  # sig-gen gap: #1149 — sig-gen skips the `||` fallback reader (untyped return); the row is hand-written.
@@ -128,7 +128,7 @@ module Rigor
128
128
  attr_reader discovered_singleton_def_nodes: Hash[String, Hash[Symbol, untyped]]
129
129
  attr_reader discovered_def_sources: Hash[String, Hash[Symbol, String]]
130
130
  attr_reader discovered_singleton_def_sources: Hash[String, Hash[Symbol, String]]
131
- attr_reader discovered_method_visibilities: Hash[String, Hash[Symbol, Symbol]]
131
+ attr_reader discovered_method_visibilities: Hash[String, Hash[Symbol | [:singleton, Symbol], Symbol]]
132
132
  # sig-gen gap: #1150 — `Data.define` members carry no type, as for every other row in this class.
133
133
  attr_reader discovered_parameter_envelopes: Hash[String, Hash[untyped, untyped]]
134
134
  attr_reader discovered_superclasses: Hash[String, String]
@@ -201,7 +201,7 @@ module Rigor
201
201
  # authored: ADR-119 WD1 sibling pairing; no inferred signature exists for it (#1150).
202
202
  def sibling_tables: () -> Hash[Symbol, untyped]
203
203
 
204
- 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 | String, 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, ?in_effect_refinements: 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
204
+ 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 | [:singleton, 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 | String, 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, ?in_effect_refinements: 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
205
205
  end
206
206
 
207
207
  class IndexedKey
@@ -338,6 +338,7 @@ module Rigor
338
338
  # Issue #530 — public so the external-gem ancestry probe asks the same question the walk does.
339
339
  def known_user_class?: (String | Symbol name) -> bool
340
340
  def singleton_def_for: (String | Symbol class_name, String | Symbol method_name) -> ::Prism::DefNode?
341
+ def module_function_def?: (untyped, untyped) -> (false | true)
341
342
  def singleton_def_through_ancestors: (String | Symbol class_name, String | Symbol method_name, ?name_memo: Hash[untyped, untyped]) -> ([::Prism::DefNode, String] | [nil, nil])
342
343
  def user_def_site_for: (String | Symbol class_name, String | Symbol method_name) -> String?
343
344
  def user_singleton_def_site_for: (String | Symbol class_name, String | Symbol method_name) -> String?
@@ -78,17 +78,24 @@ Make sure a single `rigortype` is on the load path.
78
78
  - `warn` — the baseline file failed to load. Check the `baseline:` path
79
79
  in the config.
80
80
 
81
- ### `plugin_gap` — the stack has a plugin that is not enabled
81
+ ### `plugin_gap` — the stack has a plugin that is not listed
82
82
 
83
- Read from the `DEPENDENCIES` section of `Gemfile.lock`.
83
+ Read from the `DEPENDENCIES` section of `Gemfile.lock`, plus the Rails
84
+ components a direct `rails` stands for. A `plugins:` entry counts as
85
+ listing its plugin even with `enabled: false`. Skipped when no
86
+ `.rigor.yml` or `.rigor.dist.yml` is discovered in the current directory.
84
87
 
85
88
  - `fail` — the project depends on gems that bundled plugins model, and
86
- **none** of those plugins is enabled: framework calls will not resolve.
87
- Add the plugins for the stack to `plugins:` (the `rigor-plugin-tune`
88
- skill does this).
89
+ `plugins:` lists **none** of the bundled plugins that model gems (those
90
+ declaring `target_gems:`; `rigor-rbs-inline` does not count): framework
91
+ calls will not resolve. Add the plugins for the stack to `plugins:` (the
92
+ `rigor-plugin-tune` skill does this). Once any such plugin is listed,
93
+ the remaining gaps are reported as the per-plugin warnings below.
89
94
  - `warn` — one per plugin that models a direct dependency but is not
90
- enabled. Enable it, or leave it out deliberately; declining a plugin is
91
- a legitimate choice.
95
+ listed. Add it, or decline it deliberately — declining is a legitimate
96
+ choice, and the way to record it is a `plugins:` entry with
97
+ `enabled: false`. Do not enable an unrelated plugin only to silence
98
+ this check.
92
99
 
93
100
  ### `gemfile_install` — Rigor is a project dependency
94
101
 
@@ -98,10 +105,15 @@ Read from the `DEPENDENCIES` section of `Gemfile.lock`.
98
105
 
99
106
  ### `bundle_layout` — gem-shipped signatures are not discovered
100
107
 
101
- `warn` when a `Gemfile.lock` exists but Rigor cannot locate the installed
102
- bundle (the gems live in the active Ruby's default gem home). Gems that
103
- ship their own `sig/` are then not loaded. Point Rigor at the install
104
- root with `bundler.bundle_path:`, install into `vendor/bundle`, or supply
108
+ `warn` when a `Gemfile.lock` exists in the current directory but Rigor
109
+ cannot locate the installed bundle — typically the gems live in the
110
+ active Ruby's default gem home. Gems that ship their own `sig/` are then
111
+ not loaded. Rigor looks only where the project records the bundle: a
112
+ `bundler.bundle_path:` in `.rigor.yml` is used alone and must exist;
113
+ without one, and unless `bundler.auto_detect: false`, it reads
114
+ `.bundle/config`'s `BUNDLE_PATH`, then an in-tree `vendor/bundle`, then
115
+ the global Bundler config. Point Rigor at the install root with
116
+ `bundler.bundle_path:`, install into `vendor/bundle`, or supply
105
117
  signatures with `rbs collection install` instead.
106
118
 
107
119
  ## Not covered by `rigor doctor`: is the analysis seeing your code?
@@ -212,7 +212,8 @@ rigor explain [<rule>]
212
212
  | `--format=text\|json` | Default `text`. |
213
213
 
214
214
  With no argument it lists every rule. With a rule id, a legacy alias, or
215
- a family prefix (`call`, `flow`, `assert`, `dump`, `def`) it prints the
215
+ a family prefix (`call`, `flow`, `def`, `assert`, `dump`, `global`,
216
+ `suppression`, `static`, `effect`) it prints the
216
217
  rule's firing conditions, the severity per profile, the evidence tier,
217
218
  and how to suppress it.
218
219
 
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.3
4
+ version: 0.4.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rigor contributors
@@ -321,6 +321,7 @@ files:
321
321
  - data/core_overlay/module.rbs
322
322
  - data/core_overlay/numeric.rbs
323
323
  - data/core_overlay/object_space.rbs
324
+ - data/core_overlay/ostruct.rbs
324
325
  - data/core_overlay/pathname.rbs
325
326
  - data/core_overlay/proc.rbs
326
327
  - data/core_overlay/process.rbs
@@ -445,6 +446,7 @@ files:
445
446
  - lib/rigor/analysis/buffer_binding.rb
446
447
  - lib/rigor/analysis/check_rules.rb
447
448
  - lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb
449
+ - lib/rigor/analysis/check_rules/clause_coverage.rb
448
450
  - lib/rigor/analysis/check_rules/dead_assignment_collector.rb
449
451
  - lib/rigor/analysis/check_rules/dead_version_guard_arms.rb
450
452
  - lib/rigor/analysis/check_rules/declaration_sourced_guard.rb
@@ -454,6 +456,7 @@ files:
454
456
  - lib/rigor/analysis/check_rules/lexical_method_sites.rb
455
457
  - lib/rigor/analysis/check_rules/main_pass_collector.rb
456
458
  - lib/rigor/analysis/check_rules/published_constant_guard.rb
459
+ - lib/rigor/analysis/check_rules/rbs_receiver_surface.rb
457
460
  - lib/rigor/analysis/check_rules/return_in_ensure_collector.rb
458
461
  - lib/rigor/analysis/check_rules/ruby_deprecations.rb
459
462
  - lib/rigor/analysis/check_rules/rule_ids.rb
@@ -704,6 +707,7 @@ files:
704
707
  - lib/rigor/inference/builtins/struct_catalog.rb
705
708
  - lib/rigor/inference/builtins/time_catalog.rb
706
709
  - lib/rigor/inference/captured_locals.rb
710
+ - lib/rigor/inference/clause_widening.rb
707
711
  - lib/rigor/inference/closure_escape_analyzer.rb
708
712
  - lib/rigor/inference/content_join.rb
709
713
  - lib/rigor/inference/coverage_scanner.rb
@@ -742,6 +746,7 @@ files:
742
746
  - lib/rigor/inference/in_effect_refinements/walk.rb
743
747
  - lib/rigor/inference/index_write_widening.rb
744
748
  - lib/rigor/inference/indexed_narrowing.rb
749
+ - lib/rigor/inference/intervening_ivar_reset.rb
745
750
  - lib/rigor/inference/jump_targets.rb
746
751
  - lib/rigor/inference/key_presence_guard.rb
747
752
  - lib/rigor/inference/key_presence_guard/scan.rb
@@ -818,7 +823,10 @@ files:
818
823
  - lib/rigor/inference/project_method_ownership.rb
819
824
  - lib/rigor/inference/project_patched_definedness_guard.rb
820
825
  - lib/rigor/inference/project_patched_methods.rb
826
+ - lib/rigor/inference/project_patched_mixins.rb
821
827
  - lib/rigor/inference/project_patched_scanner.rb
828
+ - lib/rigor/inference/project_patched_visibility.rb
829
+ - lib/rigor/inference/project_seed_tables.rb
822
830
  - lib/rigor/inference/protection_scanner.rb
823
831
  - lib/rigor/inference/range_constant.rb
824
832
  - lib/rigor/inference/rbs_type_translator.rb
@@ -828,8 +836,10 @@ files:
828
836
  - lib/rigor/inference/refine_self.rb
829
837
  - lib/rigor/inference/refined_dispatch.rb
830
838
  - lib/rigor/inference/refined_dispatch/class_object.rb
839
+ - lib/rigor/inference/refined_dispatch/constructed_class.rb
831
840
  - lib/rigor/inference/refined_dispatch/own_method.rb
832
841
  - lib/rigor/inference/refinement_mutation.rb
842
+ - lib/rigor/inference/reflective_ivar_write.rb
833
843
  - lib/rigor/inference/repeated_or_writes.rb
834
844
  - lib/rigor/inference/return_barrier.rb
835
845
  - lib/rigor/inference/rewrite_mutation.rb
@@ -841,6 +851,8 @@ files:
841
851
  - lib/rigor/inference/scope_indexer/member_layouts_collector.rb
842
852
  - lib/rigor/inference/scope_indexer/mixin_accumulator.rb
843
853
  - lib/rigor/inference/scope_indexer/superclasses_collector.rb
854
+ - lib/rigor/inference/self_call_ivar_writes.rb
855
+ - lib/rigor/inference/singleton_external_ahead.rb
844
856
  - lib/rigor/inference/singleton_hook_decline.rb
845
857
  - lib/rigor/inference/singleton_object_constant.rb
846
858
  - lib/rigor/inference/statement_evaluator.rb
@@ -943,6 +955,7 @@ files:
943
955
  - lib/rigor/reflection.rb
944
956
  - lib/rigor/reflection/constant_ancestors.rb
945
957
  - lib/rigor/reflection/constant_path.rb
958
+ - lib/rigor/rooted_glob.rb
946
959
  - lib/rigor/runtime/jit.rb
947
960
  - lib/rigor/scope.rb
948
961
  - lib/rigor/scope/discovery_index.rb
@@ -966,6 +979,7 @@ files:
966
979
  - lib/rigor/sig_gen/skip_reason_catalog.rb
967
980
  - lib/rigor/sig_gen/superclass_spelling.rb
968
981
  - lib/rigor/sig_gen/type_elaborator.rb
982
+ - lib/rigor/sig_gen/write_reporter.rb
969
983
  - lib/rigor/sig_gen/write_result.rb
970
984
  - lib/rigor/sig_gen/writer.rb
971
985
  - lib/rigor/signature_path_audit.rb
@@ -1287,6 +1301,7 @@ files:
1287
1301
  - sig/rigor/plugin/type_node_resolver.rbs
1288
1302
  - sig/rigor/rbs_extended.rbs
1289
1303
  - sig/rigor/reflection.rbs
1304
+ - sig/rigor/rooted_glob.rbs
1290
1305
  - sig/rigor/scope.rbs
1291
1306
  - sig/rigor/sig_gen/skip_reason_catalog.rbs
1292
1307
  - sig/rigor/source.rbs