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
@@ -3,53 +3,35 @@
3
3
  module Rigor
4
4
  module Plugin
5
5
  class RailsRoutes < Rigor::Plugin::Base
6
- # Generates the catalogue of route helpers a `devise_for
7
- # :resources` call adds to a Rails app's routing table.
8
- # The set is fixed and well-documented (the
9
- # [Devise wiki](https://github.com/heartcombo/devise/wiki/How-To:-Add-routes-helpers)
10
- # enumerates every generated helper); the generator
11
- # mirrors it.
6
+ # Generates the catalogue of route helpers a `devise_for :resources` call adds to a Rails app's
7
+ # routing table. The set is fixed and well-documented (the
8
+ # [Devise wiki](https://github.com/heartcombo/devise/wiki/How-To:-Add-routes-helpers) enumerates
9
+ # every generated helper); the generator mirrors it.
12
10
  #
13
- # Used by `RoutesParser` when it sees a top-level
14
- # `devise_for :name` (or `devise_for :name, skip:
15
- # [...]`) call. Each generated helper is materialised as
16
- # a `HelperTable::Entry` so the analyzer's
17
- # `unknown-helper` rule recognises them and the arity
18
- # check accepts the canonical signatures (Devise helpers
19
- # take zero positional args plus an optional trailing
20
- # options hash, which `HelperTable#accepts_arity?`
21
- # already handles via the `arity + 1` rule).
11
+ # Used by `RoutesParser` when it sees a top-level `devise_for :name` (or `devise_for :name, skip:
12
+ # [...]`) call. Each generated helper is materialised as a `HelperTable::Entry` so the analyzer's
13
+ # `unknown-helper` rule recognises them and the arity check accepts the canonical signatures (Devise
14
+ # helpers take zero positional args plus an optional trailing options hash, which
15
+ # `HelperTable#accepts_arity?` already handles via the `arity + 1` rule).
22
16
  #
23
17
  # Scope:
24
18
  #
25
- # - Recognises the standard six controllers
26
- # (`:sessions`, `:passwords`, `:confirmations`,
27
- # `:unlocks`, `:registrations`, `:omniauth_callbacks`).
28
- # - Honours `skip: [...]` to suppress controllers the
29
- # project disables.
30
- # - Honours `path: "..."` to remap the URL path (does
31
- # NOT remap the helper-name prefix — Devise uses the
32
- # resource name for the helper, not the path).
33
- # - Does NOT yet honour `as: "..."` overrides, `class_name:`,
34
- # `controllers: { ... }` (these are rare; expand on
35
- # demand).
36
- # - `omniauth_callbacks` helpers are dynamic — the
37
- # provider name is supplied at call time
38
- # (`user_facebook_omniauth_authorize_path`) and Devise
39
- # declares them from the configured providers, which
40
- # live in an initializer this static parser does not
41
- # read. The suffix patterns are registered in
42
- # `OMNIAUTH_SUFFIXES` and consulted by
43
- # `HelperTable#omniauth_match?`.
19
+ # - Recognises the standard six controllers (`:sessions`, `:passwords`, `:confirmations`, `:unlocks`,
20
+ # `:registrations`, `:omniauth_callbacks`).
21
+ # - Honours `skip: [...]` to suppress controllers the project disables.
22
+ # - Honours `path: "..."` to remap the URL path (does NOT remap the helper-name prefix — Devise uses
23
+ # the resource name for the helper, not the path).
24
+ # - Does NOT yet honour `as: "..."` overrides, `class_name:`, `controllers: { ... }` (these are rare;
25
+ # expand on demand).
26
+ # - `omniauth_callbacks` helpers are dynamic the provider name is supplied at call time
27
+ # (`user_facebook_omniauth_authorize_path`) and Devise declares them from the configured providers,
28
+ # which live in an initializer this static parser does not read. The suffix patterns are registered
29
+ # in `OMNIAUTH_SUFFIXES` and consulted by `HelperTable#omniauth_match?`.
44
30
  module DeviseRoutes
