rigortype 0.2.7 → 0.2.8

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 (414) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/docs/manual/04-diagnostics.md +7 -4
  4. data/docs/manual/06-baseline.md +5 -2
  5. data/docs/manual/15-type-protection-coverage.md +6 -4
  6. data/lib/rigor/analysis/baseline.rb +56 -96
  7. data/lib/rigor/analysis/buffer_binding.rb +13 -20
  8. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +29 -48
  9. data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +35 -54
  10. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +23 -37
  11. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +16 -23
  12. data/lib/rigor/analysis/check_rules/rule_walk.rb +48 -71
  13. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +19 -28
  14. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +48 -67
  15. data/lib/rigor/analysis/check_rules.rb +174 -274
  16. data/lib/rigor/analysis/dependency_recorder.rb +26 -38
  17. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +13 -25
  18. data/lib/rigor/analysis/dependency_source_inference/builder.rb +17 -30
  19. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +15 -26
  20. data/lib/rigor/analysis/dependency_source_inference/index.rb +45 -76
  21. data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +23 -40
  22. data/lib/rigor/analysis/dependency_source_inference/walker.rb +50 -83
  23. data/lib/rigor/analysis/dependency_source_inference.rb +6 -10
  24. data/lib/rigor/analysis/diagnostic.rb +48 -75
  25. data/lib/rigor/analysis/erb_template_detector.rb +13 -18
  26. data/lib/rigor/analysis/fact_store.rb +12 -22
  27. data/lib/rigor/analysis/incremental.rb +47 -63
  28. data/lib/rigor/analysis/incremental_session.rb +74 -100
  29. data/lib/rigor/analysis/project_scan.rb +12 -23
  30. data/lib/rigor/analysis/result.rb +3 -5
  31. data/lib/rigor/analysis/rule_catalog.rb +67 -95
  32. data/lib/rigor/analysis/run_stats.rb +38 -61
  33. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +101 -160
  34. data/lib/rigor/analysis/runner/pool_coordinator.rb +121 -188
  35. data/lib/rigor/analysis/runner/project_pre_passes.rb +62 -96
  36. data/lib/rigor/analysis/runner/run_snapshots.rb +12 -18
  37. data/lib/rigor/analysis/runner.rb +220 -333
  38. data/lib/rigor/analysis/self_call_resolution_recorder.rb +30 -43
  39. data/lib/rigor/analysis/worker_session.rb +64 -102
  40. data/lib/rigor/ast/type_node.rb +6 -8
  41. data/lib/rigor/ast.rb +6 -10
  42. data/lib/rigor/bleeding_edge.rb +27 -35
  43. data/lib/rigor/builtins/hkt_builtins.rb +7 -14
  44. data/lib/rigor/builtins/imported_refinements.rb +106 -149
  45. data/lib/rigor/builtins/predefined_constant_refinements.rb +51 -65
  46. data/lib/rigor/builtins/regex_refinement.rb +32 -43
  47. data/lib/rigor/builtins/static_return_refinements.rb +57 -81
  48. data/lib/rigor/cache/descriptor.rb +58 -94
  49. data/lib/rigor/cache/incremental_snapshot.rb +32 -48
  50. data/lib/rigor/cache/rbs_cache_producer.rb +10 -15
  51. data/lib/rigor/cache/rbs_class_ancestor_table.rb +10 -16
  52. data/lib/rigor/cache/rbs_class_type_param_names.rb +10 -16
  53. data/lib/rigor/cache/rbs_constant_table.rb +8 -13
  54. data/lib/rigor/cache/rbs_descriptor.rb +8 -13
  55. data/lib/rigor/cache/rbs_environment.rb +11 -19
  56. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +11 -19
  57. data/lib/rigor/cache/rbs_known_class_names.rb +8 -12
  58. data/lib/rigor/cache/store.rb +96 -151
  59. data/lib/rigor/cli/annotate_command.rb +51 -88
  60. data/lib/rigor/cli/baseline_command.rb +10 -17
  61. data/lib/rigor/cli/check_command.rb +121 -199
  62. data/lib/rigor/cli/check_runner_factory.rb +8 -14
  63. data/lib/rigor/cli/ci_detector.rb +9 -14
  64. data/lib/rigor/cli/command.rb +8 -14
  65. data/lib/rigor/cli/coverage_command.rb +19 -26
  66. data/lib/rigor/cli/coverage_mutation.rb +14 -19
  67. data/lib/rigor/cli/coverage_report.rb +0 -2
  68. data/lib/rigor/cli/coverage_scan.rb +7 -11
  69. data/lib/rigor/cli/diagnostic_formats.rb +43 -62
  70. data/lib/rigor/cli/diff_command.rb +10 -20
  71. data/lib/rigor/cli/docs_command.rb +26 -40
  72. data/lib/rigor/cli/doctor_command.rb +8 -9
  73. data/lib/rigor/cli/explain_command.rb +4 -7
  74. data/lib/rigor/cli/fused_protection_renderer.rb +4 -5
  75. data/lib/rigor/cli/fused_protection_report.rb +6 -8
  76. data/lib/rigor/cli/lsp_command.rb +11 -19
  77. data/lib/rigor/cli/mcp_command.rb +4 -6
  78. data/lib/rigor/cli/mutation_protection_renderer.rb +3 -4
  79. data/lib/rigor/cli/mutation_protection_report.rb +6 -9
  80. data/lib/rigor/cli/options.rb +11 -19
  81. data/lib/rigor/cli/plugin_command.rb +18 -30
  82. data/lib/rigor/cli/plugins_command.rb +29 -51
  83. data/lib/rigor/cli/plugins_renderer.rb +12 -20
  84. data/lib/rigor/cli/prism_colorizer.rb +13 -19
  85. data/lib/rigor/cli/protection_renderer.rb +5 -7
  86. data/lib/rigor/cli/protection_report.rb +27 -17
  87. data/lib/rigor/cli/renderable.rb +4 -6
  88. data/lib/rigor/cli/show_bleedingedge_command.rb +5 -8
  89. data/lib/rigor/cli/sig_gen_command.rb +14 -26
  90. data/lib/rigor/cli/skill_command.rb +30 -47
  91. data/lib/rigor/cli/skill_describe.rb +40 -64
  92. data/lib/rigor/cli/trace_command.rb +9 -16
  93. data/lib/rigor/cli/trace_renderer.rb +35 -51
  94. data/lib/rigor/cli/triage_command.rb +6 -10
  95. data/lib/rigor/cli/triage_renderer.rb +1 -2
  96. data/lib/rigor/cli/type_of_command.rb +14 -23
  97. data/lib/rigor/cli/type_of_renderer.rb +3 -5
  98. data/lib/rigor/cli/type_scan_command.rb +6 -10
  99. data/lib/rigor/cli/type_scan_renderer.rb +3 -5
  100. data/lib/rigor/cli/type_scan_report.rb +2 -3
  101. data/lib/rigor/cli/upgrade_command.rb +2 -3
  102. data/lib/rigor/cli.rb +16 -28
  103. data/lib/rigor/config_audit.rb +35 -44
  104. data/lib/rigor/configuration/dependencies.rb +41 -70
  105. data/lib/rigor/configuration/severity_profile.rb +28 -42
  106. data/lib/rigor/configuration.rb +150 -241
  107. data/lib/rigor/environment/bundle_sig_discovery.rb +74 -118
  108. data/lib/rigor/environment/class_registry.rb +19 -26
  109. data/lib/rigor/environment/constant_type_cache_holder.rb +14 -21
  110. data/lib/rigor/environment/hkt_registry_holder.rb +6 -12
  111. data/lib/rigor/environment/lockfile_resolver.rb +31 -47
  112. data/lib/rigor/environment/rbs_collection_discovery.rb +39 -63
  113. data/lib/rigor/environment/rbs_coverage_report.rb +24 -42
  114. data/lib/rigor/environment/rbs_loader.rb +420 -518
  115. data/lib/rigor/environment/reflection.rb +28 -53
  116. data/lib/rigor/environment/reporters.rb +13 -24
  117. data/lib/rigor/environment.rb +176 -291
  118. data/lib/rigor/flow_contribution/conflict.rb +16 -27
  119. data/lib/rigor/flow_contribution/element.rb +7 -12
  120. data/lib/rigor/flow_contribution/fact.rb +33 -57
  121. data/lib/rigor/flow_contribution/merge_result.rb +6 -9
  122. data/lib/rigor/flow_contribution/merger.rb +32 -47
  123. data/lib/rigor/flow_contribution.rb +37 -55
  124. data/lib/rigor/inference/acceptance.rb +133 -219
  125. data/lib/rigor/inference/block_parameter_binder.rb +52 -82
  126. data/lib/rigor/inference/body_fixpoint.rb +22 -30
  127. data/lib/rigor/inference/budget_trace.rb +43 -63
  128. data/lib/rigor/inference/builtins/array_catalog.rb +4 -6
  129. data/lib/rigor/inference/builtins/comparable_catalog.rb +4 -6
  130. data/lib/rigor/inference/builtins/complex_catalog.rb +13 -19
  131. data/lib/rigor/inference/builtins/date_catalog.rb +38 -67
  132. data/lib/rigor/inference/builtins/encoding_catalog.rb +22 -36
  133. data/lib/rigor/inference/builtins/enumerable_catalog.rb +4 -6
  134. data/lib/rigor/inference/builtins/exception_catalog.rb +38 -57
  135. data/lib/rigor/inference/builtins/hash_catalog.rb +9 -14
  136. data/lib/rigor/inference/builtins/method_catalog.rb +29 -48
  137. data/lib/rigor/inference/builtins/numeric_catalog.rb +13 -18
  138. data/lib/rigor/inference/builtins/pathname_catalog.rb +8 -13
  139. data/lib/rigor/inference/builtins/proc_catalog.rb +38 -62
  140. data/lib/rigor/inference/builtins/random_catalog.rb +21 -32
  141. data/lib/rigor/inference/builtins/range_catalog.rb +12 -21
  142. data/lib/rigor/inference/builtins/rational_catalog.rb +9 -15
  143. data/lib/rigor/inference/builtins/re_catalog.rb +29 -48
  144. data/lib/rigor/inference/builtins/set_catalog.rb +18 -27
  145. data/lib/rigor/inference/builtins/string_catalog.rb +12 -19
  146. data/lib/rigor/inference/builtins/struct_catalog.rb +18 -30
  147. data/lib/rigor/inference/builtins/time_catalog.rb +28 -44
  148. data/lib/rigor/inference/closure_escape_analyzer.rb +44 -67
  149. data/lib/rigor/inference/coverage_scanner.rb +13 -18
  150. data/lib/rigor/inference/def_return_typer.rb +8 -14
  151. data/lib/rigor/inference/dynamic_origin.rb +23 -20
  152. data/lib/rigor/inference/expression_typer.rb +774 -1152
  153. data/lib/rigor/inference/fallback.rb +8 -12
  154. data/lib/rigor/inference/fallback_tracer.rb +4 -10
  155. data/lib/rigor/inference/flow_tracer.rb +25 -36
  156. data/lib/rigor/inference/hkt_body.rb +45 -68
  157. data/lib/rigor/inference/hkt_body_parser.rb +33 -52
  158. data/lib/rigor/inference/hkt_reducer.rb +38 -59
  159. data/lib/rigor/inference/hkt_registry.rb +50 -76
  160. data/lib/rigor/inference/indexed_narrowing.rb +55 -81
  161. data/lib/rigor/inference/macro_block_self_type.rb +21 -34
  162. data/lib/rigor/inference/method_dispatcher/array_to_h_folding.rb +11 -18
  163. data/lib/rigor/inference/method_dispatcher/block_folding.rb +49 -83
  164. data/lib/rigor/inference/method_dispatcher/call_context.rb +16 -23
  165. data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +13 -22
  166. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +307 -454
  167. data/lib/rigor/inference/method_dispatcher/data_folding.rb +24 -34
  168. data/lib/rigor/inference/method_dispatcher/file_folding.rb +22 -37
  169. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +52 -81
  170. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +51 -78
  171. data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +60 -97
  172. data/lib/rigor/inference/method_dispatcher/math_folding.rb +23 -34
  173. data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +18 -26
  174. data/lib/rigor/inference/method_dispatcher/method_folding.rb +37 -71
  175. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +114 -182
  176. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +122 -193
  177. data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +15 -24
  178. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +64 -93
  179. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +31 -45
  180. data/lib/rigor/inference/method_dispatcher/set_folding.rb +6 -9
  181. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +356 -511
  182. data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +22 -32
  183. data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +15 -21
  184. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +66 -82
  185. data/lib/rigor/inference/method_dispatcher/time_folding.rb +10 -15
  186. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +9 -13
  187. data/lib/rigor/inference/method_dispatcher.rb +378 -524
  188. data/lib/rigor/inference/method_parameter_binder.rb +81 -114
  189. data/lib/rigor/inference/multi_target_binder.rb +51 -68
  190. data/lib/rigor/inference/mutation_widening.rb +115 -158
  191. data/lib/rigor/inference/narrowing.rb +509 -727
  192. data/lib/rigor/inference/origin_lookup.rb +38 -0
  193. data/lib/rigor/inference/parameter_inference_collector.rb +100 -100
  194. data/lib/rigor/inference/precision_scanner.rb +26 -35
  195. data/lib/rigor/inference/project_patched_methods.rb +20 -32
  196. data/lib/rigor/inference/project_patched_scanner.rb +23 -37
  197. data/lib/rigor/inference/protection_scanner.rb +16 -18
  198. data/lib/rigor/inference/rbs_type_translator.rb +49 -66
  199. data/lib/rigor/inference/scope_indexer.rb +473 -817
  200. data/lib/rigor/inference/statement_evaluator.rb +706 -1111
  201. data/lib/rigor/inference/struct_fold_safety.rb +34 -48
  202. data/lib/rigor/inference/synthetic_method.rb +9 -16
  203. data/lib/rigor/inference/synthetic_method_index.rb +20 -35
  204. data/lib/rigor/inference/synthetic_method_scanner.rb +63 -103
  205. data/lib/rigor/language_server/buffer_resolution.rb +6 -9
  206. data/lib/rigor/language_server/buffer_table.rb +11 -18
  207. data/lib/rigor/language_server/completion_provider.rb +69 -116
  208. data/lib/rigor/language_server/debouncer.rb +16 -25
  209. data/lib/rigor/language_server/diagnostic_publisher.rb +26 -40
  210. data/lib/rigor/language_server/document_symbol_provider.rb +14 -19
  211. data/lib/rigor/language_server/folding_range_provider.rb +11 -19
  212. data/lib/rigor/language_server/hover_provider.rb +8 -11
  213. data/lib/rigor/language_server/hover_renderer.rb +56 -93
  214. data/lib/rigor/language_server/loop.rb +14 -19
  215. data/lib/rigor/language_server/project_context.rb +46 -74
  216. data/lib/rigor/language_server/selection_range_provider.rb +9 -12
  217. data/lib/rigor/language_server/server.rb +55 -83
  218. data/lib/rigor/language_server/signature_help_provider.rb +29 -46
  219. data/lib/rigor/language_server/synchronized_writer.rb +4 -7
  220. data/lib/rigor/language_server/uri.rb +8 -13
  221. data/lib/rigor/language_server.rb +4 -6
  222. data/lib/rigor/mcp/loop.rb +2 -3
  223. data/lib/rigor/mcp/server.rb +4 -7
  224. data/lib/rigor/mcp.rb +3 -6
  225. data/lib/rigor/plugin/access_denied_error.rb +5 -8
  226. data/lib/rigor/plugin/additional_initializer.rb +21 -31
  227. data/lib/rigor/plugin/base.rb +335 -518
  228. data/lib/rigor/plugin/blueprint.rb +14 -23
  229. data/lib/rigor/plugin/box.rb +18 -29
  230. data/lib/rigor/plugin/fact_store.rb +16 -26
  231. data/lib/rigor/plugin/inflector.rb +37 -53
  232. data/lib/rigor/plugin/io_boundary.rb +33 -56
  233. data/lib/rigor/plugin/isolation.rb +42 -55
  234. data/lib/rigor/plugin/load_error.rb +10 -15
  235. data/lib/rigor/plugin/loader.rb +30 -49
  236. data/lib/rigor/plugin/macro/block_as_method.rb +20 -32
  237. data/lib/rigor/plugin/macro/heredoc_template.rb +35 -58
  238. data/lib/rigor/plugin/macro/nested_class_template.rb +22 -36
  239. data/lib/rigor/plugin/macro/trait_registry.rb +34 -51
  240. data/lib/rigor/plugin/macro.rb +10 -15
  241. data/lib/rigor/plugin/manifest.rb +85 -144
  242. data/lib/rigor/plugin/node_context.rb +14 -22
  243. data/lib/rigor/plugin/node_rule_walk.rb +49 -74
  244. data/lib/rigor/plugin/protocol_contract.rb +25 -39
  245. data/lib/rigor/plugin/registry.rb +132 -205
  246. data/lib/rigor/plugin/services.rb +21 -33
  247. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -19
  248. data/lib/rigor/plugin/trust_policy.rb +24 -38
  249. data/lib/rigor/plugin/type_node_resolver.rb +15 -24
  250. data/lib/rigor/protection/diagnostic_oracle.rb +11 -13
  251. data/lib/rigor/protection/mutation_scanner.rb +27 -35
  252. data/lib/rigor/protection/mutator.rb +50 -70
  253. data/lib/rigor/protection/test_suite_oracle.rb +20 -27
  254. data/lib/rigor/rbs_extended/conformance_checker.rb +48 -68
  255. data/lib/rigor/rbs_extended/hkt_directives.rb +28 -54
  256. data/lib/rigor/rbs_extended/reporter.rb +24 -40
  257. data/lib/rigor/rbs_extended.rb +107 -197
  258. data/lib/rigor/reflection.rb +68 -86
  259. data/lib/rigor/scope/discovery_index.rb +14 -19
  260. data/lib/rigor/scope.rb +255 -310
  261. data/lib/rigor/sig_gen/classification.rb +6 -10
  262. data/lib/rigor/sig_gen/generator.rb +197 -323
  263. data/lib/rigor/sig_gen/layout_index.rb +12 -20
  264. data/lib/rigor/sig_gen/method_candidate.rb +12 -17
  265. data/lib/rigor/sig_gen/observation_collector.rb +38 -70
  266. data/lib/rigor/sig_gen/observed_call.rb +13 -23
  267. data/lib/rigor/sig_gen/path_mapper.rb +17 -29
  268. data/lib/rigor/sig_gen/renderer.rb +7 -13
  269. data/lib/rigor/sig_gen/type_elaborator.rb +15 -28
  270. data/lib/rigor/sig_gen/write_result.rb +8 -16
  271. data/lib/rigor/sig_gen/writer.rb +95 -174
  272. data/lib/rigor/sig_gen.rb +3 -6
  273. data/lib/rigor/signature_path_audit.rb +24 -30
  274. data/lib/rigor/source/constant_path.rb +10 -14
  275. data/lib/rigor/source/literals.rb +31 -45
  276. data/lib/rigor/source/node_locator.rb +9 -11
  277. data/lib/rigor/source/node_walker.rb +9 -13
  278. data/lib/rigor/source.rb +3 -4
  279. data/lib/rigor/testing.rb +16 -20
  280. data/lib/rigor/triage/catalogue.rb +38 -62
  281. data/lib/rigor/triage.rb +31 -52
  282. data/lib/rigor/trinary.rb +9 -13
  283. data/lib/rigor/type/acceptance_router.rb +4 -6
  284. data/lib/rigor/type/accepts_result.rb +10 -14
  285. data/lib/rigor/type/app.rb +19 -27
  286. data/lib/rigor/type/bot.rb +4 -6
  287. data/lib/rigor/type/bound_method.rb +10 -15
  288. data/lib/rigor/type/combinator.rb +165 -257
  289. data/lib/rigor/type/constant.rb +23 -34
  290. data/lib/rigor/type/data_class.rb +10 -15
  291. data/lib/rigor/type/data_instance.rb +14 -20
  292. data/lib/rigor/type/difference.rb +21 -32
  293. data/lib/rigor/type/dynamic.rb +3 -5
  294. data/lib/rigor/type/hash_shape.rb +32 -18
  295. data/lib/rigor/type/integer_range.rb +11 -16
  296. data/lib/rigor/type/intersection.rb +27 -42
  297. data/lib/rigor/type/nominal.rb +10 -15
  298. data/lib/rigor/type/plain_lattice.rb +9 -13
  299. data/lib/rigor/type/refined.rb +67 -114
  300. data/lib/rigor/type/singleton.rb +4 -6
  301. data/lib/rigor/type/struct_class.rb +11 -16
  302. data/lib/rigor/type/struct_instance.rb +15 -21
  303. data/lib/rigor/type/tuple.rb +14 -19
  304. data/lib/rigor/type/union.rb +30 -42
  305. data/lib/rigor/type_node/generic.rb +14 -26
  306. data/lib/rigor/type_node/identifier.rb +12 -19
  307. data/lib/rigor/type_node.rb +3 -12
  308. data/lib/rigor/value_semantics.rb +16 -21
  309. data/lib/rigor/version.rb +1 -1
  310. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +18 -30
  311. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +16 -29
  312. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +12 -23
  313. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +22 -39
  314. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +26 -46
  315. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +51 -87
  316. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +10 -17
  317. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +26 -39
  318. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +118 -176
  319. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +30 -51
  320. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +57 -92
  321. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +71 -108
  322. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +13 -22
  323. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +15 -25
  324. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +8 -13
  325. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +16 -24
  326. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +30 -47
  327. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +79 -129
  328. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +36 -57
  329. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +27 -41
  330. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +16 -24
  331. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +148 -235
  332. data/plugins/rigor-activerecord/lib/rigor-activerecord.rb +3 -5
  333. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +8 -14
  334. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +9 -15
  335. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +4 -7
  336. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +23 -41
  337. data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +3 -5
  338. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +6 -10
  339. data/plugins/rigor-activesupport-core-ext/lib/rigor-activesupport-core-ext.rb +8 -11
  340. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +30 -45
  341. data/plugins/rigor-devise/lib/rigor-devise.rb +3 -5
  342. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +43 -69
  343. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +26 -35
  344. data/plugins/rigor-dry-schema/lib/rigor-dry-schema.rb +3 -5
  345. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +30 -47
  346. data/plugins/rigor-dry-struct/lib/rigor-dry-struct.rb +3 -5
  347. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +67 -113
  348. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +29 -46
  349. data/plugins/rigor-dry-types/lib/rigor-dry-types.rb +3 -5
  350. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -20
  351. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +14 -23
  352. data/plugins/rigor-dry-validation/lib/rigor-dry-validation.rb +2 -3
  353. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +22 -37
  354. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +28 -46
  355. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +13 -17
  356. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +30 -47
  357. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +52 -80
  358. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +21 -31
  359. data/plugins/rigor-graphql/lib/rigor-graphql.rb +3 -5
  360. data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +11 -17
  361. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +20 -34
  362. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +45 -75
  363. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +22 -36
  364. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +22 -41
  365. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +17 -32
  366. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +6 -12
  367. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +4 -8
  368. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +17 -32
  369. data/plugins/rigor-rails/lib/rigor-rails.rb +11 -18
  370. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +58 -92
  371. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +20 -34
  372. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +20 -31
  373. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +49 -82
  374. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +55 -92
  375. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +51 -90
  376. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +17 -30
  377. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +38 -70
  378. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +42 -74
  379. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +280 -497
  380. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +54 -89
  381. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +32 -54
  382. data/plugins/rigor-rbs-inline/lib/rigor-rbs-inline.rb +8 -13
  383. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +9 -16
  384. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +24 -39
  385. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +13 -22
  386. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +34 -56
  387. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +22 -42
  388. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +43 -75
  389. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +27 -39
  390. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +14 -19
  391. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +20 -34
  392. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +28 -47
  393. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +21 -37
  394. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +13 -22
  395. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +18 -31
  396. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +6 -10
  397. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +14 -29
  398. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +23 -41
  399. data/plugins/rigor-sinatra/lib/rigor-sinatra.rb +3 -5
  400. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +23 -38
  401. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +57 -96
  402. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +22 -35
  403. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +41 -69
  404. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +6 -10
  405. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +24 -41
  406. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +29 -50
  407. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +55 -88
  408. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +159 -262
  409. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +21 -29
  410. data/plugins/rigor-statesman/lib/rigor-statesman.rb +3 -5
  411. data/plugins/rigor-typescript-utility-types/lib/rigor/plugin/typescript_utility_types.rb +23 -37
  412. data/plugins/rigor-typescript-utility-types/lib/rigor-typescript-utility-types.rb +4 -6
  413. data/sig/rigor/scope.rbs +6 -0
  414. metadata +2 -1
