rigortype 0.2.7 → 0.2.9

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 (433) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/data/core_overlay/csv.rbs +28 -0
  4. data/data/core_overlay/psych.rbs +22 -0
  5. data/docs/handbook/01-getting-started.md +9 -1
  6. data/docs/handbook/02-everyday-types.md +4 -1
  7. data/docs/handbook/08-understanding-errors.md +3 -3
  8. data/docs/manual/01-installation.md +1 -0
  9. data/docs/manual/02-cli-reference.md +16 -7
  10. data/docs/manual/04-diagnostics.md +7 -4
  11. data/docs/manual/06-baseline.md +5 -2
  12. data/docs/manual/07-plugins.md +1 -1
  13. data/docs/manual/14-rails-quickstart.md +4 -2
  14. data/docs/manual/15-type-protection-coverage.md +27 -4
  15. data/docs/manual/plugins/rigor-actionpack.md +1 -1
  16. data/docs/manual/plugins/rigor-activerecord.md +12 -5
  17. data/docs/manual/plugins/rigor-rails-routes.md +11 -0
  18. data/lib/rigor/analysis/baseline.rb +56 -96
  19. data/lib/rigor/analysis/buffer_binding.rb +13 -20
  20. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +29 -48
  21. data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +35 -54
  22. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +23 -37
  23. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +16 -23
  24. data/lib/rigor/analysis/check_rules/rule_walk.rb +48 -71
  25. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +19 -28
  26. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +48 -67
  27. data/lib/rigor/analysis/check_rules.rb +174 -274
  28. data/lib/rigor/analysis/dependency_recorder.rb +26 -38
  29. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +13 -25
  30. data/lib/rigor/analysis/dependency_source_inference/builder.rb +17 -30
  31. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +15 -26
  32. data/lib/rigor/analysis/dependency_source_inference/index.rb +45 -76
  33. data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +23 -40
  34. data/lib/rigor/analysis/dependency_source_inference/walker.rb +50 -83
  35. data/lib/rigor/analysis/dependency_source_inference.rb +6 -10
  36. data/lib/rigor/analysis/diagnostic.rb +48 -75
  37. data/lib/rigor/analysis/erb_template_detector.rb +13 -18
  38. data/lib/rigor/analysis/fact_store.rb +12 -22
  39. data/lib/rigor/analysis/incremental.rb +47 -63
  40. data/lib/rigor/analysis/incremental_session.rb +74 -100
  41. data/lib/rigor/analysis/project_scan.rb +12 -23
  42. data/lib/rigor/analysis/result.rb +3 -5
  43. data/lib/rigor/analysis/rule_catalog.rb +67 -95
  44. data/lib/rigor/analysis/run_stats.rb +38 -61
  45. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +101 -160
  46. data/lib/rigor/analysis/runner/pool_coordinator.rb +121 -188
  47. data/lib/rigor/analysis/runner/project_pre_passes.rb +62 -96
  48. data/lib/rigor/analysis/runner/run_snapshots.rb +12 -18
  49. data/lib/rigor/analysis/runner.rb +220 -333
  50. data/lib/rigor/analysis/self_call_resolution_recorder.rb +30 -43
  51. data/lib/rigor/analysis/worker_session.rb +64 -102
  52. data/lib/rigor/ast/type_node.rb +6 -8
  53. data/lib/rigor/ast.rb +6 -10
  54. data/lib/rigor/bleeding_edge.rb +27 -35
  55. data/lib/rigor/builtins/hkt_builtins.rb +7 -14
  56. data/lib/rigor/builtins/imported_refinements.rb +106 -149
  57. data/lib/rigor/builtins/predefined_constant_refinements.rb +51 -65
  58. data/lib/rigor/builtins/regex_refinement.rb +32 -43
  59. data/lib/rigor/builtins/static_return_refinements.rb +57 -81
  60. data/lib/rigor/cache/descriptor.rb +58 -94
  61. data/lib/rigor/cache/incremental_snapshot.rb +32 -48
  62. data/lib/rigor/cache/rbs_cache_producer.rb +10 -15
  63. data/lib/rigor/cache/rbs_class_ancestor_table.rb +10 -16
  64. data/lib/rigor/cache/rbs_class_type_param_names.rb +10 -16
  65. data/lib/rigor/cache/rbs_constant_table.rb +8 -13
  66. data/lib/rigor/cache/rbs_descriptor.rb +8 -13
  67. data/lib/rigor/cache/rbs_environment.rb +11 -19
  68. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +11 -19
  69. data/lib/rigor/cache/rbs_known_class_names.rb +8 -12
  70. data/lib/rigor/cache/store.rb +248 -186
  71. data/lib/rigor/cli/annotate_command.rb +51 -88
  72. data/lib/rigor/cli/baseline_command.rb +10 -17
  73. data/lib/rigor/cli/check_command.rb +121 -199
  74. data/lib/rigor/cli/check_runner_factory.rb +8 -14
  75. data/lib/rigor/cli/ci_detector.rb +9 -14
  76. data/lib/rigor/cli/command.rb +8 -14
  77. data/lib/rigor/cli/coverage_command.rb +62 -48
  78. data/lib/rigor/cli/coverage_mutation.rb +14 -19
  79. data/lib/rigor/cli/coverage_report.rb +0 -2
  80. data/lib/rigor/cli/coverage_scan.rb +7 -11
  81. data/lib/rigor/cli/diagnostic_formats.rb +46 -62
  82. data/lib/rigor/cli/diff_command.rb +10 -20
  83. data/lib/rigor/cli/docs_command.rb +26 -40
  84. data/lib/rigor/cli/doctor_command.rb +8 -9
  85. data/lib/rigor/cli/explain_command.rb +4 -7
  86. data/lib/rigor/cli/fused_protection_renderer.rb +4 -5
  87. data/lib/rigor/cli/fused_protection_report.rb +6 -8
  88. data/lib/rigor/cli/lsp_command.rb +11 -19
  89. data/lib/rigor/cli/mcp_command.rb +4 -6
  90. data/lib/rigor/cli/mutation_protection_renderer.rb +3 -4
  91. data/lib/rigor/cli/mutation_protection_report.rb +6 -9
  92. data/lib/rigor/cli/options.rb +11 -19
  93. data/lib/rigor/cli/plugin_command.rb +18 -30
  94. data/lib/rigor/cli/plugins_command.rb +29 -51
  95. data/lib/rigor/cli/plugins_renderer.rb +12 -20
  96. data/lib/rigor/cli/prism_colorizer.rb +13 -19
  97. data/lib/rigor/cli/protection_fork_scan.rb +55 -0
  98. data/lib/rigor/cli/protection_renderer.rb +5 -7
  99. data/lib/rigor/cli/protection_report.rb +34 -18
  100. data/lib/rigor/cli/renderable.rb +4 -6
  101. data/lib/rigor/cli/show_bleedingedge_command.rb +5 -8
  102. data/lib/rigor/cli/sig_gen_command.rb +14 -26
  103. data/lib/rigor/cli/skill_command.rb +30 -47
  104. data/lib/rigor/cli/skill_describe.rb +40 -64
  105. data/lib/rigor/cli/trace_command.rb +9 -16
  106. data/lib/rigor/cli/trace_renderer.rb +35 -51
  107. data/lib/rigor/cli/triage_command.rb +6 -10
  108. data/lib/rigor/cli/triage_renderer.rb +1 -2
  109. data/lib/rigor/cli/type_of_command.rb +14 -23
  110. data/lib/rigor/cli/type_of_renderer.rb +3 -5
  111. data/lib/rigor/cli/type_scan_command.rb +6 -10
  112. data/lib/rigor/cli/type_scan_renderer.rb +3 -5
  113. data/lib/rigor/cli/type_scan_report.rb +2 -3
  114. data/lib/rigor/cli/upgrade_command.rb +2 -3
  115. data/lib/rigor/cli.rb +16 -28
  116. data/lib/rigor/config_audit.rb +35 -44
  117. data/lib/rigor/configuration/dependencies.rb +41 -70
  118. data/lib/rigor/configuration/severity_profile.rb +28 -42
  119. data/lib/rigor/configuration.rb +150 -241
  120. data/lib/rigor/environment/bundle_sig_discovery.rb +74 -118
  121. data/lib/rigor/environment/class_registry.rb +19 -26
  122. data/lib/rigor/environment/constant_type_cache_holder.rb +14 -21
  123. data/lib/rigor/environment/hkt_registry_holder.rb +6 -12
  124. data/lib/rigor/environment/lockfile_resolver.rb +31 -47
  125. data/lib/rigor/environment/missing_gem_constant_index.rb +128 -0
  126. data/lib/rigor/environment/rbs_collection_discovery.rb +39 -63
  127. data/lib/rigor/environment/rbs_coverage_report.rb +24 -42
  128. data/lib/rigor/environment/rbs_loader.rb +420 -518
  129. data/lib/rigor/environment/reflection.rb +28 -53
  130. data/lib/rigor/environment/reporters.rb +13 -24
  131. data/lib/rigor/environment.rb +225 -304
  132. data/lib/rigor/flow_contribution/conflict.rb +16 -27
  133. data/lib/rigor/flow_contribution/element.rb +7 -12
  134. data/lib/rigor/flow_contribution/fact.rb +33 -57
  135. data/lib/rigor/flow_contribution/merge_result.rb +6 -9
  136. data/lib/rigor/flow_contribution/merger.rb +32 -47
  137. data/lib/rigor/flow_contribution.rb +37 -55
  138. data/lib/rigor/inference/acceptance.rb +133 -219
  139. data/lib/rigor/inference/block_parameter_binder.rb +52 -82
  140. data/lib/rigor/inference/body_fixpoint.rb +22 -30
  141. data/lib/rigor/inference/budget_trace.rb +43 -63
  142. data/lib/rigor/inference/builtins/array_catalog.rb +4 -6
  143. data/lib/rigor/inference/builtins/comparable_catalog.rb +4 -6
  144. data/lib/rigor/inference/builtins/complex_catalog.rb +13 -19
  145. data/lib/rigor/inference/builtins/date_catalog.rb +38 -67
  146. data/lib/rigor/inference/builtins/encoding_catalog.rb +22 -36
  147. data/lib/rigor/inference/builtins/enumerable_catalog.rb +4 -6
  148. data/lib/rigor/inference/builtins/exception_catalog.rb +38 -57
  149. data/lib/rigor/inference/builtins/hash_catalog.rb +9 -14
  150. data/lib/rigor/inference/builtins/method_catalog.rb +29 -48
  151. data/lib/rigor/inference/builtins/numeric_catalog.rb +13 -18
  152. data/lib/rigor/inference/builtins/pathname_catalog.rb +8 -13
  153. data/lib/rigor/inference/builtins/proc_catalog.rb +38 -62
  154. data/lib/rigor/inference/builtins/random_catalog.rb +21 -32
  155. data/lib/rigor/inference/builtins/range_catalog.rb +12 -21
  156. data/lib/rigor/inference/builtins/rational_catalog.rb +9 -15
  157. data/lib/rigor/inference/builtins/re_catalog.rb +29 -48
  158. data/lib/rigor/inference/builtins/set_catalog.rb +18 -27
  159. data/lib/rigor/inference/builtins/string_catalog.rb +12 -19
  160. data/lib/rigor/inference/builtins/struct_catalog.rb +18 -30
  161. data/lib/rigor/inference/builtins/time_catalog.rb +28 -44
  162. data/lib/rigor/inference/closure_escape_analyzer.rb +44 -67
  163. data/lib/rigor/inference/coverage_scanner.rb +13 -18
  164. data/lib/rigor/inference/def_return_typer.rb +8 -14
  165. data/lib/rigor/inference/dynamic_origin.rb +23 -20
  166. data/lib/rigor/inference/expression_typer.rb +794 -1154
  167. data/lib/rigor/inference/fallback.rb +8 -12
  168. data/lib/rigor/inference/fallback_tracer.rb +4 -10
  169. data/lib/rigor/inference/flow_tracer.rb +25 -36
  170. data/lib/rigor/inference/fork_map.rb +87 -0
  171. data/lib/rigor/inference/hkt_body.rb +45 -68
  172. data/lib/rigor/inference/hkt_body_parser.rb +33 -52
  173. data/lib/rigor/inference/hkt_reducer.rb +38 -59
  174. data/lib/rigor/inference/hkt_registry.rb +50 -76
  175. data/lib/rigor/inference/indexed_narrowing.rb +55 -81
  176. data/lib/rigor/inference/macro_block_self_type.rb +21 -34
  177. data/lib/rigor/inference/method_dispatcher/array_to_h_folding.rb +11 -18
  178. data/lib/rigor/inference/method_dispatcher/block_folding.rb +49 -83
  179. data/lib/rigor/inference/method_dispatcher/call_context.rb +16 -23
  180. data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +13 -22
  181. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +307 -454
  182. data/lib/rigor/inference/method_dispatcher/data_folding.rb +24 -34
  183. data/lib/rigor/inference/method_dispatcher/file_folding.rb +22 -37
  184. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +52 -81
  185. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +51 -78
  186. data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +60 -97
  187. data/lib/rigor/inference/method_dispatcher/math_folding.rb +23 -34
  188. data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +18 -26
  189. data/lib/rigor/inference/method_dispatcher/method_folding.rb +37 -71
  190. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +114 -182
  191. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +122 -193
  192. data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +15 -24
  193. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +64 -93
  194. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +31 -45
  195. data/lib/rigor/inference/method_dispatcher/set_folding.rb +6 -9
  196. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +356 -511
  197. data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +22 -32
  198. data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +15 -21
  199. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +66 -82
  200. data/lib/rigor/inference/method_dispatcher/time_folding.rb +10 -15
  201. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +9 -13
  202. data/lib/rigor/inference/method_dispatcher.rb +378 -524
  203. data/lib/rigor/inference/method_parameter_binder.rb +81 -114
  204. data/lib/rigor/inference/multi_target_binder.rb +51 -68
  205. data/lib/rigor/inference/mutation_widening.rb +115 -158
  206. data/lib/rigor/inference/narrowing.rb +627 -727
  207. data/lib/rigor/inference/origin_lookup.rb +38 -0
  208. data/lib/rigor/inference/parameter_inference_collector.rb +154 -109
  209. data/lib/rigor/inference/precision_scanner.rb +26 -35
  210. data/lib/rigor/inference/project_patched_methods.rb +20 -32
  211. data/lib/rigor/inference/project_patched_scanner.rb +23 -37
  212. data/lib/rigor/inference/protection_scanner.rb +16 -18
  213. data/lib/rigor/inference/rbs_type_translator.rb +49 -66
  214. data/lib/rigor/inference/scope_indexer.rb +473 -821
  215. data/lib/rigor/inference/statement_evaluator.rb +754 -1114
  216. data/lib/rigor/inference/struct_fold_safety.rb +34 -48
  217. data/lib/rigor/inference/synthetic_method.rb +9 -16
  218. data/lib/rigor/inference/synthetic_method_index.rb +20 -35
  219. data/lib/rigor/inference/synthetic_method_scanner.rb +63 -103
  220. data/lib/rigor/language_server/buffer_resolution.rb +6 -9
  221. data/lib/rigor/language_server/buffer_table.rb +11 -18
  222. data/lib/rigor/language_server/completion_provider.rb +69 -116
  223. data/lib/rigor/language_server/debouncer.rb +16 -25
  224. data/lib/rigor/language_server/diagnostic_publisher.rb +26 -40
  225. data/lib/rigor/language_server/document_symbol_provider.rb +14 -19
  226. data/lib/rigor/language_server/folding_range_provider.rb +11 -19
  227. data/lib/rigor/language_server/hover_provider.rb +8 -11
  228. data/lib/rigor/language_server/hover_renderer.rb +56 -93
  229. data/lib/rigor/language_server/loop.rb +14 -19
  230. data/lib/rigor/language_server/project_context.rb +46 -74
  231. data/lib/rigor/language_server/selection_range_provider.rb +9 -12
  232. data/lib/rigor/language_server/server.rb +55 -83
  233. data/lib/rigor/language_server/signature_help_provider.rb +29 -46
  234. data/lib/rigor/language_server/synchronized_writer.rb +4 -7
  235. data/lib/rigor/language_server/uri.rb +8 -13
  236. data/lib/rigor/language_server.rb +4 -6
  237. data/lib/rigor/mcp/loop.rb +2 -3
  238. data/lib/rigor/mcp/server.rb +4 -7
  239. data/lib/rigor/mcp.rb +3 -6
  240. data/lib/rigor/plugin/access_denied_error.rb +5 -8
  241. data/lib/rigor/plugin/additional_initializer.rb +21 -31
  242. data/lib/rigor/plugin/base.rb +335 -518
  243. data/lib/rigor/plugin/blueprint.rb +14 -23
  244. data/lib/rigor/plugin/box.rb +18 -29
  245. data/lib/rigor/plugin/fact_store.rb +16 -26
  246. data/lib/rigor/plugin/inflector.rb +37 -53
  247. data/lib/rigor/plugin/io_boundary.rb +33 -56
  248. data/lib/rigor/plugin/isolation.rb +42 -55
  249. data/lib/rigor/plugin/load_error.rb +10 -15
  250. data/lib/rigor/plugin/loader.rb +30 -49
  251. data/lib/rigor/plugin/macro/block_as_method.rb +20 -32
  252. data/lib/rigor/plugin/macro/heredoc_template.rb +35 -58
  253. data/lib/rigor/plugin/macro/nested_class_template.rb +22 -36
  254. data/lib/rigor/plugin/macro/trait_registry.rb +34 -51
  255. data/lib/rigor/plugin/macro.rb +10 -15
  256. data/lib/rigor/plugin/manifest.rb +85 -144
  257. data/lib/rigor/plugin/node_context.rb +14 -22
  258. data/lib/rigor/plugin/node_rule_walk.rb +49 -74
  259. data/lib/rigor/plugin/protocol_contract.rb +25 -39
  260. data/lib/rigor/plugin/registry.rb +132 -205
  261. data/lib/rigor/plugin/services.rb +21 -33
  262. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -19
  263. data/lib/rigor/plugin/trust_policy.rb +24 -38
  264. data/lib/rigor/plugin/type_node_resolver.rb +15 -24
  265. data/lib/rigor/protection/diagnostic_oracle.rb +11 -13
  266. data/lib/rigor/protection/mutation_scanner.rb +27 -35
  267. data/lib/rigor/protection/mutator.rb +50 -70
  268. data/lib/rigor/protection/test_suite_oracle.rb +20 -27
  269. data/lib/rigor/rbs_extended/conformance_checker.rb +48 -68
  270. data/lib/rigor/rbs_extended/hkt_directives.rb +28 -54
  271. data/lib/rigor/rbs_extended/reporter.rb +24 -40
  272. data/lib/rigor/rbs_extended.rb +107 -197
  273. data/lib/rigor/reflection.rb +68 -86
  274. data/lib/rigor/scope/discovery_index.rb +14 -19
  275. data/lib/rigor/scope.rb +255 -310
  276. data/lib/rigor/sig_gen/classification.rb +6 -10
  277. data/lib/rigor/sig_gen/generator.rb +197 -323
  278. data/lib/rigor/sig_gen/layout_index.rb +12 -20
  279. data/lib/rigor/sig_gen/method_candidate.rb +12 -17
  280. data/lib/rigor/sig_gen/observation_collector.rb +38 -70
  281. data/lib/rigor/sig_gen/observed_call.rb +13 -23
  282. data/lib/rigor/sig_gen/path_mapper.rb +17 -29
  283. data/lib/rigor/sig_gen/renderer.rb +7 -13
  284. data/lib/rigor/sig_gen/type_elaborator.rb +15 -28
  285. data/lib/rigor/sig_gen/write_result.rb +8 -16
  286. data/lib/rigor/sig_gen/writer.rb +95 -174
  287. data/lib/rigor/sig_gen.rb +3 -6
  288. data/lib/rigor/signature_path_audit.rb +24 -30
  289. data/lib/rigor/source/constant_path.rb +10 -14
  290. data/lib/rigor/source/literals.rb +31 -45
  291. data/lib/rigor/source/node_locator.rb +9 -11
  292. data/lib/rigor/source/node_walker.rb +9 -13
  293. data/lib/rigor/source.rb +3 -4
  294. data/lib/rigor/testing.rb +16 -20
  295. data/lib/rigor/triage/catalogue.rb +38 -62
  296. data/lib/rigor/triage.rb +31 -52
  297. data/lib/rigor/trinary.rb +9 -13
  298. data/lib/rigor/type/acceptance_router.rb +4 -6
  299. data/lib/rigor/type/accepts_result.rb +10 -14
  300. data/lib/rigor/type/app.rb +19 -27
  301. data/lib/rigor/type/bot.rb +4 -6
  302. data/lib/rigor/type/bound_method.rb +10 -15
  303. data/lib/rigor/type/combinator.rb +165 -257
  304. data/lib/rigor/type/constant.rb +23 -34
  305. data/lib/rigor/type/data_class.rb +10 -15
  306. data/lib/rigor/type/data_instance.rb +14 -20
  307. data/lib/rigor/type/difference.rb +21 -32
  308. data/lib/rigor/type/dynamic.rb +3 -5
  309. data/lib/rigor/type/hash_shape.rb +32 -18
  310. data/lib/rigor/type/integer_range.rb +11 -16
  311. data/lib/rigor/type/intersection.rb +27 -42
  312. data/lib/rigor/type/nominal.rb +10 -15
  313. data/lib/rigor/type/plain_lattice.rb +9 -13
  314. data/lib/rigor/type/refined.rb +67 -114
  315. data/lib/rigor/type/singleton.rb +4 -6
  316. data/lib/rigor/type/struct_class.rb +11 -16
  317. data/lib/rigor/type/struct_instance.rb +15 -21
  318. data/lib/rigor/type/tuple.rb +14 -19
  319. data/lib/rigor/type/union.rb +30 -42
  320. data/lib/rigor/type_node/generic.rb +14 -26
  321. data/lib/rigor/type_node/identifier.rb +12 -19
  322. data/lib/rigor/type_node.rb +3 -12
  323. data/lib/rigor/value_semantics.rb +16 -21
  324. data/lib/rigor/version.rb +1 -1
  325. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +18 -30
  326. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +16 -29
  327. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +12 -23
  328. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +22 -39
  329. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +26 -46
  330. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +51 -87
  331. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +10 -17
  332. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +26 -39
  333. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +141 -184
  334. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +30 -51
  335. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +57 -92
  336. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +92 -108
  337. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +13 -22
  338. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +15 -25
  339. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +8 -13
  340. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +16 -24
  341. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +40 -48
  342. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +140 -129
  343. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +56 -59
  344. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +27 -41
  345. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +16 -24
  346. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +172 -0
  347. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +168 -241
  348. data/plugins/rigor-activerecord/lib/rigor-activerecord.rb +3 -5
  349. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +8 -14
  350. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +9 -15
  351. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +4 -7
  352. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +23 -41
  353. data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +3 -5
  354. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +6 -10
  355. data/plugins/rigor-activesupport-core-ext/lib/rigor-activesupport-core-ext.rb +8 -11
  356. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +32 -0
  357. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +30 -45
  358. data/plugins/rigor-devise/lib/rigor-devise.rb +3 -5
  359. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +43 -69
  360. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +26 -35
  361. data/plugins/rigor-dry-schema/lib/rigor-dry-schema.rb +3 -5
  362. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +30 -47
  363. data/plugins/rigor-dry-struct/lib/rigor-dry-struct.rb +3 -5
  364. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +67 -113
  365. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +29 -46
  366. data/plugins/rigor-dry-types/lib/rigor-dry-types.rb +3 -5
  367. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -20
  368. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +14 -23
  369. data/plugins/rigor-dry-validation/lib/rigor-dry-validation.rb +2 -3
  370. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +22 -37
  371. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +28 -46
  372. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +13 -17
  373. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +30 -47
  374. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +52 -80
  375. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +21 -31
  376. data/plugins/rigor-graphql/lib/rigor-graphql.rb +3 -5
  377. data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +11 -17
  378. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +20 -34
  379. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +45 -75
  380. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +22 -36
  381. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +22 -41
  382. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +17 -32
  383. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +6 -12
  384. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +4 -8
  385. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +17 -32
  386. data/plugins/rigor-rails/lib/rigor-rails.rb +11 -18
  387. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +58 -92
  388. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +20 -34
  389. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +20 -31
  390. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +49 -82
  391. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +55 -92
  392. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +51 -90
  393. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +17 -30
  394. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +189 -0
  395. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +38 -70
  396. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +61 -75
  397. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +319 -505
  398. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +91 -96
  399. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +32 -54
  400. data/plugins/rigor-rbs-inline/lib/rigor-rbs-inline.rb +8 -13
  401. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +9 -16
  402. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +24 -39
  403. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +13 -22
  404. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +34 -56
  405. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +22 -42
  406. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +43 -75
  407. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +27 -39
  408. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +14 -19
  409. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +20 -34
  410. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +28 -47
  411. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +21 -37
  412. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +13 -22
  413. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +18 -31
  414. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +6 -10
  415. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +14 -29
  416. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +23 -41
  417. data/plugins/rigor-sinatra/lib/rigor-sinatra.rb +3 -5
  418. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +23 -38
  419. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +57 -96
  420. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +22 -35
  421. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +41 -69
  422. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +6 -10
  423. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +24 -41
  424. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +29 -50
  425. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +55 -88
  426. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +159 -262
  427. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +21 -29
  428. data/plugins/rigor-statesman/lib/rigor-statesman.rb +3 -5
  429. data/plugins/rigor-typescript-utility-types/lib/rigor/plugin/typescript_utility_types.rb +23 -37
  430. data/plugins/rigor-typescript-utility-types/lib/rigor-typescript-utility-types.rb +4 -6
  431. data/sig/rigor/environment.rbs +1 -0
  432. data/sig/rigor/scope.rbs +6 -0
  433. metadata +9 -1
