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
@@ -4,8 +4,7 @@ require "rigor/plugin"
4
4
 
5
5
  module Rigor
6
6
  module Plugin
7
- # ADR-16 Tier A worked plugin: recognises Sinatra's class-
8
- # level route DSL.
7
+ # ADR-16 Tier A worked plugin: recognises Sinatra's class-level route DSL.
9
8
  #
10
9
  # Sinatra's modular style:
11
10
  #
@@ -20,52 +19,35 @@ module Rigor
20
19
  # end
21
20
  # end
22
21
  #
23
- # At runtime `Sinatra::Base#generate_method`
24
- # (`lib/sinatra/base.rb:1788-1793`) does `define_method(name,
25
- # &block); remove_method`, turning each block into a real
26
- # instance method of the user's app class. The substrate's
27
- # Tier A hook (`Rigor::Inference::MacroBlockSelfType`)
28
- # replays the same contract statically: the block runs with
29
- # `self : Nominal[MyApp]`, so bare identifiers (`params`,
30
- # `redirect`, `halt`, `session`, `headers`, `content_type`,
31
- # `body`, `status`, `erb`, …) resolve through
32
- # `Sinatra::Base`'s RBS via rigor's normal inference path.
22
+ # At runtime `Sinatra::Base#generate_method` (`lib/sinatra/base.rb:1788-1793`) does
23
+ # `define_method(name, &block); remove_method`, turning each block into a real instance method of the
24
+ # user's app class. The substrate's Tier A hook (`Rigor::Inference::MacroBlockSelfType`) replays the
25
+ # same contract statically: the block runs with `self : Nominal[MyApp]`, so bare identifiers
26
+ # (`params`, `redirect`, `halt`, `session`, `headers`, `content_type`, `body`, `status`, `erb`, …)
27
+ # resolve through `Sinatra::Base`'s RBS via rigor's normal inference path.
33
28
  #
34
29
  # ## Reach
35
30
  #
36
- # All nine class-level HTTP verb methods Sinatra exposes:
37
- # `get`, `post`, `put`, `delete`, `head`, `options`, `patch`,
38
- # `link`, `unlink` (`lib/sinatra/base.rb:1531-1553`). Both
39
- # modular-style subclasses of `Sinatra::Base` and `Sinatra::Application`
40
- # (classic top-level style, when used via `class App <
41
- # Sinatra::Application`) match because the receiver
42
- # constraint accepts every subclass.
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
33
+ # of `Sinatra::Base` and `Sinatra::Application` (classic top-level style, when used via `class App <
34
+ # Sinatra::Application`) match because the receiver constraint accepts every subclass.
43
35
  #
44
36
  # ## What the plugin does NOT do (yet)
45
37
  #
46
- # - **Routing diagnostics.** Path uniqueness, conflict
47
- # detection, named-route reverse lookup none of these
48
- # are in slice 1c's scope.
49
- # - **Custom helpers.** `helpers do ... end` blocks that
50
- # inject module methods into the app's instance namespace
51
- # are Tier C / Tier B work, not Tier A.
52
- # - **Configure / settings.** `configure do ... end` and
53
- # `set :session_secret, "..."` are settings DSL, not
54
- # route DSL handled by separate substrate entries when
55
- # demand surfaces.
56
- # - **Classic-style top-level routes.** A bare
57
- # `get '/path' do ... end` at the top of a script (no
58
- # enclosing `class < Sinatra::Base`) is the classic-mode
59
- # pattern (`lib/sinatra/main.rb`). Tier A as currently
60
- # wired requires the receiver's class to be visible at
61
- # the call site; a top-level call's receiver is the
62
- # classic-mode `Sinatra::Application`, which the
63
- # `Sinatra::Delegator` mixin forwards from `main`. The
64
- # classic style is deferred until the demand justifies
65
- # the extra match shape.
38
+ # - **Routing diagnostics.** Path uniqueness, conflict detection, named-route reverse lookup — none of
39
+ # these are in slice 1c's scope.
40
+ # - **Custom helpers.** `helpers do ... end` blocks that inject module methods into the app's instance
41
+ # namespace are Tier C / Tier B work, not Tier A.
42
+ # - **Configure / settings.** `configure do ... end` and `set :session_secret, "..."` are settings
43
+ # DSL, not route DSL handled by separate substrate entries when demand surfaces.
44
+ # - **Classic-style top-level routes.** A bare `get '/path' do ... end` at the top of a script (no
45
+ # enclosing `class < Sinatra::Base`) is the classic-mode pattern (`lib/sinatra/main.rb`). Tier A as
46
+ # currently wired requires the receiver's class to be visible at the call site; a top-level call's
47
+ # receiver is the classic-mode `Sinatra::Application`, which the `Sinatra::Delegator` mixin forwards
48
+ # from `main`. The classic style is deferred until the demand justifies the extra match shape.
66
49
  #