@@ -1207,15 +1207,12 @@ module Rigor
1207
1207
  build_undefined_method_diagnostic(path, call_node, receiver_type)
1208
1208
  end
1209
1209
 
1210
- # An arm that makes a sound "undefined on every arm" verdict
1211
- # impossible: a non-class surface (Dynamic / Top / Bot), a singleton
1212
- # (slice 1 reasons about instance arms only), the generic metaclass
1213
- # `Class` / `Module` (a value typed as one is *some* class/module object
1214
- # whose singleton methods cannot be enumerated from the metaclass e.g.
1215
- # `plugin_class : Class` really holds a `Plugin` subclass with
1216
- # `.manifest`), an unbounded receiver (ADR-26 open class or a synthesized
1217
- # stub), or a module mixin whose Object-inherited methods the per-arm
1218
- # lookup would miss.
1210
+ # An arm that makes a sound "undefined on every arm" verdict impossible: a non-class surface
1211
+ # (Dynamic / Top / Bot), a singleton (slice 1 reasons about instance arms only), the generic
1212
+ # metaclass `Class` / `Module` (a value typed as one is *some* class/module object whose singleton
1213
+ # methods cannot be enumerated from the metaclass e.g. `plugin_class : Class` really holds a
1214
+ # `Plugin` subclass with `.manifest`), an unbounded receiver (ADR-26 open class or a synthesized
1215
+ # stub), or a module mixin whose Object-inherited methods the per-arm lookup would miss.
1219
1216
  METACLASS_ARMS = %w[Class Module].to_set.freeze