@@ -5,15 +5,12 @@ require "prism"
5
5
  module Rigor
6
6
  module Plugin
7
7
  class DryTypes < Rigor::Plugin::Base
8
- # Walks project source for `module X; include Dry.Types(); end`
9
- # shapes and emits a `{ "<X>::<Alias>" => "<UnderlyingClass>" }`
10
- # alias table covering the dry-types canonical-shortcut names.
11
- # See {DryTypes} module-docstring for the floor / ceiling
12
- # scoping.
8
+ # Walks project source for `module X; include Dry.Types(); end` shapes and emits a
9
+ # `{ "<X>::<Alias>" => "<UnderlyingClass>" }` alias table covering the dry-types canonical-shortcut
10
+ # names. See {DryTypes} module-docstring for the floor / ceiling scoping.
13
11
  module AliasScanner
14
- # The canonical-shortcut names dry-types exposes through
15
- # `include Dry.Types()`. Mirrors `Dry::Types.type_keys`
16
- # from the upstream gem.
12
+ # The canonical-shortcut names dry-types exposes through `include Dry.Types()`. Mirrors
13
+ # `Dry::Types.type_keys` from the upstream gem.
17
14
  CANONICAL_ALIASES = {
18
15
  "String" => "String",
19
16
  "Integer" => "Integer",
@@ -32,29 +29,21 @@ module Rigor
32
29
  "Any" => "Object"
33
30
  }.freeze