67
- # See `plugins/rigor-sinatra/README.md` for usage and the
68
- # demo script under `plugins/rigor-sinatra/demo/`.
50
+ # See `plugins/rigor-sinatra/README.md` for usage and the demo script under `plugins/rigor-sinatra/demo/`.
69
51
  class Sinatra < Rigor::Plugin::Base
70
52
  manifest(
71
53
  id: "sinatra",
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Gem entry point. Required by Rigor's plugin loader when
4
- # `.rigor.yml` lists `rigor-sinatra` under `plugins:`. The
5
- # loader expects this `require` to side-effect a call to
6
- # `Rigor::Plugin.register`, which the body of
7
- # `lib/rigor/plugin/sinatra.rb` performs at load time.
3
+ # Gem entry point. Required by Rigor's plugin loader when `.rigor.yml` lists `rigor-sinatra` under
4
+ # `plugins:`. The loader expects this `require` to side-effect a call to `Rigor::Plugin.register`, which
5
+ # the body of `lib/rigor/plugin/sinatra.rb` performs at load time.
8
6
  require_relative "rigor/plugin/sinatra"
@@ -7,11 +7,9 @@ require_relative "type_translator"
7
7
  module Rigor
8
8
  module Plugin
9
9
  class Sorbet < Rigor::Plugin::Base
10
- # Slice 6 of ADR-11 — recognises `T.absurd(x)` calls and
11
- # composes them with the engine's flow-sensitive
12
- # narrowing. `T.absurd` asserts that a code branch is
13
- # statically unreachable; it's the standard Sorbet idiom
14
- # for case/when exhaustiveness:
10
+ # Slice 6 of ADR-11 — recognises `T.absurd(x)` calls and composes them with the engine's
11
+ # flow-sensitive narrowing. `T.absurd` asserts that a code branch is statically unreachable; it's
12
+ # the standard Sorbet idiom for case/when exhaustiveness:
15
13
  #
16
14
  # case x
17
15
  # when A then ...
@@ -20,33 +18,23 @@ module Rigor
20
18
  # T.absurd(x)
21
19
  # end
22
20
  #
23
- # If every case has been handled, `x` at the `else` branch
24
- # has been narrowed to `T.noreturn` (Rigor's `Type::Bot`)
25
- # and the assertion holds. If the user forgot a case, `x`
26
- # narrows to whatever's left and the assertion is wrong —
27
- # we surface that mistake as `plugin.sorbet.absurd-reachable`.
21
+ # If every case has been handled, `x` at the `else` branch has been narrowed to `T.noreturn`
22
+ # (Rigor's `Type::Bot`) and the assertion holds. If the user forgot a case, `x` narrows to whatever's
23
+ # left and the assertion is wrong we surface that mistake as `plugin.sorbet.absurd-reachable`.
28
24
  #
29
25
  # ## Two-phase mechanism
30
26
  #
31
- # The recogniser is invoked from the plugin's `dynamic_return` rule
32
- # where the per-node `scope:` carries the proper narrowing
33
- # context. The rule:
27
+ # The recogniser is invoked from the plugin's `dynamic_return` rule where the per-node `scope:`
28
+ # carries the proper narrowing context. The rule:
34
29
  #
35
- # - Contributes a `bot` return type regardless of
36
- # reachability (faithful to `T.absurd`'s runtime
37
- # behaviour: it always raises). This lets the engine's
38
- # existing flow analysis treat code after `T.absurd` as
39
- # unreachable, matching what users of Sorbet expect.
40
- # - When the branch is REACHABLE (the discriminant's type
41
- # isn't `bot`), the recogniser also records the call
42
- # node in a per-plugin set. The plugin's
43
- # `diagnostics_for_file` later walks the AST for
44
- # `T.absurd` calls and emits a
45
- # `plugin.sorbet.absurd-reachable` warning at every
46
- # call_node whose object identity matches the recorded
47
- # set. We rely on the runner only parsing each file
48
- # once per run, so the same Prism node object is seen
49
- # in both the `dynamic_return` rule and
30
+ # - Contributes a `bot` return type regardless of reachability (faithful to `T.absurd`'s runtime
31
+ # behaviour: it always raises). This lets the engine's existing flow analysis treat code after
32
+ # `T.absurd` as unreachable, matching what users of Sorbet expect.
33
+ # - When the branch is REACHABLE (the discriminant's type isn't `bot`), the recogniser also records
34
+ # the call node in a per-plugin set. The plugin's `diagnostics_for_file` later walks the AST for
35
+ # `T.absurd` calls and emits a `plugin.sorbet.absurd-reachable` warning at every call_node whose
36
+ # object identity matches the recorded set. We rely on the runner only parsing each file once per
37
+ # run, so the same Prism node object is seen in both the `dynamic_return` rule and
50
38
  # `diagnostics_for_file`.
51
39
  module AbsurdRecognizer
52
40
  # @param call_node [Prism::CallNode]
@@ -56,19 +44,17 @@ module Rigor
56
44
  return false unless call_node.name == :absurd
57
45
  return false unless TypeTranslator.sorbet_t_namespaced?(call_node.receiver)
58
46
 
59
- # Slice 6 only handles single-argument `T.absurd(x)`;
60
- # no-arg / multi-arg shapes are syntax errors at
61
- # Sorbet's level too.
47
+ # Slice 6 only handles single-argument `T.absurd(x)`; no-arg / multi-arg shapes are syntax
48
+ # errors at Sorbet's level too.
62
49
  arguments = call_node.arguments&.arguments
63
50
  arguments&.size == 1
64
51
  end
65
52
 
66
53
  # @param call_node [Prism::CallNode]
67
54
  # @param scope [Rigor::Scope, nil]
68
- # @return [Boolean] true when the discriminant has been
69
- # narrowed to `bot` (the branch is unreachable, so
70
- # `T.absurd` is correct). The caller suppresses the
71
- # `absurd-reachable` diagnostic in this case.
55
+ # @return [Boolean] true when the discriminant has been narrowed to `bot` (the branch is
56
+ # unreachable, so `T.absurd` is correct). The caller suppresses the `absurd-reachable` diagnostic
57
+ # in this case.
72
58
  def self.exhaustive?(call_node, scope)
73
59
  return false if scope.nil?
74
60
 
@@ -76,9 +62,8 @@ module Rigor
76
62
  arg_type = scope.type_of(arg)
77
63
  arg_type.equal?(Rigor::Type::Bot.instance) || arg_type.is_a?(Rigor::Type::Bot)
78
64
  rescue StandardError
79
- # On synthetic / unrecognised nodes the typer may
80
- # raise; treat as "can't prove unreachable" so the
81
- # diagnostic fires conservatively.
65
+ # On synthetic / unrecognised nodes the typer may raise; treat as "can't prove unreachable" so
66
+ # the diagnostic fires conservatively.
82
67
  false
83
68
  end
84
69
  end
@@ -7,13 +7,10 @@ require_relative "type_translator"
7
7
  module Rigor
8
8
  module Plugin
9
9
  class Sorbet < Rigor::Plugin::Base
10
- # Lifts Sorbet's type-assertion calls (`T.let`, `T.cast`,
11
- # `T.must`, `T.must_because`, `T.unsafe`, `T.reveal_type`)
12
- # into `FlowContribution` return-type contributions.
13
- # ADR-11 slice 2 covered the original four; the
14
- # `must_because` / `reveal_type` light follow-up extends
15
- # the same module rather than splitting into a parallel
16
- # recogniser.
10
+ # Lifts Sorbet's type-assertion calls (`T.let`, `T.cast`, `T.must`, `T.must_because`, `T.unsafe`,
11
+ # `T.reveal_type`) into `FlowContribution` return-type contributions. ADR-11 slice 2 covered the
12
+ # original four; the `must_because` / `reveal_type` light follow-up extends the same module rather
13
+ # than splitting into a parallel recogniser.
17
14
  #
18
15
  # | Sorbet form | Contribution |
19
16
  # | ---------------------------- | --------------------------------------- |
@@ -26,54 +23,39 @@ module Rigor
26
23
  # | `T.assert_type!(expr, T)` | return type ← translated `T` + static subtype check |
27
24
  # | `T.bind(self, T)` | return type ← `Constant[nil]` + post_return_fact narrowing self to translated `T` |
28
25
  #
29
- # The Sorbet runtime's `T.let` / `T.cast` actually return
30
- # the inner expression unchanged at runtime; their job is
31
- # purely to *assert* a static type. From Rigor's static
32
- # perspective the simplest faithful translation is "the
33
- # call's return type IS the asserted type" — the call
34
- # site's downstream uses see that type. This matches what
35
- # `%a{rigor:v1:assert: x is T}` would do for an assignment
36
- # in the surrounding scope.
26
+ # The Sorbet runtime's `T.let` / `T.cast` actually return the inner expression unchanged at runtime;
27
+ # their job is purely to *assert* a static type. From Rigor's static perspective the simplest
28
+ # faithful translation is "the call's return type IS the asserted type" — the call site's downstream
29
+ # uses see that type. This matches what `%a{rigor:v1:assert: x is T}` would do for an assignment in
30
+ # the surrounding scope.
37
31
  #
38
- # `T.must_because` is `T.must` with a second-argument
39
- # string explanation. The static behaviour is identical to
40
- # `T.must` — strip `nil` from the inferred type — so the
41
- # recogniser dispatches through the same path.
32
+ # `T.must_because` is `T.must` with a second-argument string explanation. The static behaviour is
33
+ # identical to `T.must` strip `nil` from the inferred type — so the recogniser dispatches through
34
+ # the same path.
42
35
  #
43
- # `T.reveal_type` is "diagnostic-only" in Sorbet: it
44
- # passes the value through unchanged at runtime AND
45
- # surfaces the inferred static type as a build-time
46
- # message. The recogniser contributes the inferred type
47
- # (so chained call sites still resolve as if the
48
- # `T.reveal_type` wrapper weren't there); the plugin's
49
- # `diagnostics_for_file` hook surfaces the
50
- # `plugin.sorbet.reveal-type` `:info` message for human
51
- # consumption.
36
+ # `T.reveal_type` is "diagnostic-only" in Sorbet: it passes the value through unchanged at runtime
37
+ # AND surfaces the inferred static type as a build-time message. The recogniser contributes the
38
+ # inferred type (so chained call sites still resolve as if the `T.reveal_type` wrapper weren't
39
+ # there); the plugin's `diagnostics_for_file` hook surfaces the `plugin.sorbet.reveal-type` `:info`
40
+ # message for human consumption.
52
41
  #
53
- # `T.bind(self, T)` is recognised as block-scope self
54
- # narrowing. The recogniser returns a contribution whose
55
- # `post_return_facts` carries a `Fact(target_kind: :self)`
56
- # so the engine's `apply_self_post_return_fact` narrows
57
- # `scope.self_type` for the surrounding scope (in a block
58
- # body, the rest of the block). The first argument MUST be
59
- # a literal `Prism::SelfNode` — Sorbet rejects other
60
- # receivers and the recogniser mirrors that. The runtime
61
- # call returns nil, so the static return type is
62
- # `Constant[nil]`.
42
+ # `T.bind(self, T)` is recognised as block-scope self narrowing. The recogniser returns a
43
+ # contribution whose `post_return_facts` carries a `Fact(target_kind: :self)` so the engine's
44
+ # `apply_self_post_return_fact` narrows `scope.self_type` for the surrounding scope (in a block
45
+ # body, the rest of the block). The first argument MUST be a literal `Prism::SelfNode` — Sorbet
46
+ # rejects other receivers and the recogniser mirrors that. The runtime call returns nil, so the
47
+ # static return type is `Constant[nil]`.
63
48
  module AssertionRecognizer
64
- # Method names this recogniser claims as Sorbet
65
- # assertions. The plugin checks call sites against this
66
- # set before any catalog lookup so a `T.let` call
67
- # inside an analysed file always resolves through this
68
- # module.
49
+ # Method names this recogniser claims as Sorbet assertions. The plugin checks call sites against
50
+ # this set before any catalog lookup so a `T.let` call inside an analysed file always resolves
51
+ # through this module.
69
52
  SORBET_ASSERTIONS = %i[let cast must must_because unsafe reveal_type assert_type! bind].freeze
70
53
 
71
54
  module_function
72
55
 
73
56
  # @param call_node [Prism::CallNode]
74
57
  # @param scope [Rigor::Scope]
75
- # @param plugin_id [String] used for the contribution's
76
- # `provenance.source_family`.
58
+ # @param plugin_id [String] used for the contribution's `provenance.source_family`.
77
59
  # @return [Rigor::FlowContribution, nil]
78
60
  def recognize(call_node:, scope:, plugin_id:)
79
61
  return nil unless TypeTranslator.sorbet_t_namespaced?(call_node.receiver)
@@ -97,20 +79,15 @@ module Rigor
97
79
  end
98
80
  end
99
81
 
100
- # `T.bind(self, T)` recognition. Sorbet rejects any
101
- # non-`self` receiver argument, and the recogniser
102
- # mirrors that — calls like `T.bind(other, X)` fall
103
- # through silently. The contribution carries:
82
+ # `T.bind(self, T)` recognition. Sorbet rejects any non-`self` receiver argument, and the
83
+ # recogniser mirrors that — calls like `T.bind(other, X)` fall through silently. The contribution
84
+ # carries:
104
85
  #
105
- # - `return_type: Constant[nil]` — Sorbet's runtime
106
- # `T.bind` returns nil; chained calls would be a
86
+ # - `return_type: Constant[nil]` — Sorbet's runtime `T.bind` returns nil; chained calls would be a
107
87
  # bug, but the typing stays accurate.
108
- # - `post_return_facts: [Fact(target_kind: :self,
109
- # type: T)]` the engine's
110
- # `apply_self_post_return_fact` narrows
111
- # `scope.self_type` for the surrounding scope. In a
112
- # block body, that scope is the block's own, so the
113
- # narrowing applies to the rest of the block —
88
+ # - `post_return_facts: [Fact(target_kind: :self, type: T)]` — the engine's
89
+ # `apply_self_post_return_fact` narrows `scope.self_type` for the surrounding scope. In a block
90
+ # body, that scope is the block's own, so the narrowing applies to the rest of the block —
114
91
  # matching Sorbet's documented contract.
115
92
  def recognize_bind(call_node, plugin_id)
116
93
  first_arg = nth_argument(call_node, 0)
@@ -137,15 +114,11 @@ module Rigor
137
114
  )