1220
1217
  private_constant :METACLASS_ARMS
1221
1218
 
@@ -1229,15 +1226,11 @@ module Rigor
1229
1226
  module_mixin_receiver?(member, scope)
1230
1227
  end
1231
1228
 
1232
- # Slice 7 phase 19 — PHPStan-style `dump_type(value)`.
1233
- # When the engine recognises a call to `dump_type` (with
1234
- # any of the supported receiver shapes implicit self
1235
- # after `include Rigor::Testing`, `Rigor::Testing.dump_type`,
1236
- # or `Rigor.dump_type`), it emits an `:info` diagnostic
1237
- # showing the inferred type of the argument expression.
1238
- # The diagnostic does NOT count toward `Result#error_count`
1239
- # so a fixture peppered with `dump_type` calls still
1240
- # passes `rigor check`.
1229
+ # Slice 7 phase 19 — PHPStan-style `dump_type(value)`. When the engine recognises a call to
1230
+ # `dump_type` (with any of the supported receiver shapes implicit self after `include
1231
+ # Rigor::Testing`, `Rigor::Testing.dump_type`, or `Rigor.dump_type`), it emits an `:info` diagnostic
1232
+ # showing the inferred type of the argument expression. The diagnostic does NOT count toward
1233
+ # `Result#error_count` so a fixture peppered with `dump_type` calls still passes `rigor check`.
1241
1234
  def dump_type_diagnostic(path, call_node, scope_index)