34
31
 
35
- # Slice 2 — nested-category aliases. dry-types installs
36
- # four parallel coercion categories: `Coercible::*`
37
- # (everything-to-target coercion), `Strict::*` (no
38
- # coercion; raise if mismatch), `Params::*` (HTTP /
39
- # query-string-style coercion, used by Hanami / Roda /
40
- # dry-web in request handling), `JSON::*` (JSON-shape
41
- # coercion). Each category exposes the same set of names
42
- # as the canonical shortcuts above, plus a few additions
43
- # that are category-specific (`Params::Nil`,
44
- # `JSON::Symbol`). For Rigor's purposes the underlying
45
- # class is the same regardless of category — coercion
46
- # semantics are a runtime concern. We register every
47
- # `<module>::<Category>::<Name>` mapping the upstream gem
48
- # publishes so call-site references work uniformly.
32
+ # Slice 2 — nested-category aliases. dry-types installs four parallel coercion categories:
33
+ # `Coercible::*` (everything-to-target coercion), `Strict::*` (no coercion; raise if mismatch),
34
+ # `Params::*` (HTTP / query-string-style coercion, used by Hanami / Roda / dry-web in request
35
+ # handling), `JSON::*` (JSON-shape coercion). Each category exposes the same set of names as the
36
+ # canonical shortcuts above, plus a few additions that are category-specific (`Params::Nil`,
37
+ # `JSON::Symbol`). For Rigor's purposes the underlying class is the same regardless of category —
38
+ # coercion semantics are a runtime concern. We register every `<module>::<Category>::<Name>`
39
+ # mapping the upstream gem publishes so call-site references work uniformly.
49
40
  NESTED_CATEGORIES = %w[Coercible Strict Params JSON].freeze