138
115
  end
139
116
 
140
- # `T.assert_type!(expr, T)` shares the typed-assertion
141
- # contribution shape with `T.cast` (return is the
142
- # asserted type), so the recogniser delegates the
143
- # return-type half through `resolve_typed_assertion`.
144
- # The static subtype check that distinguishes
145
- # `assert_type!` from `cast` lives in the plugin's
146
- # `diagnostics_for_file` hook (mirroring the
147
- # absurd-recognizer pattern: record the call here, emit
148
- # the diagnostic from the per-file walker).
117
+ # `T.assert_type!(expr, T)` shares the typed-assertion contribution shape with `T.cast` (return is
118
+ # the asserted type), so the recogniser delegates the return-type half through
119
+ # `resolve_typed_assertion`. The static subtype check that distinguishes `assert_type!` from
120
+ # `cast` lives in the plugin's `diagnostics_for_file` hook (mirroring the absurd-recognizer
121
+ # pattern: record the call here, emit the diagnostic from the per-file walker).
149
122
  def assert_type_check(call_node, scope)
150
123
  return nil if scope.nil?
151
124
 
@@ -162,14 +135,11 @@ module Rigor
162
135
  nil
163
136
  end
164
137
 
165
- # `T.reveal_type(expr)` returns `expr` unchanged at
166
- # runtime; the Sorbet-side semantics is "make the
167
- # inferred static type visible to the user." The
168
- # contribution mirrors `T.must` minus the nil-stripping:
169
- # the call's return type is the inner expression's
170
- # inferred type. The companion diagnostic is emitted by
171
- # the plugin's `diagnostics_for_file` hook; this
172
- # method handles the contribution only.
138
+ # `T.reveal_type(expr)` returns `expr` unchanged at runtime; the Sorbet-side semantics is "make
139
+ # the inferred static type visible to the user." The contribution mirrors `T.must` minus the
140
+ # nil-stripping: the call's return type is the inner expression's inferred type. The companion
141
+ # diagnostic is emitted by the plugin's `diagnostics_for_file` hook; this method handles the
142
+ # contribution only.
173
143
  def resolve_reveal_type(call_node, scope)