1242
1235
  return nil unless rigor_testing_call?(call_node, :dump_type)
1243
1236
  return nil if call_node.arguments.nil? || call_node.arguments.arguments.empty?
@@ -1257,14 +1250,11 @@ module Rigor
1257
1250
  )
1258
1251
  end
1259
1252
 
1260
- # Slice 7 phase 19 — PHPStan-style `assert_type("...", value)`.
1261
- # The first argument MUST be a string literal containing
1262
- # the expected `Type#describe(:short)` rendering. When
1263
- # the inferred type's short description does not equal
1264
- # the expected literal, an `:error`-severity diagnostic
1265
- # is emitted; matching calls produce no output. This
1266
- # lets a fixture document its expected types inline:
1267
- # subsequent `rigor check` runs flag any drift.
1253
+ # Slice 7 phase 19 — PHPStan-style `assert_type("...", value)`. The first argument MUST be a
1254
+ # string literal containing the expected `Type#describe(:short)` rendering. When the inferred
1255
+ # type's short description does not equal the expected literal, an `:error`-severity diagnostic is
1256
+ # emitted; matching calls produce no output. This lets a fixture document its expected types
1257
+ # inline: subsequent `rigor check` runs flag any drift.
1268
1258
  def assert_type_diagnostic(path, call_node, scope_index)
1269
1259
  return nil unless rigor_testing_call?(call_node, :assert_type)
1270
1260
  return nil if call_node.arguments.nil? || call_node.arguments.arguments.size < 2
@@ -1289,21 +1279,16 @@ module Rigor
1289
1279
  # `Testing.dump_type(x)`
1290
1280
  # `Rigor.dump_type(x)`
1291
1281
  # `Rigor::Testing.dump_type(x)`
1292
- # The receiver check is purely structural — we do not
1293
- # consult RBS because the helpers are no-op stubs the
1294
- # user MAY shadow with their own definition; a name
1295
- # clash is the deliberate trade-off for ergonomic
1296
- # invocation.
1282
+ # The receiver check is purely structural — we do not consult RBS — because the helpers are no-op
1283
+ # stubs the user MAY shadow with their own definition; a name clash is the deliberate trade-off for
1284
+ # ergonomic invocation.
1297
1285
  RIGOR_TESTING_RECEIVERS = ["Rigor", "Rigor::Testing", "Testing"].freeze
1298
1286
  private_constant :RIGOR_TESTING_RECEIVERS
1299
1287
 
1300
- # The dump/assert helpers' own implementation methods
1301
- # call back into `Testing.dump_type` / `assert_type` to
1302
- # share the no-op runtime stub. We do NOT want those
1303
- # internal calls to surface diagnostics they are
1304
- # reflexive plumbing, not user assertions. This filter
1305
- # skips diagnostics when the call site's `self_type` is
1306
- # the `Rigor` or `Rigor::Testing` module itself.
1288
+ # The dump/assert helpers' own implementation methods call back into `Testing.dump_type` /
1289
+ # `assert_type` to share the no-op runtime stub. We do NOT want those internal calls to surface
1290
+ # diagnostics they are reflexive plumbing, not user assertions. This filter skips diagnostics
1291
+ # when the call site's `self_type` is the `Rigor` or `Rigor::Testing` module itself.
1307
1292
  SELF_REFERENTIAL_SCOPES = ["Rigor", "Rigor::Testing"].freeze
1308
1293
  private_constant :SELF_REFERENTIAL_SCOPES
1309
1294
 
@@ -1348,20 +1333,17 @@ module Rigor
1348
1333
  )
1349
1334
  end
1350
1335
 
1351
- # Diagnoses calls that the analyzer can prove will always
1352
- # raise. Today the only triggering shape is integer
1353
- # division/modulo by a literal zero divisor:
1336
+ # Diagnoses calls that the analyzer can prove will always raise. Today the only triggering shape is
1337
+ # integer division/modulo by a literal zero divisor:
1354
1338
  #
1355
1339
  # 5 / 0 # => ZeroDivisionError
1356
1340
  # x.modulo(0) # => ZeroDivisionError when x: Integer
1357
1341
  # xs.size % 0 # same — non_negative_int / Constant[0]
1358
1342
  #
1359
- # Float divmod by zero returns Infinity/NaN at runtime, so
1360
- # the rule restricts to Integer-rooted receivers (`Constant`,
1361
- # `IntegerRange`, `Nominal[Integer]`). The argument MUST be a
1362
- # `Constant<Integer>` whose value is exactly zero a
1363
- # `Union[Constant[0], Constant[2]]` divisor "may" raise,
1364
- # which we surface separately (future slice).
1343
+ # Float divmod by zero returns Infinity/NaN at runtime, so the rule restricts to Integer-rooted
1344
+ # receivers (`Constant`, `IntegerRange`, `Nominal[Integer]`). The argument MUST be a
1345
+ # `Constant<Integer>` whose value is exactly zero — a `Union[Constant[0], Constant[2]]` divisor
1346
+ # "may" raise, which we surface separately (future slice).
1365
1347
  INTEGER_RAISING_OPERATORS = %i[/ % div modulo divmod].freeze
1366
1348
  private_constant :INTEGER_RAISING_OPERATORS
1367
1349
 