50
41
  private_constant :NESTED_CATEGORIES
51
42
 
52
43
  module_function
53
44
 
54
- # @param paths [Array<String>] absolute paths to `.rb`
55
- # files the project's `paths:` resolves to.
56
- # @return [Hash{String => String}] frozen
57
- # `{aliased_name => underlying_class_name}` map. Empty
45
+ # @param paths [Array<String>] absolute paths to `.rb` files the project's `paths:` resolves to.
46
+ # @return [Hash{String => String}] frozen `{aliased_name => underlying_class_name}` map. Empty
58
47
  # when no `include Dry.Types()` declaration is found.
59
48
  def scan(paths:)
60
49
  results = paths.flat_map { |path| scan_file(path) }
@@ -64,16 +53,16 @@ module Rigor
64
53
  base = canonical_table(modules)
65
54
  results.each do |result|
66
55
  result[:compositions].each do |const_name, underlying|
67
- # Each result's compositions are scoped under that
68
- # result's enclosing module (`Types::Email`, etc.).
56
+ # Each result's compositions are scoped under that result's enclosing module
57
+ # (`Types::Email`, etc.).
69
58
  base["#{result[:module_name]}::#{const_name}"] ||= underlying
70
59
  end
71
60
  end
72
61
  base.freeze
73
62
  end