45
- # The standard Devise controllers and the helper
46
- # actions each generates. Keys are the controller
47
- # name; values are arrays of `[helper_prefix,
48
- # http_method, action]` triples `helper_prefix` is
49
- # the part BEFORE the resource-name segment (Rails
50
- # produces `<prefix>_<resource>_<suffix>_path`; for
51
- # the bare `<resource>_session_path` shape the prefix
52
- # is empty).
31
+ # The standard Devise controllers and the helper actions each generates. Keys are the controller
32
+ # name; values are arrays of `[helper_prefix, http_method, action]` triples — `helper_prefix` is
33
+ # the part BEFORE the resource-name segment (Rails produces `<prefix>_<resource>_<suffix>_path`;
34
+ # for the bare `<resource>_session_path` shape the prefix is empty).
53
35
  CONTROLLER_HELPERS = {
54
36
  sessions: [
55
37
  ["new", "session", :get, :new],
@@ -77,13 +59,10 @@ module Rigor
77
59
  ]
78
60
  }.freeze
79
61
 
80
- # Dynamic-provider helper patterns. `_omniauth_authorize_path`
81
- # and `_omniauth_callback_path` are generated per
82
- # configured provider (Facebook, GitHub, ) by an
83
- # initializer this parser does not read. We treat any
84
- # `<resource>_<provider>_omniauth_(authorize|callback)_(path|url)`
85
- # call as recognised when the resource is one the
86
- # project declared via `devise_for`.
62
+ # Dynamic-provider helper patterns. `_omniauth_authorize_path` and `_omniauth_callback_path` are
63
+ # generated per configured provider (Facebook, GitHub, …) by an initializer this parser does not
64
+ # read. We treat any `<resource>_<provider>_omniauth_(authorize|callback)_(path|url)` call as
65
+ # recognised when the resource is one the project declared via `devise_for`.
87
66
  OMNIAUTH_SUFFIXES = %w[
88
67
  _omniauth_authorize_path _omniauth_authorize_url
89
68
  _omniauth_callback_path _omniauth_callback_url
@@ -91,16 +70,12 @@ module Rigor
91
70
 
92
71
  module_function
93
72
 
94
- # @param resource [String, Symbol] the `devise_for`
95
- # argument typically `:users`. Used as both the
96
- # helper-name segment (`new_user_session_path`) and
97
- # to derive the resource path.
98
- # @param skip [Array<Symbol>] controllers the project
99
- # disables via `devise_for :users, skip: [:registrations]`.
100
- # @return [Array<HelperTable::Entry>] one entry per
101
- # generated `_path` helper. The caller is expected to
102
- # pair `_url` variants the same way `RoutesParser` does
103
- # for other entries.
73
+ # @param resource [String, Symbol] the `devise_for` argument — typically `:users`. Used as both the
74
+ # helper-name segment (`new_user_session_path`) and to derive the resource path.
75
+ # @param skip [Array<Symbol>] controllers the project disables via `devise_for :users, skip:
76
+ # [:registrations]`.
77
+ # @return [Array<HelperTable::Entry>] one entry per generated `_path` helper. The caller is
78
+ # expected to pair `_url` variants the same way `RoutesParser` does for other entries.
104
79
  def generate(resource:, skip: [])
105
80
  resource_segment = singularize(resource.to_s)
106
81
  skip_set = skip.to_set(&:to_sym)
@@ -126,21 +101,15 @@ module Rigor
126
101
  end
127
102
  end
128
103
 
129
- # Devise also ships *scoped* helpers
130
- # (`Devise::Controllers::UrlHelpers`) that DROP the
131
- # resource segment and take the scope as a positional
132
- # argument: `new_password_path(resource_name)` is the
104
+ # Devise also ships *scoped* helpers (`Devise::Controllers::UrlHelpers`) that DROP the resource
105
+ # segment and take the scope as a positional argument: `new_password_path(resource_name)` is the
133
106
  # bare form of `new_user_password_path`. Used inside
134
- # `Auth::PasswordsController < Devise::PasswordsController`
135
- # (Mastodon's idiom). Arity 1 (the scope), and a
136
- # trailing options-hash bumps it via the existing
137
- # `arity + 1` rule.
107
+ # `Auth::PasswordsController < Devise::PasswordsController` (Mastodon's idiom). Arity 1 (the
108
+ # scope), and a trailing options-hash bumps it via the existing `arity + 1` rule.
138
109
  entries.concat(scoped_helpers(skip_set))
139
110
 
140
- # `omniauth_authorize_path(scope, provider)` and
141
- # `omniauth_callback_path(scope, provider)` are
142
- # `Devise::Controllers::UrlHelpers` scoped helpers
143
- # that delegate to the OmniAuth gem. Arity 2.
111
+ # `omniauth_authorize_path(scope, provider)` and `omniauth_callback_path(scope, provider)` are
112
+ # `Devise::Controllers::UrlHelpers` scoped helpers that delegate to the OmniAuth gem. Arity 2.
144
113
  unless skip_set.include?(:omniauth_callbacks)
145
114
  %w[omniauth_authorize_path omniauth_callback_path].each do |name|
146
115
  entries << HelperTable::Entry.new(
@@ -156,11 +125,9 @@ module Rigor
156
125
  entries
157
126
  end
158
127
 
159
- # Scoped helpers Devise exposes inside its controllers.
160
- # The arity-1 family caller supplies the resource
161
- # scope (`:user` etc.) as the first positional arg.
162
- # Names mirror the qualified `<scope>_<name>_path`
163
- # entries above, minus the scope segment.
128
+ # Scoped helpers Devise exposes inside its controllers. The arity-1 family — caller supplies the
129
+ # resource scope (`:user` etc.) as the first positional arg. Names mirror the qualified
130
+ # `<scope>_<name>_path` entries above, minus the scope segment.
164
131
  SCOPED_HELPERS = {
165
132
  sessions: %w[new_session_path session_path destroy_session_path],
166
133
  passwords: %w[new_password_path edit_password_path password_path],
@@ -186,28 +153,22 @@ module Rigor
186
153
  end
187
154
  end
188
155
 
189
- # Returns the set of OmniAuth pattern suffixes the
190
- # analyzer accepts for a given resource. The analyzer
191
- # consults this set (via `HelperTable#omniauth_match?`)
192
- # when a `*_path` / `*_url` call's name does not match
193
- # any registered entry and its prefix matches a Devise
194
- # resource.
156
+ # Returns the set of OmniAuth pattern suffixes the analyzer accepts for a given resource. The
157
+ # analyzer consults this set (via `HelperTable#omniauth_match?`) when a `*_path` / `*_url` call's
158
+ # name does not match any registered entry and its prefix matches a Devise resource.
195
159
  def omniauth_suffixes
196
160
  OMNIAUTH_SUFFIXES
197
161
  end
198
162
 
199
- # ADR-39 — delegates to the shared inflector (real
200
- # `ActiveSupport::Inflector` when available), replacing the tiny
201
- # AS-replica this module used to duplicate from `RoutesParser`.
163
+ # ADR-39 — delegates to the shared inflector (real `ActiveSupport::Inflector` when available),
164
+ # replacing the tiny AS-replica this module used to duplicate from `RoutesParser`.
202
165
  def singularize(word)
203
166
  Rigor::Plugin::Inflector.singularize(word)
204
167
  end
205
168
 
206
- # Approximate path generation. We don't honour the
207
- # `path:` Devise option here because the helper NAME
208
- # is what the analyzer cares about; the `path` field is
209
- # informational and surfaces only in the `:helper` info
210
- # diagnostic.
169
+ # Approximate path generation. We don't honour the `path:` Devise option here because the helper
170
+ # NAME is what the analyzer cares about; the `path` field is informational and surfaces only in
171
+ # the `:helper` info diagnostic.
211
172
  def devise_path_for(resource_segment, controller, action)
212
173
  plural = "#{resource_segment}s"
213
174
  base = "/#{plural}"
@@ -3,39 +3,27 @@
3
3
  module Rigor
4
4
  module Plugin
5
5
  class RailsRoutes < Rigor::Plugin::Base
6
- # Generates the catalogue of OAuth route helpers a
7
- # `use_doorkeeper do ... end` block adds to a Rails app's
8
- # routing table. The set is the Doorkeeper gem's
9
- # documented default see Doorkeeper's
10
- # [Routing](https://github.com/doorkeeper-gem/doorkeeper/wiki/Customizing-routes)
11
- # documentation; this module mirrors the canonical
12
- # helper names.
6
+ # Generates the catalogue of OAuth route helpers a `use_doorkeeper do ... end` block adds to a Rails
7
+ # app's routing table. The set is the Doorkeeper gem's documented default — see Doorkeeper's
8
+ # [Routing](https://github.com/doorkeeper-gem/doorkeeper/wiki/Customizing-routes) documentation; this
9
+ # module mirrors the canonical helper names.
13
10
  #
14
- # Used by `RoutesParser` when it sees a `use_doorkeeper`
15
- # call. Each generated helper is materialised as a
16
- # `HelperTable::Entry` so the analyzer's
17
- # `unknown-helper` rule recognises them. Arity for the
18
- # `oauth_application_path(:id)` / `oauth_authorized_application_path(:id)`
19
- # forms is 1 (the `:id`); all other Doorkeeper helpers
20
- # are arity 0.
11
+ # Used by `RoutesParser` when it sees a `use_doorkeeper` call. Each generated helper is materialised
12
+ # as a `HelperTable::Entry` so the analyzer's `unknown-helper` rule recognises them. Arity for the
13
+ # `oauth_application_path(:id)` / `oauth_authorized_application_path(:id)` forms is 1 (the `:id`);
14
+ # all other Doorkeeper helpers are arity 0.
21
15
  #
22
16
  # Scope:
23
17
  #
24
- # - Recognises the standard helper set: authorization,
25
- # token, revoke, introspect, token_info, userinfo,
26
- # applications (resources), authorized_applications.
27
- # - Honours `skip_controllers <names>` inside the block
28
- # to omit the named controller's helpers.
29
- # - Does NOT yet honour `controllers ...:` remappings
30
- # those change WHICH class serves the route but not
31
- # the helper NAMES, so omitting them is sound.
32
- # - Does NOT yet honour custom `as:` / `at:` on
33
- # `use_doorkeeper` itself.
18
+ # - Recognises the standard helper set: authorization, token, revoke, introspect, token_info,
19
+ # userinfo, applications (resources), authorized_applications.
20
+ # - Honours `skip_controllers <names>` inside the block to omit the named controller's helpers.
21
+ # - Does NOT yet honour `controllers ...:` remappings — those change WHICH class serves the route but
22
+ # not the helper NAMES, so omitting them is sound.
23
+ # - Does NOT yet honour custom `as:` / `at:` on `use_doorkeeper` itself.
34
24
  module DoorkeeperRoutes
35
- # Per-controller helper catalogue. Keys are the
36
- # `skip_controllers :<key>` names; values are the
37
- # entries (name + arity + path + http_method).
38
- # Both `_path` and `_url` variants are paired by
25
+ # Per-controller helper catalogue. Keys are the `skip_controllers :<key>` names; values are the
26
+ # entries (name + arity + path + http_method). Both `_path` and `_url` variants are paired by
39
27
  # `RoutesParser` after registration.
40
28
  CONTROLLER_HELPERS = {
41
29
  authorizations: [
@@ -78,8 +66,7 @@ module Rigor
78
66
 
79
67
  module_function
80
68
 
81
- # @param skip [Array<Symbol>] controller names the
82
- # project omits via `skip_controllers :name, ...`.
69
+ # @param skip [Array<Symbol>] controller names the project omits via `skip_controllers :name, ...`.
83
70
  # @return [Array<HelperTable::Entry>] flattened entries.
84
71
  def generate(skip: [])
85
72
  skip_set = skip.to_set(&:to_sym)
@@ -5,76 +5,54 @@ require "prism"
5
5
  module Rigor
6
6
  module Plugin
7
7
  class RailsRoutes < Rigor::Plugin::Base
8
- # Walks `app/helpers/**/*.rb` (or any configured set of
9
- # helper directories) and extracts every public
10
- # module-level method name so the analyzer's
11
- # `unknown-helper` rule does not false-fire on calls to
12
- # project-defined `*_path` / `*_url` helpers (the
13
- # canonical Rails pattern: a `UrlHelper` module exposing
14
- # `full_asset_url`, `host_to_url`, `frontend_asset_url`,
15
- # and so on).
8
+ # Walks `app/helpers/**/*.rb` (or any configured set of helper directories) and extracts every public
9
+ # module-level method name so the analyzer's `unknown-helper` rule does not false-fire on calls to
10
+ # project-defined `*_path` / `*_url` helpers (the canonical Rails pattern: a `UrlHelper` module
11
+ # exposing `full_asset_url`, `host_to_url`, `frontend_asset_url`, and so on).
16
12
  #
17
13
  # The discoverer is deliberately conservative:
18
14
  #
19
- # - Walks `def name(...)` declarations at the module /
20
- # class top level. Nested `def` inside a method body or
21
- # a `define_method` macro is NOT extracted (the same
22
- # reason `rigor-actionpack` is not the canonical
23
- # custom-helper sourcemetaprogrammed helpers are out
24
- # of scope for a static scan).
25
- # - **Visibility-agnostic.** A `private` / `protected`
26
- # `def some_url` IS registered. The `unknown-helper`
27
- # rule is a project-wide name-presence check, not a
28
- # visibility checkif the user wrote a `_path` /
29
- # `_url` method anywhere in `app/`, they very likely
30
- # intend to call it (often Mastodon's pattern: a
31
- # controller's `private def page_url(page)` invoked
32
- # by a `link_to` in its own view). The core engine's
33
- # `call.undefined-method` rule still catches the case
34
- # where the receiver genuinely cannot see the method.
35
- # - Ignores `def self.x` (singleton-method definitions) —
36
- # Rails helpers are instance methods on the helper
37
- # module; class-side `self.x` shapes are usually
38
- # internal utilities, not view helpers.
39
- # - Filters the resulting set to names ending in `_path`
40
- # or `_url` (the dispatch surface the analyzer's rule
41
- # actually cares about). A helper whose name does not
42
- # end with either suffix is irrelevant to the rule's
43
- # false-positive surface, so excluding it costs nothing
44
- # and keeps the registered set focused.
15
+ # - Walks `def name(...)` declarations at the module / class top level. Nested `def` inside a method
16
+ # body or a `define_method` macro is NOT extracted (the same reason `rigor-actionpack` is not the
17
+ # canonical custom-helper source metaprogrammed helpers are out of scope for a static scan).
18
+ # - **Visibility-agnostic.** A `private` / `protected` `def some_url` IS registered. The
19
+ # `unknown-helper` rule is a project-wide name-presence check, not a visibility check if the user
20
+ # wrote a `_path` / `_url` method anywhere in `app/`, they very likely intend to call it (often
21
+ # Mastodon's pattern: a controller's `private def page_url(page)` invoked by a `link_to` in its own
22
+ # view). The core engine's `call.undefined-method` rule still catches the case where the receiver
23
+ # genuinely cannot see the method.
24
+ # - Ignores `def self.x` (singleton-method definitions) Rails helpers are instance methods on the
25
+ # helper module; class-side `self.x` shapes are usually internal utilities, not view helpers.
26
+ # - Filters the resulting set to names ending in `_path` or `_url` (the dispatch surface the
27
+ # analyzer's rule actually cares about). A helper whose name does not end with either suffix is
28
+ # irrelevant to the rule's false-positive surface, so excluding it costs nothing and keeps the
29
+ # registered set focused.
45
30
  #
46
31
  # Out of scope (intentional):
47
32
  #
48
- # - Helpers defined via `define_method` or via macros like
49
- # `inheritance_traversed_helpers`. These would need
50
- # ADR-16 macro substrate or per-plugin custom recognizers.
51
- # - Helpers inherited from gems (`Devise::Controllers::Helpers`,
52
- # `ViteRails::TagHelpers`, etc.). Those need their own
53
- # handling — Devise auto-routes are covered by the
54
- # companion `DeviseRoutes` generator; other gem-injected
55
- # helpers fall to ADR-25 plugin-contributed RBS or the
56
- # ADR-10 `dependencies.source_inference:` path.
33
+ # - Helpers defined via `define_method` or via macros like `inheritance_traversed_helpers`. These
34
+ # would need ADR-16 macro substrate or per-plugin custom recognizers.
35
+ # - Helpers inherited from gems (`Devise::Controllers::Helpers`, `ViteRails::TagHelpers`, etc.).
36
+ # Those need their own handling — Devise auto-routes are covered by the companion `DeviseRoutes`
37
+ # generator; other gem-injected helpers fall to ADR-25 plugin-contributed RBS or the ADR-10
38
+ # `dependencies.source_inference:` path.
57
39
  module HelperDiscoverer
58
40
  HELPER_SUFFIXES = [/_path\z/, /_url\z/].freeze
59
41
 
60
42
  module_function
61
43
 
62
- # @param contents_per_path [Hash{String => String}]
63
- # file path source text. The caller is responsible
64
- # for reading files (typically through the trusted
65
- # `IoBoundary` so cache invalidation works).
66
- # @return [Set<String>] method names suitable for
67
- # inclusion in the `HelperTable`'s custom-helper set.
44
+ # @param contents_per_path [Hash{String => String}] file path → source text. The caller is
45
+ # responsible for reading files (typically through the trusted `IoBoundary` so cache invalidation
46
+ # works).
47
+ # @return [Set<String>] method names suitable for inclusion in the `HelperTable`'s custom-helper set.
68
48
  def discover(contents_per_path)
69
49
  names = []
70
50
  contents_per_path.each_value do |contents|
71
51
  names.concat(extract_from_contents(contents))
72
52
  rescue StandardError
73
- # Skip any file whose AST walk explodes — discovery
74
- # is best-effort and a parse failure in one helper
75
- # file MUST NOT abort discovery for the rest. Other
76
- # rules will still surface the parse failure on the
77
- # affected file through the core pipeline.
53
+ # Skip any file whose AST walk explodes — discovery is best-effort and a parse failure in one
54
+ # helper file MUST NOT abort discovery for the rest. Other rules will still surface the parse
55
+ # failure on the affected file through the core pipeline.
78
56
  next
79
57
  end
80
58
  names.to_set
@@ -89,17 +67,9 @@ module Rigor
89
67
  walker.helper_names
90
68
  end
91
69
 
92
- # Per-file AST walker. Tracks the current visibility
93
- # mode (`:public` by default; `private` / `protected`
94
- # calls flip the bit) so a `def` declared after a bare
95
- # `private` is excluded.
96
- #
97
- # The walker is recursive into module / class bodies so
98
- # `module UrlHelpers; module Routing; def foo_url; end;
99
- # end; end` registers `foo_url`. It does NOT recurse
100
- # into method bodies (a `def inside def` would be
101
- # `define_method`-shaped, which we skip per the
102
- # docstring).
70
+ # Per-file AST walker. The walker is recursive into module / class bodies so `module UrlHelpers;
71
+ # module Routing; def foo_url; end; end; end` registers `foo_url`. It does NOT recurse into method
72
+ # bodies (a `def inside def` would be `define_method`-shaped, which we skip per the docstring).
103
73
  class ModuleBodyWalker
104
74
  attr_reader :helper_names
105
75
 
@@ -134,11 +104,9 @@ module Rigor
134
104
  def visit_statements(body)
135
105
  return if body.nil?
136
106
 
137
- # Visibility tracking is intentionally absent — see
138
- # the "visibility-agnostic" point in the module
139
- # docstring. A `private def page_url(page)` inside
140
- # a controller IS registered so a caller in the
141
- # paired view does not false-fire `unknown-helper`.
107
+ # Visibility tracking is intentionally absent — see the "visibility-agnostic" point in the
108
+ # module docstring. A `private def page_url(page)` inside a controller IS registered so a
109
+ # caller in the paired view does not false-fire `unknown-helper`.
142
110
  statements_of(body).each do |stmt|
143
111
  case stmt
144
112
  when Prism::DefNode
@@ -6,71 +6,50 @@ require_relative "doorkeeper_routes"
6
6
  module Rigor
7
7
  module Plugin
8
8
  class RailsRoutes < Rigor::Plugin::Base
9
- # Frozen catalogue of route helpers parsed from
10
- # `config/routes.rb`. Each entry maps a helper name
11
- # (`users_path`, `edit_user_path`, …) to the metadata
12
- # downstream consumers and the analyzer's per-call
9
+ # Frozen catalogue of route helpers parsed from `config/routes.rb`. Each entry maps a helper name
10
+ # (`users_path`, `edit_user_path`, …) to the metadata downstream consumers and the analyzer's per-call
13
11
  # validation need:
14
12
  #
15
- # - `arity`: number of positional arguments the helper
16
- # takes. `users_path` → 0; `user_path(:id)` → 1;
13
+ # - `arity`: number of positional arguments the helper takes. `users_path` → 0; `user_path(:id)` → 1;
17
14
  # `user_post_path(:user_id, :id)` → 2.
18
- # - `path`: the path template Rails generates
19
- # (`/users/:user_id/posts/:id`).
20
- # - `http_method`: `:get` / `:post` / `:patch` / `:put` /
21
- # `:delete` for the canonical action; `nil` for
22
- # helpers that span multiple methods (a `resources`
23
- # show helper isn't HTTP-method-specific in the
24
- # helper sense — it's path-sensitive only).
25
- # - `action`: `:index` / `:show` / `:new` / `:edit` /
26
- # `:create` / `:update` / `:destroy` for resourceful
27
- # routes; `:custom` for explicit `get`/`post`/etc.;
28
- # `:root` for the root route.
15
+ # - `path`: the path template Rails generates (`/users/:user_id/posts/:id`).
16
+ # - `http_method`: `:get` / `:post` / `:patch` / `:put` / `:delete` for the canonical action; `nil`
17
+ # for helpers that span multiple methods (a `resources` show helper isn't HTTP-method-specific in
18
+ # the helper sense it's path-sensitive only).
19
+ # - `action`: `:index` / `:show` / `:new` / `:edit` / `:create` / `:update` / `:destroy` for
20
+ # resourceful routes; `:custom` for explicit `get`/`post`/etc.; `:root` for the root route.
29
21
  #
30
- # Both `_path` and `_url` forms share the same metadata —
31
- # the table records each helper twice (once with `_path`,
32
- # once with `_url`) for `O(1)` lookup at the call site.
22
+ # Both `_path` and `_url` forms share the same metadata — the table records each helper twice (once
23
+ # with `_path`, once with `_url`) for `O(1)` lookup at the call site.
33
24
  class HelperTable
34
25
  Entry = Data.define(:name, :arity, :path, :http_method, :action)
35
26
 
36
27
  attr_reader :entries, :custom_helpers, :devise_resources
37
28
 
38
- # @param entries [Array<Entry>] freshly built; the
39
- # factory below is the canonical construction path.
40
- # @param custom_helpers [Enumerable<String>] names of
41
- # project-defined helper methods (typically pulled
42
- # from `app/helpers/**/*.rb` by
43
- # {HelperDiscoverer}) that the analyzer should treat
44
- # as known they do NOT correspond to a route but
45
- # their presence MUST NOT fire `unknown-helper`. No
46
- # arity / path metadata is recorded; the analyzer
47
- # skips the route-side checks for these names.
48
- # @param devise_resources [Enumerable<String>] resource
49
- # names declared via `devise_for :resource` (already
50
- # singularised, e.g. `"user"`). Used to recognise the
51
- # dynamic OmniAuth helper family
52
- # (`<resource>_<provider>_omniauth_(authorize|callback)_(path|url)`)
53
- # whose provider segment is supplied at runtime — no
54
- # per-name entry is registered for them but they MUST
55
- # NOT fire `unknown-helper` either.
29
+ # @param entries [Array<Entry>] freshly built; the factory below is the canonical construction path.
30
+ # @param custom_helpers [Enumerable<String>] names of project-defined helper methods (typically
31
+ # pulled from `app/helpers/**/*.rb` by {HelperDiscoverer}) that the analyzer should treat as
32
+ # known they do NOT correspond to a route but their presence MUST NOT fire `unknown-helper`. No
33
+ # arity / path metadata is recorded; the analyzer skips the route-side checks for these names.
34
+ # @param devise_resources [Enumerable<String>] resource names declared via `devise_for :resource`
35
+ # (already singularised, e.g. `"user"`). Used to recognise the dynamic OmniAuth helper family
36
+ # (`<resource>_<provider>_omniauth_(authorize|callback)_(path|url)`) whose provider segment is
37
+ # supplied at runtime no per-name entry is registered for them but they MUST NOT fire
38
+ # `unknown-helper` either.
56
39
  def initialize(entries, custom_helpers: [], devise_resources: [])
57
40
  @entries = entries.freeze
58
- # Multimap: a single helper name can map to multiple
59
- # entries when an uncountable-noun resource registers
60
- # both an arity-0 index helper and an arity-1 show
61
- # helper under the same `news_path` name. `find`
62
- # returns the first entry (preserving the previous
63
- # API); `accepts_arity?` checks against every entry.
41
+ # Multimap: a single helper name can map to multiple entries when an uncountable-noun resource
42
+ # registers both an arity-0 index helper and an arity-1 show helper under the same `news_path`
43
+ # name. `find` returns the first entry (preserving the previous API); `accepts_arity?` checks
44
+ # against every entry.
64
45
  @by_name = entries.group_by(&:name).transform_values(&:freeze).freeze
65
46
  @custom_helpers = custom_helpers.to_set.freeze
66
47
  @devise_resources = devise_resources.to_set(&:to_s).freeze
67
48
  freeze
68
49
  end
69
50
 
70
- # @return [Entry, nil] First matching entry; for the
71
- # uncountable-noun case this is the index helper
72
- # (the show helper is also registered but starts
73
- # second).
51
+ # @return [Entry, nil] First matching entry; for the uncountable-noun case this is the index helper
52
+ # (the show helper is also registered but starts second).
74
53
  def find(helper_name)
75
54
  @by_name[helper_name.to_s]&.first
76
55
  end
@@ -80,14 +59,10 @@ module Rigor
80
59
  @by_name.key?(helper_name.to_s)
81
60
  end
82
61
 
83
- # True when `helper_name` is either a registered route
84
- # helper, a discovered project-defined custom helper,
85
- # OR a dynamic OmniAuth-shaped helper for one of the
86
- # declared `devise_for` resources. The
87
- # `unknown-helper` rule consults this predicate to
88
- # decide whether to fire — `known?` alone misses
89
- # custom helpers and OmniAuth providers, which would
90
- # then false-fire on canonical Rails-app patterns.
62
+ # True when `helper_name` is either a registered route helper, a discovered project-defined custom
63
+ # helper, OR a dynamic OmniAuth-shaped helper for one of the declared `devise_for` resources. The
64
+ # `unknown-helper` rule consults this predicate to decide whether to fire — `known?` alone misses
65
+ # custom helpers and OmniAuth providers, which would then false-fire on canonical Rails-app patterns.
91
66
  def recognised?(helper_name)
92
67
  name = helper_name.to_s
93
68
  return true if @by_name.key?(name)
@@ -96,11 +71,9 @@ module Rigor
96
71
  omniauth_match?(name)
97
72
  end
98
73
 
99
- # `<resource>_<provider>_omniauth_(authorize|callback)_(path|url)` — when
100
- # `<resource>` matches a declared `devise_for` resource
101
- # the helper is dynamic-provider Devise OmniAuth. The
102
- # provider segment is opaque to a static parser, so we
103
- # accept any non-empty token between the resource and
74
+ # `<resource>_<provider>_omniauth_(authorize|callback)_(path|url)` — when `<resource>` matches a
75
+ # declared `devise_for` resource the helper is dynamic-provider Devise OmniAuth. The provider
76
+ # segment is opaque to a static parser, so we accept any non-empty token between the resource and
104
77
  # the omniauth suffix.
105
78
  def omniauth_match?(name)
106
79
  return false if @devise_resources.empty?
@@ -115,19 +88,16 @@ module Rigor
115
88
  end
116
89
  end
117
90
 
118
- # @return [Boolean] true when any entry under this
119
- # helper name accepts the given positional arity.
91
+ # @return [Boolean] true when any entry under this helper name accepts the given positional arity.
120
92
  #
121
- # Rails helpers have the signature `helper(*segments, options = {})`,
122
- # so `expected + 1` is always valid the extra argument is treated
123
- # as a query-params/options hash (e.g. `users_path(page: 2)` or
124
- # `user_path(@u, pagination_params(...))`).
93
+ # Rails helpers have the signature `helper(*segments, options = {})`, so `expected + 1` is always
94
+ # valid the extra argument is treated as a query-params/options hash (e.g. `users_path(page: 2)`
95
+ # or `user_path(@u, pagination_params(...))`).
125
96
  def accepts_arity?(helper_name, arity)
126
97
  (@by_name[helper_name.to_s] || []).any? { |entry| entry.arity == arity || entry.arity + 1 == arity }
127
98
  end
128
99
 
129
- # @return [Array<Integer>] all accepted positional
130
- # arities for a helper name. Empty when unknown.
100
+ # @return [Array<Integer>] all accepted positional arities for a helper name. Empty when unknown.
131
101
  def acceptable_arities(helper_name)
132
102
  (@by_name[helper_name.to_s] || []).map(&:arity).uniq
133
103
  end
@@ -146,11 +116,9 @@ module Rigor
146
116
  end
147
117
 
148
118
  def to_h
149
- # Plain dump for fact-store publishing (ADR-9). Each
150
- # name serialises as a small Hash for the FIRST entry
151
- # under that name, with `acceptable_arities` carrying
152
- # the full arity set so cross-plugin consumers can
153
- # honour the uncountable-noun multi-arity case.
119
+ # Plain dump for fact-store publishing (ADR-9). Each name serialises as a small Hash for the
120
+ # FIRST entry under that name, with `acceptable_arities` carrying the full arity set so
121
+ # cross-plugin consumers can honour the uncountable-noun multi-arity case.
154
122
  @by_name.transform_values do |group|
155
123
  entry = group.first
156
124
  { name: entry.name, arity: entry.arity, path: entry.path,