@@ -1414,41 +1396,27 @@ module Rigor
1414
1396
  )
1415
1397
  end
1416
1398
 
1417
- # v0.1.2 — `flow.unreachable-branch`. Fires when an
1418
- # `IfNode` / `UnlessNode` whose predicate is a literal
1419
- # `true` / `false` / `nil` (or a literal numeric /
1420
- # string / symbol whose Ruby truthiness is known
1421
- # at-a-glance) has an observable dead branch. The
1422
- # diagnostic points at the dead branch (not the
1423
- # predicate) so the squiggle lands on the code that
1424
- # never runs.
1399
+ # v0.1.2 — `flow.unreachable-branch`. Fires when an `IfNode` / `UnlessNode` whose predicate is a
1400
+ # literal `true` / `false` / `nil` (or a literal numeric / string / symbol whose Ruby truthiness is
1401
+ # known at-a-glance) has an observable dead branch. The diagnostic points at the dead branch (not
1402
+ # the predicate) so the squiggle lands on the code that never runs.
1425
1403
  #
1426
1404
  # Conservative envelope — by deliberate v0.1.2 design:
1427
- # - Only **literal-shaped** predicates fire. Inferred-
1428
- # constant predicates (`x.method?` that happens to
1429
- # fold to `Constant<bool>`) are intentionally
1430
- # skipped Rigor's loop / mutation / RBS-strictness
1431
- # modelling is incomplete enough that an inferred
1432
- # constant can be a false positive (e.g. accumulator
1433
- # `arr << x` doesn't widen the carrier; defensive
1434
- # `module.name.nil?` checks against anonymous-class
1435
- # nil that the RBS `Module#name -> String` sig hides).
1436
- # The literal-only envelope captures the clear
1437
- # "user wrote `if false`" case without false alarms.
1438
- # - Empty dead branches (e.g. `if false; end` with no
1439
- # body) are skipped — there is no useful location to
1440
- # point at.
1441
- # - Postfix-`if` / `unless` modifiers with a literal
1442
- # predicate ARE flagged (`expr if false` body never
1443
- # runs, exactly like the block form).
1444
- # - Elsif chains (`subsequent` is itself an `IfNode`)
1445
- # ARE flagged — the entire downstream chain is
1446
- # unreachable when the outer predicate is a constant
1447
- # literal.
1405
+ # - Only **literal-shaped** predicates fire. Inferred-constant predicates (`x.method?` that happens
1406
+ # to fold to `Constant<bool>`) are intentionally skipped — Rigor's loop / mutation /
1407
+ # RBS-strictness modelling is incomplete enough that an inferred constant can be a false positive
1408
+ # (e.g. accumulator `arr << x` doesn't widen the carrier; defensive `module.name.nil?` checks
1409
+ # against anonymous-class nil that the RBS `Module#name -> String` sig hides). The literal-only
1410
+ # envelope captures the clear "user wrote `if false`" case without false alarms.
1411
+ # - Empty dead branches (e.g. `if false; end` with no body) are skipped — there is no useful
1412
+ # location to point at.
1413
+ # - Postfix-`if` / `unless` modifiers with a literal predicate ARE flagged (`expr if false` body
1414
+ # never runs, exactly like the block form).
1415
+ # - Elsif chains (`subsequent` is itself an `IfNode`) ARE flagged the entire downstream chain is
1416
+ # unreachable when the outer predicate is a constant literal.
1448
1417
  #
1449
- # Broadening to inferred-constant predicates is queued
1450
- # for a later v0.1.x release once the loop / mutation
1451
- # gaps named above are closed.
1418
+ # Broadening to inferred-constant predicates is queued for a later v0.1.x release once the loop /
1419
+ # mutation gaps named above are closed.
1452
1420
  def unreachable_branch_diagnostic(path, node, scope_index)
1453
1421
  scope = scope_index[node]
1454
1422
  return nil if scope.nil?
@@ -1462,12 +1430,9 @@ module Rigor
1462
1430
  build_unreachable_branch_diagnostic(path, dead_branch, polarity)
1463
1431
  end
1464
1432
 
1465
- # Returns `:truthy` / `:falsey` for a syntactically-
1466
- # literal predicate, or nil for anything else.
1467
- # `TrueNode`, `FalseNode`, `NilNode` are the
1468
- # unambiguous cases. Numeric / string / symbol literals
1469
- # are always truthy in Ruby (any non-`false` / non-`nil`
1470
- # value is truthy, including `0` and `""`).
1433
+ # Returns `:truthy` / `:falsey` for a syntactically-literal predicate, or nil for anything else.
1434
+ # `TrueNode`, `FalseNode`, `NilNode` are the unambiguous cases. Numeric / string / symbol literals
1435
+ # are always truthy in Ruby (any non-`false` / non-`nil` value is truthy, including `0` and `""`).
1471
1436
  TRUTHY_LITERAL_NODES = [
1472
1437
  Prism::TrueNode, Prism::IntegerNode, Prism::FloatNode,
1473
1438
  Prism::StringNode, Prism::SymbolNode, Prism::RegularExpressionNode
@@ -1484,26 +1449,19 @@ module Rigor
1484
1449
  nil
1485
1450
  end
1486
1451
 
1487
- # v0.1.2 — `def.method-visibility-mismatch`. Fires when
1488
- # an explicit-receiver `Prism::CallNode` targets a
1489
- # user-class method whose `discovered_method_visibilities`
1490
- # entry is `:private`. The rule is intentionally narrow:
1452
+ # v0.1.2 — `def.method-visibility-mismatch`. Fires when an explicit-receiver `Prism::CallNode`
1453
+ # targets a user-class method whose `discovered_method_visibilities` entry is `:private`. The rule
1454
+ # is intentionally narrow:
1491
1455
  #
1492
- # - Only `:private`. `:protected` access depends on
1493
- # subclass tracking the engine does not yet model;
1494
- # broadening waits for that surface.
1495
- # - Only user classes whose visibility table the indexer
1496
- # built. RBS-known classes (stdlib, gems) are NOT
1497
- # consulted yet RBS visibility is reliable but
1498
- # surfacing it would broaden the rule to a level the
1499
- # per-rule false-positive triage hasn't covered.
1500
- # - Implicit-self calls are skipped (always allowed for
1501
- # private). Calls whose receiver is `Prism::SelfNode`
1502
- # are also skipped — Ruby 2.7+ permits `self.foo` for
1503
- # private methods.
1504
- # - Receiver MUST resolve to a `Type::Nominal` so the
1505
- # rule has a single class identity to query. Unions /
1506
- # Dynamic / shape carriers are skipped.
1456
+ # - Only `:private`. `:protected` access depends on subclass tracking the engine does not yet
1457
+ # model; broadening waits for that surface.
1458
+ # - Only user classes whose visibility table the indexer built. RBS-known classes (stdlib, gems)
1459
+ # are NOT consulted yet RBS visibility is reliable but surfacing it would broaden the rule to a
1460
+ # level the per-rule false-positive triage hasn't covered.
1461
+ # - Implicit-self calls are skipped (always allowed for private). Calls whose receiver is
1462
+ # `Prism::SelfNode` are also skipped Ruby 2.7+ permits `self.foo` for private methods.
1463
+ # - Receiver MUST resolve to a `Type::Nominal` so the rule has a single class identity to query.
1464
+ # Unions / Dynamic / shape carriers are skipped.
1507
1465
  def visibility_mismatch_diagnostic(path, call_node, scope_index)
1508
1466
  return nil unless explicit_non_self_receiver?(call_node.receiver)
1509
1467
 
@@ -1538,20 +1496,15 @@ module Rigor
1538
1496
  )
1539
1497
  end
1540
1498
 
1541
- # Pulls a single concrete class name from an ivar write's
1542
- # rvalue type. Returns nil when the type is too unstable
1543
- # to compare (Union / Dynamic / IntegerRange / etc.).
1544
- # `concrete_class_name` already covers Nominal / Singleton
1545
- # / Constant / Tuple / HashShape; the wrapper exists so
1546
- # the ivar rule can extend the envelope (or apply
1547
- # different filters) without disturbing the call rules.
1499
+ # Pulls a single concrete class name from an ivar write's rvalue type. Returns nil when the type is
1500
+ # too unstable to compare (Union / Dynamic / IntegerRange / etc.). `concrete_class_name` already
1501
+ # covers Nominal / Singleton / Constant / Tuple / HashShape; the wrapper exists so the ivar rule can
1502
+ # extend the envelope (or apply different filters) without disturbing the call rules.
1548
1503
  #