74
63
 
75
- # Populates the canonical-shortcut + nested-category
76
- # table (15 + 15 × 4 = 75 entries per alias module).
64
+ # Populates the canonical-shortcut + nested-category table (15 + 15 × 4 = 75 entries per alias
65
+ # module).
77
66
  def canonical_table(modules)
78
67
  modules.each_with_object({}) do |module_name, acc|
79
68
  CANONICAL_ALIASES.each do |alias_name, underlying|
@@ -96,22 +85,18 @@ module Rigor
96
85
  { module_name: module_info[:module_name], compositions: compositions }
97
86
  end
98
87
  rescue StandardError
99
- # Missing-file / parse failures degrade to "no
100
- # contribution from this file"; the plugin's
101
- # user-visible surface is the published fact, and
102
- # dropping unparseable files keeps the fact stable.
88
+ # Missing-file / parse failures degrade to "no contribution from this file"; the plugin's
89
+ # user-visible surface is the published fact, and dropping unparseable files keeps the fact
90
+ # stable.
103
91
  []
104
92
  end
105
93
  private_class_method :scan_file
106
94
 
107
- # Walks a Prism AST collecting alias-module info:
108
- # `{module_name:, body:}` for every `module X; include
109
- # Dry.Types(); end` shape. Tracks the enclosing module
110
- # chain so a nested `module App; module Types; include
111
- # Dry.Types(); end; end` publishes `"App::Types"` as the
112
- # alias scope. The `body:` field is the
113
- # `Prism::StatementsNode` (or nil) we re-walk later for
114
- # user-authored compositions (slice 3).
95
+ # Walks a Prism AST collecting alias-module info: `{module_name:, body:}` for every `module X;
96
+ # include Dry.Types(); …end` shape. Tracks the enclosing module chain so a nested `module App;
97
+ # module Types; include Dry.Types(); end; end` publishes `"App::Types"` as the alias scope. The
98
+ # `body:` field is the `Prism::StatementsNode` (or nil) we re-walk later for user-authored
99
+ # compositions (slice 3).
115
100
  def collect_alias_modules(node, qualified_prefix)
116
101
  return [] unless node.is_a?(Prism::Node)
117
102
 
@@ -127,9 +112,8 @@ module Rigor
127
112
  end
128
113
  current + children
129
114
  when Prism::ClassNode
130
- # Module-level declarations win; we don't recurse into
131
- # class bodies for `include Dry.Types()` because the
132
- # canonical pattern is module-level.
115
+ # Module-level declarations win; we don't recurse into class bodies for `include
116
+ # Dry.Types()` because the canonical pattern is module-level.
133
117
  []
134
118
  else
135
119
  node.compact_child_nodes.flat_map { |c| collect_alias_modules(c, qualified_prefix) }
@@ -137,31 +121,20 @@ module Rigor
137
121
  end
138
122
  private_class_method :collect_alias_modules
139
123
 
140
- # Slice 3 — user-authored composition recognition.
141
- # Walks the alias-module body for `Email =
142
- # String.constrained(...)` shapes. Each
143
- # `ConstantWriteNode` whose RHS is a method chain
144
- # rooted on a canonical-shortcut name (`String`,
145
- # `Integer`, …) — or on a nested-category form
146
- # (`Strict::String` etc.) — registers the LHS under
147
- # the canonical head's underlying class. Unions
148
- # (`String | Integer`) and intersections are skipped
149
- # (no single underlying class).
124
+ # Slice 3 — user-authored composition recognition. Walks the alias-module body for `Email =
125
+ # String.constrained(...)` shapes. Each `ConstantWriteNode` whose RHS is a method chain rooted on
126
+ # a canonical-shortcut name (`String`, `Integer`, …) — or on a nested-category form
127
+ # (`Strict::String` etc.) registers the LHS under the canonical head's underlying class. Unions
128
+ # (`String | Integer`) and intersections are skipped (no single underlying class).
150
129
  #
151
- # Slice 4 — transitive composition resolution. After
152
- # the direct (slice-3) pass collects compositions
153
- # whose RHS root is canonical, a second pass walks the
154
- # remaining `ConstantWriteNode`s for RHS shapes that
155
- # resolve THROUGH an already-published composition
156
- # e.g. `ManagerEmail = Email` (bare reference) or
157
- # `ManagerEmail = Email.constrained(min_size: 3)`
158
- # (method chain rooted on a composition LHS). Cycle
159
- # detection: `A = B; B = A` resolves neither (each
160
- # LHS's resolution walk sees itself in the visited
161
- # set and bails). Unknown references (`ManagerEmail =
162
- # NotAComposition`) silently drop — the user is free
163
- # to assign any constant to any other; the plugin only
164
- # publishes facts when the underlying class is known.
130
+ # Slice 4 — transitive composition resolution. After the direct (slice-3) pass collects
131
+ # compositions whose RHS root is canonical, a second pass walks the remaining
132
+ # `ConstantWriteNode`s for RHS shapes that resolve THROUGH an already-published composition
133
+ # e.g. `ManagerEmail = Email` (bare reference) or `ManagerEmail = Email.constrained(min_size: 3)`
134
+ # (method chain rooted on a composition LHS). Cycle detection: `A = B; B = A` resolves neither
135
+ # (each LHS's resolution walk sees itself in the visited set and bails). Unknown references
136
+ # (`ManagerEmail = NotAComposition`) silently drop — the user is free to assign any constant to
137
+ # any other; the plugin only publishes facts when the underlying class is known.
165
138
  def collect_compositions(body)