174
144
  inner = nth_argument(call_node, 0)
175
145
  return Rigor::Type::Combinator.untyped if inner.nil? || scope.nil?
@@ -177,17 +147,14 @@ module Rigor
177
147
  inner_type = scope.type_of(inner)
178
148
  inner_type || Rigor::Type::Combinator.untyped
179
149
  rescue StandardError
180
- # Synthetic / virtual nodes can raise from
181
- # `scope.type_of`; degrade gracefully so the dispatcher
150
+ # Synthetic / virtual nodes can raise from `scope.type_of`; degrade gracefully so the dispatcher
182
151
  # can still proceed with a benign untyped envelope.
183
152
  Rigor::Type::Combinator.untyped
184
153
  end
185
154
 
186
- # `T.let(expr, T)` and `T.cast(expr, T)` share the same
187
- # 2-argument shape: `arguments[1]` is the type
188
- # expression. The first argument is opaque to slice 2
189
- # we don't try to verify it at runtime; that's `srb tc`'s
190
- # job and is out of scope per ADR-11.
155
+ # `T.let(expr, T)` and `T.cast(expr, T)` share the same 2-argument shape: `arguments[1]` is the
156
+ # type expression. The first argument is opaque to slice 2 — we don't try to verify it at runtime;
157
+ # that's `srb tc`'s job and is out of scope per ADR-11.
191
158
  def resolve_typed_assertion(call_node)