1549
- # `TrueClass` / `FalseClass` are both normalised to
1550
- # `"bool"` here so the common boolean-flag idiom
1551
- # (`@loaded = false` in `initialize` then `@loaded = true`
1552
- # on first work) doesn't fire the mismatch rule. A real
1553
- # `bool → String` drift still trips because the second
1554
- # write's `ivar_class_for` returns `"String"`.
1504
+ # `TrueClass` / `FalseClass` are both normalised to `"bool"` here so the common boolean-flag idiom
1505
+ # (`@loaded = false` in `initialize` then `@loaded = true` on first work) doesn't fire the mismatch
1506
+ # rule. A real `bool String` drift still trips because the second write's `ivar_class_for` returns
1507
+ # `"String"`.
1555
1508
  def ivar_class_for(type)
1556
1509
  name = concrete_class_name(type)
1557
1510
  return "bool" if %w[TrueClass FalseClass].include?(name)
@@ -1615,8 +1568,8 @@ module Rigor
1615
1568
  )
1616
1569
  end
1617
1570
 
1618
- # Returns the dead-branch node for a literal-predicate
1619
- # if/unless, or nil when no observable branch is dead.
1571
+ # Returns the dead-branch node for a literal-predicate if/unless, or nil when no observable branch
1572
+ # is dead.
1620
1573
  def unreachable_branch_for(node, truthy)
1621
1574
  dead =
1622
1575
  case node
@@ -2136,30 +2089,22 @@ module Rigor
2136
2089
  )
2137
2090
  end
2138
2091
 
2139
- # ADR-8 § "`def.return-type-mismatch` rule" — flags a
2140
- # `def m(...) ... end` whose body's last expression's
2141
- # type cannot satisfy the RBS-declared return type.
2142
- # Conservative envelope (v0.1.x first cut):
2092
+ # ADR-8 § "`def.return-type-mismatch` rule" — flags a `def m(...) ... end` whose body's last
2093
+ # expression's type cannot satisfy the RBS-declared return type. Conservative envelope (v0.1.x
2094
+ # first cut):
2143
2095
  #
2144
- # - Skips methods without an RBS declaration. The rule
2145
- # has no contract to compare against for source-only
2146
- # methods.
2147
- # - Skips methods whose enclosing class isn't a
2148
- # `Type::Singleton` self_type that we can name (top-
2149
- # level / module-level methods land outside the rule).
2150
- # - Skips methods whose body's last expression is
2151
- # absent or types as `Dynamic[top]` (the analyzer's
2152
- # fail-soft fallback) — emitting on `Dynamic[top]`
2153
- # would be noise.
2154
- # - Compares the inferred body type against the
2155
- # declared return via `accepts?`:
2096
+ # - Skips methods without an RBS declaration. The rule has no contract to compare against for
2097
+ # source-only methods.
2098
+ # - Skips methods whose enclosing class isn't a `Type::Singleton` self_type that we can name
2099
+ # (top-level / module-level methods land outside the rule).
2100
+ # - Skips methods whose body's last expression is absent or types as `Dynamic[top]` (the
2101
+ # analyzer's fail-soft fallback) emitting on `Dynamic[top]` would be noise.
2102
+ # - Compares the inferred body type against the declared return via `accepts?`:
2156
2103
  # :yes → silent
2157
- # :no → emit at :error (severity_profile may
2158
- # re-stamp; default `balanced` keeps the
2104
+ # :no → emit at :error (severity_profile may re-stamp; default `balanced` keeps the
2159
2105
  # authored severity).
2160
- # :maybe → emit at :warning. Promoted to :error
2161
- # under `severity_profile: strict` per
2162
- # ADR-8 § "Severity profile".
2106
+ # :maybe → emit at :warning. Promoted to :error under `severity_profile: strict` per ADR-8 §
2107
+ # "Severity profile".
2163
2108
  def return_type_mismatch_diagnostic(path, def_node, scope_index)
2164
2109
  return nil if def_node.body.nil?
2165
2110
 
@@ -2181,9 +2126,8 @@ module Rigor
2181
2126
  build_return_type_mismatch_diagnostic(path, def_node, declared, inferred, severity)
2182
2127
  end
2183
2128
 
2184
- # The body of a `def` is the last `Prism::StatementsNode`
2185
- # child (or a single expression for one-liner defs).
2186
- # Take the last statement; that's the implicit return.
2129
+ # The body of a `def` is the last `Prism::StatementsNode` child (or a single expression for
2130
+ # one-liner defs). Take the last statement; that's the implicit return.
2187
2131
  def body_last_expression(body)
2188
2132
  case body
2189
2133
  when Prism::StatementsNode then body.body.last
@@ -2192,28 +2136,18 @@ module Rigor
2192
2136
  end
2193
2137
  end
2194
2138
 
2195
- # Pulls the declared RBS return type for the def. The
2196
- # enclosing class name comes from the def's scope's
2197
- # `self_type`; the method name is on the def itself.
2198
- # `def self.foo` is a singleton method dispatched
2199
- # through `Reflection.singleton_method_definition`;
2200
- # plain `def foo` uses `instance_method_definition`.
2201
- # Method overloads contribute their union of declared
2202
- # return types (any one of them satisfying the body
2203
- # silences the rule).
2139
+ # Pulls the declared RBS return type for the def. The enclosing class name comes from the def's
2140
+ # scope's `self_type`; the method name is on the def itself. `def self.foo` is a singleton method
2141
+ # dispatched through `Reflection.singleton_method_definition`; plain `def foo` uses
2142
+ # `instance_method_definition`. Method overloads contribute their union of declared return types
2143
+ # (any one of them satisfying the body silences the rule).
2204
2144
  #
2205
- # v0.1.2 — when the RBS sig carries a
2206
- # `%a{rigor:v1:return: <refinement>}` annotation
2207
- # (recognised by `RbsExtended.read_return_type_override`),
2208
- # the refinement carrier replaces the RBS-declared
2209
- # return for this rule. Annotation-driven refinements
2210
- # `non-empty-string`, `positive-int`, `non-empty-
2211
- # array[Integer]`, etc. — are stricter than the
2212
- # underlying RBS class, so a body whose inferred type
2213
- # the bare RBS sig would accept may still fail the
2214
- # refinement (e.g. `def name; ""; end` returns
2215
- # `Constant[""]`, accepted by `String` but rejected by
2216
- # `non-empty-string`).
2145
+ # v0.1.2 — when the RBS sig carries a `%a{rigor:v1:return: <refinement>}` annotation (recognised by
2146
+ # `RbsExtended.read_return_type_override`), the refinement carrier replaces the RBS-declared return
2147
+ # for this rule. Annotation-driven refinements — `non-empty-string`, `positive-int`,
2148
+ # `non-empty-array[Integer]`, etc. are stricter than the underlying RBS class, so a body whose
2149
+ # inferred type the bare RBS sig would accept may still fail the refinement (e.g. `def name; "";
2150
+ # end` returns `Constant[""]`, accepted by `String` but rejected by `non-empty-string`).
2217
2151
  def declared_return_type(def_node, scope_index)
2218
2152
  scope = scope_index[def_node]
2219
2153
  return nil if scope.nil?
@@ -2253,16 +2187,12 @@ module Rigor
2253
2187
  type.is_a?(Type::Dynamic) || (type.respond_to?(:top?) && type.top?.yes?)
2254
2188
  end
2255
2189
 
2256
- # Returns the severity to emit at, or nil to stay
2257
- # silent. The first-cut implementation only fires on
2258
- # proven (`:no`) mismatches; `:maybe` is treated as
2259
- # silent until the analyzer's narrowing becomes precise
2260
- # enough to avoid noise on common patterns (`{}`
2261
- # declared `Hash[K, V]`, `Set.new` → declared
2262
- # `Set[Symbol]`, …). ADR-8's promise to emit on
2263
- # `:maybe` under `severity_profile: strict` is
2264
- # deferred to a follow-up that lands together with the
2265
- # narrowing precision improvements.
2190
+ # Returns the severity to emit at, or nil to stay silent. The first-cut implementation only fires
2191
+ # on proven (`:no`) mismatches; `:maybe` is treated as silent until the analyzer's narrowing
2192
+ # becomes precise enough to avoid noise on common patterns (`{}` declared `Hash[K, V]`,
2193
+ # `Set.new` declared `Set[Symbol]`, …). ADR-8's promise to emit on `:maybe` under
2194
+ # `severity_profile: strict` is deferred to a follow-up that lands together with the narrowing
2195
+ # precision improvements.
2266
2196
  def compare_return(declared, inferred)