166
139
  return {} if body.nil?
167
140
 
@@ -188,14 +161,10 @@ module Rigor
188
161
  end
189
162
  private_class_method :collect_compositions
190
163
 
191
- # Slice-4 helper. Returns the un-resolved RHS root
192
- # constant name (not necessarily a CANONICAL_ALIASES
193
- # entry) for transitive resolution. Mirrors
194
- # {composition_head_canonical} but accepts any
195
- # `ConstantReadNode` / `ConstantPathNode` tail rather
196
- # than canonical-only. Declines on union / intersection
197
- # operators for the same single-underlying-class
198
- # reason.
164
+ # Slice-4 helper. Returns the un-resolved RHS root constant name (not necessarily a
165
+ # CANONICAL_ALIASES entry) for transitive resolution. Mirrors {composition_head_canonical} but
166
+ # accepts any `ConstantReadNode` / `ConstantPathNode` tail rather than canonical-only. Declines on
167
+ # union / intersection operators for the same single-underlying-class reason.
199
168
  def transitive_reference_name(node)
200
169
  case node
201
170
  when Prism::ConstantReadNode
@@ -211,14 +180,10 @@ module Rigor
211
180
  end
212
181
  private_class_method :transitive_reference_name
213
182
 
214
- # Slice-4 helper. Resolves `lhs`'s RHS through the
215
- # `direct` (slice-3) compositions table, chaining
216
- # through `ref_edges` for transitive references.
217
- # Returns the canonical underlying-class name (e.g.
218
- # `"String"`) or nil when no chain ends at a direct
219
- # composition. Cycles silently return nil — every step
220
- # adds the current lhs to `visited` and bails on
221
- # re-entry.
183
+ # Slice-4 helper. Resolves `lhs`'s RHS through the `direct` (slice-3) compositions table, chaining
184
+ # through `ref_edges` for transitive references. Returns the canonical underlying-class name
185
+ # (e.g. `"String"`) or nil when no chain ends at a direct composition. Cycles silently return nil
186
+ # every step adds the current lhs to `visited` and bails on re-entry.
222
187
  def resolve_transitive_ref(lhs, direct, ref_edges, visited:)
223
188
  return nil if visited.include?(lhs)
224
189
 
@@ -231,25 +196,19 @@ module Rigor
231
196
  end
232
197
  private_class_method :resolve_transitive_ref
233
198
 
234
- # Walks an RHS expression looking for the canonical
235
- # shortcut name at the root of a method chain. Returns
236
- # the canonical name (`"String"` etc.) or nil.
199
+ # Walks an RHS expression looking for the canonical shortcut name at the root of a method chain.
200
+ # Returns the canonical name (`"String"` etc.) or nil.
237
201
  #
238
202
  # Recognised shapes (recursively on `node.receiver`):
239
203
  #
240
- # - Bare `String` / `Integer` — `Prism::ConstantReadNode`
241
- # whose name is in `CANONICAL_ALIASES`.
242
- # - `Strict::String` / `Coercible::Integer` / etc. —
243
- # `Prism::ConstantPathNode` whose tail is in
204
+ # - Bare `String` / `Integer` — `Prism::ConstantReadNode` whose name is in `CANONICAL_ALIASES`.
205
+ # - `Strict::String` / `Coercible::Integer` / etc. — `Prism::ConstantPathNode` whose tail is in
244
206
  # `CANONICAL_ALIASES`.
245
- # - `String.constrained(...)` / `.optional` /
246
- # `.default(...)` / arbitrary single-arg method —
207
+ # - `String.constrained(...)` / `.optional` / `.default(...)` / arbitrary single-arg method —
247
208
  # recurse on the receiver.
248
209
  #
249
- # Declines on `String | Integer` (union, `:|`) and
250
- # `String & Foo` (intersection, `:&`) so the alias
251
- # table doesn't claim a single underlying class for
252
- # a multi-class composition.
210
+ # Declines on `String | Integer` (union, `:|`) and `String & Foo` (intersection, `:&`) so the
211
+ # alias table doesn't claim a single underlying class for a multi-class composition.
253
212
  def composition_head_canonical(node)
254
213
  case node
255
214
  when Prism::ConstantReadNode
@@ -266,12 +225,10 @@ module Rigor
266
225
  end
267
226
  private_class_method :composition_head_canonical
268
227
 
269
- # `include Dry.Types()` at the top of the module body is the
270
- # canonical alias declaration. We accept the call anywhere
271
- # in the body (some projects guard it with a `if defined?`
272
- # check). The argument list must be empty (or a kwargs-only
273
- # `default: :nominal` style accepted by upstream; we treat
274
- # both as "alias-installing").
228
+ # `include Dry.Types()` at the top of the module body is the canonical alias declaration. We
229
+ # accept the call anywhere in the body (some projects guard it with a `if defined?` check). The
230
+ # argument list must be empty (or a kwargs-only `default: :nominal` style accepted by upstream;
231
+ # we treat both as "alias-installing").
275
232
  def contains_dry_types_include?(body)
276
233
  return false if body.nil?
277
234
 
@@ -295,10 +252,9 @@ module Rigor
295
252
  end
296
253
  private_class_method :tree_walk
297
254
 
298
- # Matches `include Dry.Types()` (with or without kwargs).
299
- # The receiver of the include call MUST be implicit
300
- # (i.e., called on `self`), and the argument MUST be a
301
- # method call on the `Dry` constant naming `Types`.
255
+ # Matches `include Dry.Types()` (with or without kwargs). The receiver of the include call MUST
256
+ # be implicit (i.e., called on `self`), and the argument MUST be a method call on the `Dry`
257
+ # constant naming `Types`.
302
258
  def include_call_targeting_dry_types?(node)
303
259
  return false unless node.is_a?(Prism::CallNode)
304
260
  return false unless node.name == :include && node.receiver.nil?
@@ -319,11 +275,9 @@ module Rigor
319
275
  end
320
276
  private_class_method :dry_types_call?
321
277
 
322
- # Resolves a `Prism::ConstantPathNode` /
323
- # `Prism::ConstantReadNode` chain to its dot-separated
324
- # name (e.g. `"App::Types"`). Returns nil for the
325
- # dynamic-prefix shape so the scanner treats those as
326
- # opaque rather than guessing.
278
+ # Resolves a `Prism::ConstantPathNode` / `Prism::ConstantReadNode` chain to its dot-separated
279
+ # name (e.g. `"App::Types"`). Returns nil for the dynamic-prefix shape so the scanner treats
280
+ # those as opaque rather than guessing.
327
281
  def qualified_name_for(node)
328
282
  case node
329
283
  when Prism::ConstantReadNode then node.name.to_s
@@ -17,56 +17,44 @@ module Rigor
17
17
  # include Dry.Types()
18
18
  # end
19
19
  #