192
159
  type_arg = nth_argument(call_node, 1)
193
160
  return nil if type_arg.nil?
@@ -195,12 +162,9 @@ module Rigor
195
162
  TypeTranslator.translate(type_arg)
196
163
  end
197
164
 
198
- # `T.must(expr)` strips `nil` from `expr`'s inferred
199
- # type. The call's target type is therefore
200
- # `inferred(expr) - Constant[nil]`. Falls back to the
201
- # untyped envelope when the inferred shape is itself
202
- # `Dynamic[top]` or when no scope is available
203
- # (synthetic / virtual-node call sites).
165
+ # `T.must(expr)` strips `nil` from `expr`'s inferred type. The call's target type is therefore
166
+ # `inferred(expr) - Constant[nil]`. Falls back to the untyped envelope when the inferred shape is
167
+ # itself `Dynamic[top]` or when no scope is available (synthetic / virtual-node call sites).
204
168
  def resolve_must(call_node, scope)
205
169
  inner = nth_argument(call_node, 0)
206
170
  return nil if inner.nil? || scope.nil?
@@ -210,18 +174,15 @@ module Rigor
210
174
 
211
175
  strip_nil(inner_type)
212
176
  rescue StandardError
213
- # `scope.type_of` may raise on synthetic nodes; degrade
214
- # to "no contribution" rather than crash the dispatcher.
177
+ # `scope.type_of` may raise on synthetic nodes; degrade to "no contribution" rather than crash
178
+ # the dispatcher.
215
179
  nil