2267
2197
  result = declared.accepts(inferred)
2268
2198
  return :error if result.no?
@@ -2282,22 +2212,17 @@ module Rigor
2282
2212
  )
2283
2213
  end
2284
2214
 
2285
- # ADR-35 slice 1 — `def.override-visibility-reduced`. The
2286
- # Liskov signature rule for visibility: an instance-method
2287
- # override MUST NOT reduce the visibility it inherits
2288
- # (public protected/private, or protected → private),
2289
- # because a caller holding the supertype that invokes the
2290
- # method breaks when handed the subtype.
2215
+ # ADR-35 slice 1 — `def.override-visibility-reduced`. The Liskov signature rule for visibility: an
2216
+ # instance-method override MUST NOT reduce the visibility it inherits (public → protected/private,
2217
+ # or protected private), because a caller holding the supertype that invokes the method breaks
2218
+ # when handed the subtype.
2291
2219
  #
2292
- # Slice-1 scope (ADR-35 WD1, visibility carve-out): both the
2293
- # override and the shadowed method must have a STATICALLY
2294
- # OBSERVABLE visibility. The override's visibility is read
2295
- # from the source-discovered table; the parent is resolved
2296
- # against the project-discovered ancestor chain (user-source
2297
- # classes / modules only — RBS-known ancestors, whose
2298
- # accessibility RBS models as public/private only, are a
2299
- # deferred follow-on). When either side is not observable
2300
- # the rule stays silent.
2220
+ # Slice-1 scope (ADR-35 WD1, visibility carve-out): both the override and the shadowed method must
2221
+ # have a STATICALLY OBSERVABLE visibility. The override's visibility is read from the
2222
+ # source-discovered table; the parent is resolved against the project-discovered ancestor chain
2223
+ # (user-source classes / modules only — RBS-known ancestors, whose accessibility RBS models as
2224
+ # public/private only, are a deferred follow-on). When either side is not observable the rule stays
2225
+ # silent.
2301
2226
  def override_visibility_diagnostic(path, def_node, scope_index)
2302
2227
  return nil unless def_node.receiver.nil? # instance methods only
2303
2228
 
@@ -2317,8 +2242,8 @@ module Rigor
2317
2242
  return nil if parent.nil?
2318
2243
 
2319
2244
  parent_class, parent_visibility = parent
2320
- # Unknown ancestor visibility (e.g. the defining file was not
2321
- # in the analyzed set) → cannot prove a reduction, stay silent.
2245
+ # Unknown ancestor visibility (e.g. the defining file was not in the analyzed set) → cannot
2246
+ # prove a reduction, stay silent.
2322
2247
  return nil if parent_visibility.nil?
2323
2248
  return nil unless visibility_reduced?(parent_visibility, override_visibility)
2324
2249
 
@@ -2327,9 +2252,8 @@ module Rigor
2327
2252
  )
2328
2253
  end
2329
2254
 
2330
- # Returns true when `override_visibility` is strictly more
2331
- # restrictive than `parent_visibility` under the
2332
- # public > protected > private ordering.
2255
+ # Returns true when `override_visibility` is strictly more restrictive than `parent_visibility`
2256
+ # under the public > protected > private ordering.
2333
2257
  def visibility_reduced?(parent_visibility, override_visibility)
2334
2258
  parent_rank = VISIBILITY_RANK[parent_visibility]
2335
2259
  override_rank = VISIBILITY_RANK[override_visibility]
@@ -2338,14 +2262,11 @@ module Rigor
2338
2262
  override_rank < parent_rank
2339
2263
  end
2340
2264
 
2341
- # Breadth-first walk of the project-discovered ancestor chain
2342
- # (included / prepended modules first, then the superclass
2343
- # Ruby's MRO ordering), yielding each resolved ancestor class
2344
- # name nearest-first. Returns the first truthy value the block
2345
- # produces, or nil. Cross-file: the chain is followed through
2346
- # the scope tables the runner seeds from the project pre-pass
2347
- # (ADR-24 WD1). Cycle-guarded and node-count-capped. Mirrors
2348
- # `ExpressionTyper#resolve_user_def_through_ancestors`.
2265
+ # Breadth-first walk of the project-discovered ancestor chain (included / prepended modules first,
2266
+ # then the superclass Ruby's MRO ordering), yielding each resolved ancestor class name
2267
+ # nearest-first. Returns the first truthy value the block produces, or nil. Cross-file: the chain
2268
+ # is followed through the scope tables the runner seeds from the project pre-pass (ADR-24 WD1).
2269
+ # Cycle-guarded and node-count-capped. Mirrors `ExpressionTyper#resolve_user_def_through_ancestors`.
2349
2270
  def each_project_ancestor(scope, class_name)
2350
2271
  queue = ancestor_class_names(scope, class_name)
2351
2272
  seen = { class_name.to_s => true }
@@ -2366,25 +2287,22 @@ module Rigor
2366
2287
  nil
2367
2288
  end
2368
2289
 
2369
- # `[defining_class, visibility]` for the nearest user-source
2370
- # ancestor that defines an instance method `method_name`, or nil.
2290
+ # `[defining_class, visibility]` for the nearest user-source ancestor that defines an instance
2291
+ # method `method_name`, or nil.
2371
2292
  def nearest_ancestor_visibility(scope, class_name, method_name)
2372
2293
  each_project_ancestor(scope, class_name) do |ancestor|
2373
- # Stop at the nearest ancestor that DEFINES the method; its
2374
- # visibility may be nil (unknown) the caller treats unknown
2375
- # as "cannot prove a reduction" and stays silent. Never
2376
- # fabricate `:public` from a missing entry (that produced a
2377
- # large false-positive cluster on cross-file Rails concerns).
2294
+ # Stop at the nearest ancestor that DEFINES the method; its visibility may be nil (unknown) —
2295
+ # the caller treats unknown as "cannot prove a reduction" and stays silent. Never fabricate
2296
+ # `:public` from a missing entry (that produced a large false-positive cluster on cross-file
2297
+ # Rails concerns).
2378
2298
  [ancestor, scope.discovered_method_visibility(ancestor, method_name)] if scope.user_def_for(ancestor,
2379
2299
  method_name)
2380
2300
  end
2381
2301
  end
2382
2302
 
2383
- # Direct ancestors of `class_name` as project-discovered,
2384
- # qualified names: included / prepended modules first, then
2385
- # the superclass. As-written names are resolved against the
2386
- # subclass's lexical nesting; names that resolve to no
2387
- # project class/module (RBS-known / third-party) are dropped.
2303
+ # Direct ancestors of `class_name` as project-discovered, qualified names: included / prepended
2304
+ # modules first, then the superclass. As-written names are resolved against the subclass's lexical
2305
+ # nesting; names that resolve to no project class/module (RBS-known / third-party) are dropped.
2388
2306
  def ancestor_class_names(scope, class_name)
2389
2307
  names = []
2390
2308
  scope.includes_of(class_name).each do |raw|
@@ -2435,33 +2353,26 @@ module Rigor
2435
2353
  )
2436
2354
  end
2437
2355
 
2438
- # ADR-35 slice 2 — `def.override-return-widened`. The Liskov
2439
- # signature rule for returns (covariance): an override may
2440
- # *narrow* the return it inherits (return a more specific type)
2441
- # but MUST NOT *widen* it. A caller holding the supertype uses
2442
- # the result as the parent's return type; a wider override
2443
- # return breaks that use.
2356
+ # ADR-35 slice 2 — `def.override-return-widened`. The Liskov signature rule for returns
2357
+ # (covariance): an override may *narrow* the return it inherits (return a more specific type) but
2358
+ # MUST NOT *widen* it. A caller holding the supertype uses the result as the parent's return type;
2359
+ # a wider override return breaks that use.
2444
2360
  #