20
- # and publishes the resulting `{aliased_name => underlying_class}`
21
- # table as the `:dry_type_aliases` cross-plugin fact (ADR-9).
22
- # Other dry-rb adapter plugins consume this fact:
20
+ # and publishes the resulting `{aliased_name => underlying_class}` table as the `:dry_type_aliases`
21
+ # cross-plugin fact (ADR-9). Other dry-rb adapter plugins consume this fact:
23
22
  #
24
- # - `rigor-dry-struct` reads it so `attribute :city, Types::String`
25
- # promotes `address.city` from `Dynamic[Top]` to `Nominal[String]`
26
- # via ADR-18's `returns_from_arg:` fact lookup.
27
- # - `rigor-dry-validation` / `rigor-dry-schema` read it for
28
- # per-key type recognition in `schema { … }` / `params { … }`
29
- # blocks (separate plugin slice).
23
+ # - `rigor-dry-struct` reads it so `attribute :city, Types::String` promotes `address.city` from
24
+ # `Dynamic[Top]` to `Nominal[String]` via ADR-18's `returns_from_arg:` fact lookup.
25
+ # - `rigor-dry-validation` / `rigor-dry-schema` read it for per-key type recognition in `schema { … }`
26
+ # / `params { … }` blocks (separate plugin slice).
30
27
  #
31
28
  # ## Floor / ceiling (slice 1)
32
29
  #
33
30
  # Slice 1 ships the **floor**:
34
31
  #
35
- # - Recognises `module X; include Dry.Types(); end` for any
36
- # constant module name `X` (commonly `Types`, sometimes
37
- # `MyTypes` / `AppTypes`).
38
- # - Maps the **basic** dry-types constants: `String`, `Integer`,
39
- # `Float`, `Decimal`, `Symbol`, `Bool`, `True`, `False`, `Nil`,
40
- # `Date`, `DateTime`, `Time`, `Hash`, `Array`, `Any`.
41
- # - Publishes the table as `{ "<Module>::<Alias>" =>
42
- # "<UnderlyingClass>" }` so consumers can match on the
43
- # qualified constant name they see in source.
32
+ # - Recognises `module X; include Dry.Types(); end` for any constant module name `X` (commonly
33
+ # `Types`, sometimes `MyTypes` / `AppTypes`).
34
+ # - Maps the **basic** dry-types constants: `String`, `Integer`, `Float`, `Decimal`, `Symbol`, `Bool`,
35
+ # `True`, `False`, `Nil`, `Date`, `DateTime`, `Time`, `Hash`, `Array`, `Any`.
36
+ # - Publishes the table as `{ "<Module>::<Alias>" => "<UnderlyingClass>" }` so consumers can match on
37
+ # the qualified constant name they see in source.
44
38
  #
45
39
  # Implemented beyond the floor:
46
40
  #
47
- # - Nested-namespace aliases (`Types::Coercible::Integer`,
48
- # `Types::Strict::Symbol`, `Types::Params::Bool`,
49
- # `Types::JSON::Date`) the four coercion categories each
50
- # map to the same underlying class as their canonical shortcut.
51
- # - User-authored compositions (`Email = Types::String.constrained(...)`,
52
- # transitive resolution through composition chains) — the alias
53
- # surface extends beyond the 15 canonical names.
41
+ # - Nested-namespace aliases (`Types::Coercible::Integer`, `Types::Strict::Symbol`,
42
+ # `Types::Params::Bool`, `Types::JSON::Date`) — the four coercion categories each map to the same
43
+ # underlying class as their canonical shortcut.
44
+ # - User-authored compositions (`Email = Types::String.constrained(...)`, transitive resolution
45
+ # through composition chains) the alias surface extends beyond the 15 canonical names.
54
46
  #
55
47
  # Deferred:
56
48
  #
57
- # - `dry-types.unknown-alias` / `dry-types.alias-shadow`
58
- # diagnostics when downstream code references a name that
59
- # wasn't published.
49
+ # - `dry-types.unknown-alias` / `dry-types.alias-shadow` diagnostics when downstream code references
50
+ # a name that wasn't published.
60
51
  #
61
52
  # ## Why no `diagnostics_for_file` at the floor?
62
53
  #
63
- # The plugin's user-visible value at slice 1 is the published
64
- # fact every downstream uplift (precision promotion in
65
- # `address.city`, schema-key recognition in `rigor-dry-validation`)
66
- # consumes the fact rather than the plugin itself emitting
67
- # diagnostics. The diagnostics surface lands when the
68
- # `dry-types.*` rule family becomes load-bearing for
69
- # demand-driven cases.
54
+ # The plugin's user-visible value at slice 1 is the published fact — every downstream uplift
55
+ # (precision promotion in `address.city`, schema-key recognition in `rigor-dry-validation`) consumes
56
+ # the fact rather than the plugin itself emitting diagnostics. The diagnostics surface lands when the
57
+ # `dry-types.*` rule family becomes load-bearing for demand-driven cases.
70
58
  class DryTypes < Rigor::Plugin::Base
71
59
  manifest(
72
60
  id: "dry-types",
@@ -75,12 +63,9 @@ module Rigor
75
63
  produces: [:dry_type_aliases]
76
64
  )
77
65
 
78
- # Walks every project file once during `prepare(services)` to
79
- # build the alias table, then publishes via the ADR-9 fact
80
- # store. The walk is bounded by the configured `paths:`
81
- # surface; each file's parse error degrades to "no
82
- # contribution" without polluting the user-visible
83
- # diagnostic stream.
66
+ # Walks every project file once during `prepare(services)` to build the alias table, then publishes
67
+ # via the ADR-9 fact store. The walk is bounded by the configured `paths:` surface; each file's
68
+ # parse error degrades to "no contribution" without polluting the user-visible diagnostic stream.
84
69
  def prepare(services)
85
70
  aliases = AliasScanner.scan(paths: scannable_paths(services))
86
71
  return if aliases.empty?
@@ -98,11 +83,9 @@ module Rigor
98
83
 
99
84
  private
100
85
 
101
- # Resolves the project's `paths:` to a flat list of `.rb`
102
- # files the scanner walks. Mirrors `Analysis::Runner`'s
103
- # `expand_paths` floor; we don't need the runner's full
104
- # exclude/sort surface because the alias table is a
105
- # union — any duplicate scan is a no-op.
86
+ # Resolves the project's `paths:` to a flat list of `.rb` files the scanner walks. Mirrors
87
+ # `Analysis::Runner`'s `expand_paths` floor; we don't need the runner's full exclude/sort surface
88
+ # because the alias table is a union — any duplicate scan is a no-op.
106
89
  def scannable_paths(services)
107
90
  @scannable_paths ||= services.configuration.paths.flat_map do |entry|
108
91
  if File.directory?(entry)
@@ -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-dry-types` 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/dry_types.rb` performs at load time.
3
+ # Gem entry point. Required by Rigor's plugin loader when `.rigor.yml` lists `rigor-dry-types` under
4
+ # `plugins:`. The loader expects this `require` to side-effect a call to `Rigor::Plugin.register`,
5
+ # which the body of `lib/rigor/plugin/dry_types.rb` performs at load time.
8
6
  require_relative "rigor/plugin/dry_types"
@@ -5,18 +5,15 @@ require "prism"
5
5
  module Rigor
6
6
  module Plugin
7
7
  class DryValidation < Rigor::Plugin::Base