216
180
  end
217
181
 
218
- # Removes `nil` (`Constant[nil]`) from `type` using the
219
- # `Difference` carrier. Idempotent on shapes that don't
220
- # contain nil the resulting `Difference[base, removed]`
221
- # collapses to `base` if `base` already excludes the
222
- # removed value, but the simple form here is good enough
223
- # for slice 2; the precise normalisation lands when the
224
- # Difference carrier gets full algebraic support.
182
+ # Removes `nil` (`Constant[nil]`) from `type` using the `Difference` carrier. Idempotent on shapes
183
+ # that don't contain nil — the resulting `Difference[base, removed]` collapses to `base` if `base`
184
+ # already excludes the removed value, but the simple form here is good enough for slice 2; the
185
+ # precise normalisation lands when the Difference carrier gets full algebraic support.
225
186
  def strip_nil(type)
226
187
  Rigor::Type::Combinator.difference(
227
188
  type, Rigor::Type::Combinator.constant_of(nil)
@@ -3,28 +3,22 @@
3
3
  module Rigor
4
4
  module Plugin
5
5
  class Sorbet < Rigor::Plugin::Base
6
- # Per-run table of method signatures keyed by the
7
- # `(class_name, method_name, kind)` triple. Built by
8
- # {CatalogWalker} during the plugin's lazy pre-walk; read
9
- # by the plugin's `dynamic_return` rule at every gated call site.
6
+ # Per-run table of method signatures keyed by the `(class_name, method_name, kind)` triple. Built by
7
+ # {CatalogWalker} during the plugin's lazy pre-walk; read by the plugin's `dynamic_return` rule at
8
+ # every gated call site.
10
9
  #
11
- # The catalog is mutable while it is being built, then
12
- # frozen via {#freeze!} before the first read. Construction
13
- # mutability is intentionalslice 1 builds the catalog
14
- # incrementally as the walker visits each project file —
15
- # but consumers MUST treat the catalog as read-only.
10
+ # The catalog is mutable while it is being built, then frozen via {#freeze!} before the first read.
11
+ # Construction mutability is intentional — slice 1 builds the catalog incrementally as the walker
12
+ # visits each project file but consumers MUST treat the catalog as read-only.
16
13
  class Catalog
17
- # Frozen empty bucket reused for classes that have no
18
- # recorded mixins. Avoids allocating a fresh Hash on
19
- # every `mixins_for` query.
14
+ # Frozen empty bucket reused for classes that have no recorded mixins. Avoids allocating a fresh
15
+ # Hash on every `mixins_for` query.
20
16
  EMPTY_MIXINS = { include: [].freeze, extend: [].freeze }.freeze
21
17
 
22
18
  def initialize
23
19
  @entries = {}
24
- # ADR-11 slice 8 — per-class mixin declarations
25
- # collected by `CatalogWalker`. Lookup-time chain
26
- # traversal lifts sigs declared on a mixed-in
27
- # module to call sites on the host class.
20
+ # ADR-11 slice 8 — per-class mixin declarations collected by `CatalogWalker`. Lookup-time chain
21
+ # traversal lifts sigs declared on a mixed-in module to call sites on the host class.
28
22
  @mixins = {}
29
23
  @frozen_after_build = false
30
24
  end
@@ -37,13 +31,11 @@ module Rigor
37
31
  @entries[key] = signature
38
32
  end
39
33
 
40
- # @param class_name [String] the class / module that
41
- # carries the mixin (`class Post; include Foo; end`
42
- # records under `"Post"`).
34
+ # @param class_name [String] the class / module that carries the mixin (`class Post; include Foo;
35
+ # end` records under `"Post"`).
43
36
  # @param kind [:include, :extend]
44
- # @param module_name [String] the textual name of the
45
- # mixed-in module as it appeared at the include /
46
- # extend site (`"Foo"`, `"Foo::Bar"`, `"::Foo"`).
37
+ # @param module_name [String] the textual name of the mixed-in module as it appeared at the
38
+ # include / extend site (`"Foo"`, `"Foo::Bar"`, `"::Foo"`).
47
39
  def record_mixin(class_name:, kind:, module_name:)
48
40
  raise "Catalog already finalised" if @frozen_after_build
49
41
 
@@ -69,9 +61,8 @@ module Rigor
69
61
  end
70
62
 
71
63
  # @param class_name [String]
72
- # @return [Hash{Symbol => Array<String>}] frozen mapping
73
- # `{ include: [...], extend: [...] }`. Returns
74
- # {EMPTY_MIXINS} when no mixins were recorded.
64
+ # @return [Hash{Symbol => Array<String>}] frozen mapping `{ include: [...], extend: [...] }`.
65
+ # Returns {EMPTY_MIXINS} when no mixins were recorded.
75
66
  def mixins_for(class_name)
76
67
  @mixins[class_name] || EMPTY_MIXINS
77
68
  end
@@ -80,16 +71,12 @@ module Rigor
80
71
  @entries.empty?
81
72
  end
82
73
 
83
- # ADR-52 slice 4 — the distinct method names the catalog
84
- # carries at least one signature for, across every
85
- # `(class_name, kind)` owner. Feeds the plugin's run-time
86
- # `dynamic_return methods:` name gate: the engine only
87
- # consults the plugin for a call whose name appears here
88
- # (or in the static assertion vocabulary), and the
89
- # precise `(class, kind)` lookup stays in the rule block.
90
- # Computed fresh per call — the plugin memoises the
91
- # resolved set, and `freeze!` freezes the catalog itself
92
- # so a lazy memo ivar here would raise.
74
+ # ADR-52 slice 4 — the distinct method names the catalog carries at least one signature for,
75
+ # across every `(class_name, kind)` owner. Feeds the plugin's run-time `dynamic_return methods:`
76
+ # name gate: the engine only consults the plugin for a call whose name appears here (or in the
77
+ # static assertion vocabulary), and the precise `(class, kind)` lookup stays in the rule block.
78
+ # Computed fresh per call — the plugin memoises the resolved set, and `freeze!` freezes the
79
+ # catalog itself so a lazy memo ivar here would raise.
93
80
  #
94
81
  # @return [Array<Symbol>]
95
82
  def method_names