2445
- # WD1 gate (proper, type-direction): both the override and the
2446
- # shadowed ancestor method must carry an explicitly-authored
2447
- # RBS signature. The override side is gated by
2448
- # `defined_on?` (the RBS method is declared on the overriding
2449
- # class itself, not merely inherited); the parent side is the
2450
- # nearest project-discovered ancestor whose RBS declares the
2451
- # method. Inference-only either side → silent.
2361
+ # WD1 gate (proper, type-direction): both the override and the shadowed ancestor method must carry
2362
+ # an explicitly-authored RBS signature. The override side is gated by `defined_on?` (the RBS
2363
+ # method is declared on the overriding class itself, not merely inherited); the parent side is the
2364
+ # nearest project-discovered ancestor whose RBS declares the method. Inference-only either side
2365
+ # silent.
2452
2366
  #
2453
- # Fires only on a proven (`:no`) widening; generic / `untyped`
2454
- # / `self` parent returns degrade to `Dynamic[Top]` and accept
2455
- # everything, so they stay silent (FP-safe). `self`/`instance`
2456
- # are translated with `self_type: nil` on both sides, so a
2457
- # parent `-> self` and an override `-> self` never fire.
2458
- # The authored-override resolution shared by the Liskov override
2459
- # rules (`def.override-return-widened` and
2460
- # `def.override-param-narrowed`): the def must be an instance method
2461
- # whose own class declares it in RBS, and a project-discovered
2462
- # ancestor must also declare it. Returns
2463
- # `[scope, override_method, parent_class, parent_method]`, or nil
2464
- # (the rule does not fire) when any gate is unmet.
2367
+ # Fires only on a proven (`:no`) widening; generic / `untyped` / `self` parent returns degrade to
2368
+ # `Dynamic[Top]` and accept everything, so they stay silent (FP-safe). `self`/`instance` are
2369
+ # translated with `self_type: nil` on both sides, so a parent `-> self` and an override `-> self`
2370
+ # never fire.
2371
+ # The authored-override resolution shared by the Liskov override rules
2372
+ # (`def.override-return-widened` and `def.override-param-narrowed`): the def must be an instance
2373
+ # method whose own class declares it in RBS, and a project-discovered ancestor must also declare
2374
+ # it. Returns `[scope, override_method, parent_class, parent_method]`, or nil (the rule does not
2375
+ # fire) when any gate is unmet.
2465
2376
  def resolve_authored_override(def_node, scope_index)
2466
2377
  return nil unless def_node.receiver.nil? # instance methods only (singleton: follow-on)
2467
2378
 
@@ -2502,9 +2413,8 @@ module Rigor
2502
2413
  )
2503
2414
  end
2504
2415
 
2505
- # `[defining_class, RBS::Definition::Method]` for the nearest
2506
- # project-discovered ancestor whose RBS declares `method_name`
2507
- # (not the starting class's own declaration), or nil.
2416
+ # `[defining_class, RBS::Definition::Method]` for the nearest project-discovered ancestor whose
2417
+ # RBS declares `method_name` (not the starting class's own declaration), or nil.
2508
2418
  def nearest_ancestor_method_def(scope, class_name, method_name)
2509
2419
  each_project_ancestor(scope, class_name) do |ancestor|
2510
2420
  method_def = safe_instance_method_definition(ancestor, method_name, scope)
@@ -2518,8 +2428,8 @@ module Rigor
2518
2428
  nil
2519
2429
  end
2520
2430
 
2521
- # True when `method_def`'s RBS declaration lives on `class_name`
2522
- # itself (rather than being inherited from an ancestor).
2431
+ # True when `method_def`'s RBS declaration lives on `class_name` itself (rather than being
2432
+ # inherited from an ancestor).
2523
2433
  def defined_on?(method_def, class_name)
2524
2434
  defined_in = method_def.defined_in
2525
2435
  return false if defined_in.nil?
@@ -2544,23 +2454,17 @@ module Rigor
2544
2454
  )
2545
2455
  end
2546
2456
 
2547
- # ADR-35 slice 3 — `def.override-param-narrowed`. The Liskov
2548
- # signature rule for parameters (contravariance): an override
2549
- # may *widen* a parameter (accept a supertype accepting more
2550
- # is safe) but MUST NOT *narrow* it. A caller holding the
2551
- # supertype passes a parent-typed argument; a narrowed override
2552
- # parameter cannot accept it.
2457
+ # ADR-35 slice 3 — `def.override-param-narrowed`. The Liskov signature rule for parameters
2458
+ # (contravariance): an override may *widen* a parameter (accept a supertype — accepting more is
2459
+ # safe) but MUST NOT *narrow* it. A caller holding the supertype passes a parent-typed argument; a
2460
+ # narrowed override parameter cannot accept it.
2553
2461
  #
2554
- # Direction (ADR-35 WD3, corrected): fire on
2555
- # `override_param.accepts(parent_param) == :no` the override's
2556
- # (narrowed) slot cannot accept the wider parent argument type.
2557
- # WD4: type comparison at matching POSITIONAL parameter indices
2558
- # only; arity / keyword-requiredness divergence is out of scope
2559
- # for v1. Same WD1 both-sides-authored gate as slice 2;
2560
- # `untyped` / unbound-generic / interface parent params degrade
2561
- # to `Dynamic[Top]` and are skipped (FP-safe). To avoid
2562
- # overload-arm ambiguity, both sides must have exactly one
2563
- # method type.
2462
+ # Direction (ADR-35 WD3, corrected): fire on `override_param.accepts(parent_param) == :no` — the
2463
+ # override's (narrowed) slot cannot accept the wider parent argument type. WD4: type comparison at
2464
+ # matching POSITIONAL parameter indices only; arity / keyword-requiredness divergence is out of
2465
+ # scope for v1. Same WD1 both-sides-authored gate as slice 2; `untyped` / unbound-generic /
2466
+ # interface parent params degrade to `Dynamic[Top]` and are skipped (FP-safe). To avoid
2467
+ # overload-arm ambiguity, both sides must have exactly one method type.
2564
2468
  def override_param_narrowed_diagnostic(path, def_node, scope_index)
2565
2469
  resolved = resolve_authored_override(def_node, scope_index)
2566
2470
  return nil if resolved.nil?
@@ -2578,13 +2482,11 @@ module Rigor
2578
2482
  )
2579
2483
  end
2580
2484
 
2581
- # Translated positional (required + optional) parameter types of
2582
- # a method's single method type, or nil when the method is
2583
- # overloaded (multiple method types arm mapping is ambiguous)
2584
- # or the parameter list is not introspectable. Per-position
2585
- # translation failures yield `nil` at that slot (skipped by the
2586
- # comparison). `self`/`instance` translate with `self_type: nil`
2587
- # (→ `Dynamic[Top]`), matching the return-side handling.
2485
+ # Translated positional (required + optional) parameter types of a method's single method type, or
2486
+ # nil when the method is overloaded (multiple method types arm mapping is ambiguous) or the
2487
+ # parameter list is not introspectable. Per-position translation failures yield `nil` at that slot
2488
+ # (skipped by the comparison). `self`/`instance` translate with `self_type: nil` (→
2489
+ # `Dynamic[Top]`), matching the return-side handling.
2588
2490
  def positional_param_types(method_def)
2589
2491
  method_types = method_def.method_types
2590
2492
  return nil unless method_types.size == 1
@@ -2601,13 +2503,11 @@ module Rigor
2601
2503
  end
2602
2504
  end
2603
2505
 
2604
- # Index of the first positional parameter the override narrows
2605
- # relative to the parent, or nil. A position is a violation when
2606
- # the override's slot cannot accept the parent's argument type
2607
- # (`override_param.accepts(parent_param) == :no`). Positions
2608
- # where either side is missing/untranslatable, or the parent
2609
- # type degraded to `Dynamic[Top]` (untyped / unbound generic /
2610
- # interface), are skipped.
2506
+ # Index of the first positional parameter the override narrows relative to the parent, or nil. A
2507
+ # position is a violation when the override's slot cannot accept the parent's argument type
2508
+ # (`override_param.accepts(parent_param) == :no`). Positions where either side is
2509
+ # missing/untranslatable, or the parent type degraded to `Dynamic[Top]` (untyped / unbound generic
2510
+ # / interface), are skipped.
2611
2511
  def first_narrowed_param_index(override_params, parent_params)
2612
2512
  count = [override_params.size, parent_params.size].min
2613
2513
  count.times do |i|