8
- # Walks project source for `class T < Dry::Validation::Contract`
9
- # subclasses and returns the contract class FQN set.
8
+ # Walks project source for `class T < Dry::Validation::Contract` subclasses and returns the
9
+ # contract class FQN set.
10
10
  #
11
- # Recognition tightness: the superclass match accepts EITHER
12
- # the fully-qualified `Dry::Validation::Contract` (3-segment
13
- # path) OR the lexical-nested `Validation::Contract`
14
- # (2-segment path, when the class body lives inside
15
- # `module Dry`). The bare `< Contract` form (1-segment)
16
- # is NOT recognised — too ambiguous; users who deeply nest
17
- # under `Dry::Validation` should use the explicit form.
18
- # Unrelated `< MyApp::Validation::Contract` shapes with the
19
- # same tail do NOT register.
11
+ # Recognition tightness: the superclass match accepts EITHER the fully-qualified
12
+ # `Dry::Validation::Contract` (3-segment path) OR the lexical-nested `Validation::Contract`
13
+ # (2-segment path, when the class body lives inside `module Dry`). The bare `< Contract` form
14
+ # (1-segment) is NOT recognised too ambiguous; users who deeply nest under `Dry::Validation`
15
+ # should use the explicit form. Unrelated `< MyApp::Validation::Contract` shapes with the same tail
16
+ # do NOT register.
20
17
  module ContractScanner
21
18
  CONTRACT_FULL_PATH = %w[Dry Validation Contract].freeze
22
19
  CONTRACT_LEXICAL_DRY_PATH = %w[Validation Contract].freeze
@@ -24,10 +21,8 @@ module Rigor
24
21
 
25
22
  module_function
26
23
 
27
- # @param paths [Array<String>] absolute paths to `.rb`
28
- # files the project's `paths:` resolves to.
29
- # @return [Array<String>] frozen, sorted list of
30
- # recognized contract class FQNs (e.g.
24
+ # @param paths [Array<String>] absolute paths to `.rb` files the project's `paths:` resolves to.
25
+ # @return [Array<String>] frozen, sorted list of recognized contract class FQNs (e.g.
31
26
  # `["App::NewUserContract", "Types::EmailContract"]`).
32
27
  def scan(paths:)
33
28
  contracts = []
@@ -77,11 +72,9 @@ module Rigor
77
72
  end
78
73
  private_class_method :collect_module_node
79
74
 
80
- # Matches superclasses whose constant chain is EXACTLY
81
- # `Dry::Validation::Contract` (full path) OR EXACTLY
82
- # `Validation::Contract` (lexical-Dry path). Other shapes
83
- # — including same-tail-but-different-root chains and
84
- # the ambiguous bare `Contract` — do not match.
75
+ # Matches superclasses whose constant chain is EXACTLY `Dry::Validation::Contract` (full path) OR
76
+ # EXACTLY `Validation::Contract` (lexical-Dry path). Other shapes — including
77
+ # same-tail-but-different-root chains and the ambiguous bare `Contract` do not match.
85
78
  def contract_subclass?(class_node)
86
79
  superclass = class_node.superclass
87
80
  return false if superclass.nil?
@@ -15,31 +15,23 @@ module Rigor
15
15
  #
16
16
  # Slice 1 floor:
17
17
  #
18
- # - Walks the project for `class T < Dry::Validation::Contract`
19
- # subclasses and publishes the resulting set of contract
20
- # class FQNs as the `:dry_validation_contracts` ADR-9
21
- # cross-plugin fact.
22
- # - Ships an RBS overlay (`sig/dry_validation.rbs`) typing
23
- # `Dry::Validation::Contract#call` (returns Result) and
24
- # `Dry::Validation::Result#{success?, failure?, to_h}`.
25
- # The manifest's `signature_paths: ["sig"]` auto-contributes
26
- # the overlay (ADR-25) — no project-side wiring needed.
18
+ # - Walks the project for `class T < Dry::Validation::Contract` subclasses and publishes the
19
+ # resulting set of contract class FQNs as the `:dry_validation_contracts` ADR-9 cross-plugin fact.
20
+ # - Ships an RBS overlay (`sig/dry_validation.rbs`) typing `Dry::Validation::Contract#call` (returns
21
+ # Result) and `Dry::Validation::Result#{success?, failure?, to_h}`. The manifest's
22
+ # `signature_paths: ["sig"]` auto-contributes the overlay (ADR-25) — no project-side wiring needed.
27
23
  #
28
24
  # Slice 2 (deferred, per design note):
29
25
  #
30
- # - Integrate with `:dry_schema_table` (published by
31
- # `rigor-dry-schema`) so the `params { ... }` block inside a
32
- # Contract contributes a typed `result.to_h` shape per the
33
- # schema. Until this lands, `result.to_h` types as
34
- # `Hash[Symbol, untyped]` (the generic RBS overlay shape).
26
+ # - Integrate with `:dry_schema_table` (published by `rigor-dry-schema`) so the `params { ... }`
27
+ # block inside a Contract contributes a typed `result.to_h` shape per the schema. Until this lands,
28
+ # `result.to_h` types as `Hash[Symbol, untyped]` (the generic RBS overlay shape).
35
29
  #
36
- # Slice 3 (deferred): `json { ... }` adapter parity with
37
- # `params { ... }`. Same shape as slice 2.
30
+ # Slice 3 (deferred): `json { ... }` adapter parity with `params { ... }`. Same shape as slice 2.
38
31
  #
39
- # No ADR-3 amendment is needed for the validation surface
40
- # itself; `Dry::Validation::Result` is a generic class, not a
41
- # sum type (the `success?` / `failure?` predicates narrow via
42
- # existing bool flow facts).
32
+ # No ADR-3 amendment is needed for the validation surface itself; `Dry::Validation::Result` is a
33
+ # generic class, not a sum type (the `success?` / `failure?` predicates narrow via existing bool flow
34
+ # facts).
43
35
  class DryValidation < Rigor::Plugin::Base
44
36
  manifest(
45
37
  id: "dry-validation",
@@ -47,9 +39,8 @@ module Rigor
47
39
  description: "Recognises `class T < Dry::Validation::Contract` subclasses and " \
48
40
  "publishes the contract FQN set.",
49
41
  produces: [:dry_validation_contracts],
50
- # Auto-contribute the bundled RBS overlay (Contract#call -> Result,
51
- # Result#success?/#to_h/...) per ADR-25, so no project-side
52
- # signature_paths wiring is needed.
42
+ # Auto-contribute the bundled RBS overlay (Contract#call -> Result, Result#success?/#to_h/...)
43
+ # per ADR-25, so no project-side signature_paths wiring is needed.
53
44
  signature_paths: ["sig"]
54
45
  )
55
46
 
@@ -1,7 +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-dry-validation` under `plugins:`.
5
- # Side-effects a `Rigor::Plugin.register` call via the
3
+ # Gem entry point. Required by Rigor's plugin loader when `.rigor.yml` lists `rigor-dry-validation`
4
+ # under `plugins:`. Side-effects a `Rigor::Plugin.register` call via the
6
5
  # `lib/rigor/plugin/dry_validation.rb` class body.
7
6
  require_relative "rigor/plugin/dry_validation"