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
@@ -8,17 +8,13 @@ require_relative "factorybot/factory_index"
8
8
 
9
9
  module Rigor
10
10
  module Plugin
11
- # rigor-factorybot — validates `FactoryBot.create(:name,
12
- # key: ...)` and the build / build_stubbed /
13
- # attributes_for / *_list family against a per-run index
14
- # built from `factory_search_paths`.
11
+ # rigor-factorybot — validates `FactoryBot.create(:name, key: ...)` and the build / build_stubbed /
12
+ # attributes_for / *_list family against a per-run index built from `factory_search_paths`.
15
13
  #
16
- # Recognises factory NAMES + literal ATTRIBUTE KEYS in
17
- # the call's keyword hash. The AR column cross-check uses
18
- # the `rigor-activerecord` `:model_index` ADR-9 fact when
19
- # that plugin is loaded (optional `consumes:`).
20
- # Traits, sequences, parent / child factories, and dynamic
21
- # factory names are deferred to follow-up slices.
14
+ # Recognises factory NAMES + literal ATTRIBUTE KEYS in the call's keyword hash. The AR column
15
+ # cross-check uses the `rigor-activerecord` `:model_index` ADR-9 fact when that plugin is loaded
16
+ # (optional `consumes:`). Traits, sequences, parent / child factories, and dynamic factory names are
17
+ # deferred to follow-up slices.
22
18
  #
23
19
  # ## Configuration
24
20
  #
@@ -33,43 +29,32 @@ module Rigor
33
29
  #
34
30
  # ## What it checks
35
31
  #
36
- # - **Factory existence** — every entry call's first
37
- # positional Symbol / String literal is looked up in
38
- # the index. Missing factories emit `unknown-factory`
39
- # with a `DidYouMean` suggestion.
40
- # - **Attribute key existence** — every literal-Symbol
41
- # keyword-argument key is matched against the factory's
42
- # declared attribute names. Missing keys emit
43
- # `unknown-attribute` with a `DidYouMean` suggestion.
44
- # - **Trace** — recognised entry calls also emit a
45
- # `factory-call` info diagnostic listing the factory's
46
- # declared attribute set.
32
+ # - **Factory existence** — every entry call's first positional Symbol / String literal is looked up
33
+ # in the index. Missing factories emit `unknown-factory` with a `DidYouMean` suggestion.
34
+ # - **Attribute key existence** — every literal-Symbol keyword-argument key is matched against the
35
+ # factory's declared attribute names. Missing keys emit `unknown-attribute` with a `DidYouMean`
36
+ # suggestion.
37
+ # - **Trace** recognised entry calls also emit a `factory-call` info diagnostic listing the
38
+ # factory's declared attribute set.
47
39
  #
48
40
  # ## Recognised entry methods
49
41
  #
50
- # `FactoryBot.create`, `.build`, `.build_stubbed`,
51
- # `.attributes_for`, `.create_list`, `.build_list`,
52
- # `.build_stubbed_list`. The legacy `FactoryGirl` constant
53
- # is recognised identically. Implicit-receiver calls
54
- # (`create(:name)` inside an `include FactoryBot::Syntax::Methods`
55
- # context) are NOT recognised — too many false positives on
56
- # plain `create` calls outside test files; deferred until
57
- # receiver-type inference can disambiguate.
42
+ # `FactoryBot.create`, `.build`, `.build_stubbed`, `.attributes_for`, `.create_list`, `.build_list`,
43
+ # `.build_stubbed_list`. The legacy `FactoryGirl` constant is recognised identically. Implicit-receiver
44
+ # calls (`create(:name)` inside an `include FactoryBot::Syntax::Methods` context) are NOT recognised —
45
+ # too many false positives on plain `create` calls outside test files; deferred until receiver-type
46
+ # inference can disambiguate.
58
47
  #
59
48
  # ## What's recognised inside `factory :name do ... end`
60
49
  #
61
- # - `name { "Alice" }` — implicit attribute via
62
- # `method_missing` with a block (modern syntax).
63
- # - `name "Alice"` — implicit attribute with a positional
64
- # argument (legacy syntax).
50
+ # - `name { "Alice" }` — implicit attribute via `method_missing` with a block (modern syntax).
51
+ # - `name "Alice"` — implicit attribute with a positional argument (legacy syntax).
65
52
  # - `add_attribute(:name) { "Alice" }` — explicit form.
66
53
  #
67
- # Sequences (`sequence(:email) { ... }`), associations
68
- # (`association :author`), traits (`trait :admin do ... end`),
69
- # and parent / child relationships (`factory :admin,
70
- # parent: :user do ... end`) are deferred to follow-up
71
- # slices. Factories whose name is a non-literal expression
72
- # (`factory FACTORY_NAME do ... end`) are silently skipped.
54
+ # Sequences (`sequence(:email) { ... }`), associations (`association :author`), traits (`trait :admin
55
+ # do ... end`), and parent / child relationships (`factory :admin, parent: :user do ... end`) are
56
+ # deferred to follow-up slices. Factories whose name is a non-literal expression (`factory
57
+ # FACTORY_NAME do ... end`) are silently skipped.
73
58
  class Factorybot < Rigor::Plugin::Base
74
59
  manifest(
75
60
  id: "factorybot",
@@ -96,18 +81,16 @@ module Rigor
96
81
  @factory_search_paths = Array(config.fetch("factory_search_paths")).map(&:to_s)
97
82
  end
98
83
 
99
- # ADR-37 — per-call factory/attribute validation over the
100
- # engine-owned walk. Each violation carries its own location
101
- # (the call's message_loc, or the offending attribute key), so it
102
- # is positioned via `diagnostic(node, location:)`. No file-level
103
- # diagnostic remains, so there is no `diagnostics_for_file`.
84
+ # ADR-37 — per-call factory/attribute validation over the engine-owned walk. Each violation carries
85
+ # its own location (the call's message_loc, or the offending attribute key), so it is positioned
86
+ # via `diagnostic(node, location:)`. No file-level diagnostic remains, so there is no
87
+ # `diagnostics_for_file`.
104
88
  node_rule Prism::CallNode do |node, _scope, path|
105
89
  index = producer_value(:factory_index)
106
90
  next [] if index.nil? || index.empty?
107
91
 
108
- # `:model_index` is rigor-activerecord's published fact (ADR-9);
109
- # nil when that plugin isn't loaded, in which case the analyzer
110
- # falls back to factory-attributes-only checking.
92
+ # `:model_index` is rigor-activerecord's published fact (ADR-9); nil when that plugin isn't
93
+ # loaded, in which case the analyzer falls back to factory-attributes-only checking.
111
94
  violations = Analyzer.violations_for(
112
95
  call_node: node, factory_index: index,
113
96
  model_index: read_fact(plugin_id: "activerecord", name: :model_index)
@@ -6,17 +6,13 @@ require "rigor/source/literals"
6
6
  module Rigor
7
7
  module Plugin
8
8
  class Graphql < Rigor::Plugin::Base
9
- # Walks project source for `class T < GraphQL::Schema::Object`
10
- # subclasses and emits a `{type_class_fqn => {field_name => {type:, nullable:}}}`
11
- # table covering every `field :name, Type, null: ...` declaration
12
- # inside the class body.
9
+ # Walks project source for `class T < GraphQL::Schema::Object` subclasses and emits a
10
+ # `{type_class_fqn => {field_name => {type:, nullable:}}}` table covering every `field :name, Type,
11
+ # null: ...` declaration inside the class body.
13
12
  module TypeScanner
14
- # The canonical GraphQL scalar type names accepted as
15
- # `field`'s second positional argument. The plugin maps
16
- # each to the underlying Ruby class name so downstream
17
- # consumers can cross-reference against Ruby types
18
- # without re-implementing the GraphQL→Ruby coercion
19
- # table.
13
+ # The canonical GraphQL scalar type names accepted as `field`'s second positional argument. The
14
+ # plugin maps each to the underlying Ruby class name so downstream consumers can cross-reference
15
+ # against Ruby types without re-implementing the GraphQL→Ruby coercion table.
20
16
  CANONICAL_TYPES = {
21
17
  "String" => "String",
22
18
  "Integer" => "Integer",
@@ -26,21 +22,17 @@ module Rigor
26
22
  "ID" => "String"
27
23
  }.freeze
28
24
 
29
- # The base class name a Schema::Object subclass MUST
30
- # inherit from to be recognised. Match is on the
31
- # rightmost segment of the superclass constant chain so
32
- # both `GraphQL::Schema::Object` and the locally-aliased
33
- # `BaseObject = GraphQL::Schema::Object` shape work when
34
- # the alias's RHS is the canonical path.
25
+ # The base class name a Schema::Object subclass MUST inherit from to be recognised. Match is on
26
+ # the rightmost segment of the superclass constant chain so both `GraphQL::Schema::Object` and
27
+ # the locally-aliased `BaseObject = GraphQL::Schema::Object` shape work when the alias's RHS is
28
+ # the canonical path.
35
29
  SCHEMA_OBJECT_TAIL = "Object"
36
30
  SCHEMA_ENUM_TAIL = "Enum"
37
31
  SCHEMA_INPUT_OBJECT_TAIL = "InputObject"
38
32
  SCHEMA_MUTATION_TAIL = "Mutation"
39
- # Common path-segment for `Schema::Object` / `Schema::Enum`
40
- # / `Schema::InputObject` / `Schema::Mutation`; the
41
- # second-to-last segment must be `Schema` (either
42
- # fully-qualified `GraphQL::Schema::X` or lexically nested
43
- # `Schema::X` inside `module GraphQL`).
33
+ # Common path-segment for `Schema::Object` / `Schema::Enum` / `Schema::InputObject` /
34
+ # `Schema::Mutation`; the second-to-last segment must be `Schema` (either fully-qualified
35
+ # `GraphQL::Schema::X` or lexically nested `Schema::X` inside `module GraphQL`).
44
36
  SCHEMA_PARENT_SEGMENTS = %w[Schema GraphQL].freeze
45
37
  private_constant :SCHEMA_OBJECT_TAIL, :SCHEMA_ENUM_TAIL,
46
38
  :SCHEMA_INPUT_OBJECT_TAIL, :SCHEMA_MUTATION_TAIL,
@@ -48,15 +40,12 @@ module Rigor
48
40
 
49
41
  module_function
50
42
 
51
- # @param paths [Array<String>] absolute paths to `.rb` files
52
- # the project's `paths:` resolves to.
53
- # @return [Hash{Symbol => Hash}] frozen 4-key result:
54
- # `:types` (per-`Schema::Object` field table),
55
- # `:enums` (per-`Schema::Enum` value list),
56
- # `:input_objects` (per-`Schema::InputObject` argument table),
57
- # `:mutations` (per-`Schema::Mutation` arguments+fields table).
58
- # Any subset may be empty when no recognisable declaration
59
- # of that kind is found.
43
+ # @param paths [Array<String>] absolute paths to `.rb` files the project's `paths:` resolves to.
44
+ # @return [Hash{Symbol => Hash}] frozen 4-key result: `:types` (per-`Schema::Object` field
45
+ # table), `:enums` (per-`Schema::Enum` value list), `:input_objects`
46
+ # (per-`Schema::InputObject` argument table), `:mutations` (per-`Schema::Mutation`
47
+ # arguments+fields table). Any subset may be empty when no recognisable declaration of that
48
+ # kind is found.
60
49
  def scan(paths:)
61
50
  acc = empty_accumulator
62
51
  paths.each do |path|
@@ -97,10 +86,9 @@ module Rigor
97
86
  end
98
87
  private_class_method :scan_file
99
88
 
100
- # Walks the AST collecting `class X < GraphQL::Schema::Object`,
101
- # `Schema::Enum`, `Schema::InputObject`, and `Schema::Mutation`
102
- # decls at any nesting level. Returns a 4-key hash so the
103
- # caller can publish multiple cross-plugin facts from one walk.
89
+ # Walks the AST collecting `class X < GraphQL::Schema::Object`, `Schema::Enum`,
90
+ # `Schema::InputObject`, and `Schema::Mutation` decls at any nesting level. Returns a 4-key hash
91
+ # so the caller can publish multiple cross-plugin facts from one walk.
104
92
  def collect_definitions(node, qualified_prefix)
105
93
  return empty_accumulator if node.nil?
106
94
 
@@ -154,10 +142,9 @@ module Rigor
154
142
  end
155
143
  private_class_method :collect_module_node
156
144
 
157
- # `class X < GraphQL::Schema::<Tail>` matches when the
158
- # superclass's last two path segments are `Schema::<Tail>`.
159
- # Matches both `< GraphQL::Schema::<Tail>` (fully qualified)
160
- # and `< Schema::<Tail>` (lexically inside `module GraphQL`).
145
+ # `class X < GraphQL::Schema::<Tail>` matches when the superclass's last two path segments are
146
+ # `Schema::<Tail>`. Matches both `< GraphQL::Schema::<Tail>` (fully qualified) and `<
147
+ # Schema::<Tail>` (lexically inside `module GraphQL`).
161
148
  def schema_subclass?(class_node, tail)
162
149
  superclass = class_node.superclass
163
150
  return false if superclass.nil?
@@ -193,19 +180,16 @@ module Rigor
193
180
  end
194
181
  private_class_method :statement_nodes
195
182
 
196
- # Walks every top-level `value "..."` call inside an
197
- # enum subclass body and returns the value names as an
198
- # Array<String>. Both shapes graphql-ruby accepts work:
183
+ # Walks every top-level `value "..."` call inside an enum subclass body and returns the value
184
+ # names as an Array<String>. Both shapes graphql-ruby accepts work:
199
185
  #
200
186
  # value "ACTIVE"
201
187
  # value "DISABLED", value: :off, description: "..."
202
188
  #
203
- # The first positional must be a String literal — the
204
- # graphql-ruby `value` API also accepts a Symbol form
205
- # (`value :ACTIVE`) but the documented idiom is String.
206
- # Only the GraphQL-side value name is stored; the optional
207
- # `value:` kwarg (Ruby-side override) and `description:`
208
- # are omitted from the published table.
189
+ # The first positional must be a String literal — the graphql-ruby `value` API also accepts a
190
+ # Symbol form (`value :ACTIVE`) but the documented idiom is String. Only the GraphQL-side value
191
+ # name is stored; the optional `value:` kwarg (Ruby-side override) and `description:` are
192
+ # omitted from the published table.
209
193
  def collect_values(body)
210
194
  return [] if body.nil?
211
195
 
@@ -220,13 +204,10 @@ module Rigor
220
204
  end
221
205
  private_class_method :collect_values
222
206
 
223
- # Walks every top-level `argument :name, Type, required: ...`
224
- # call inside an InputObject (or Mutation) subclass body and
225
- # returns the per-argument shape table. Argument syntax
226
- # mirrors `field` except the nullability axis is named
227
- # `required:` (default `false` — per graphql-ruby's
228
- # `argument` default; the OPPOSITE polarity of `field`'s
229
- # `null:`).
207
+ # Walks every top-level `argument :name, Type, required: ...` call inside an InputObject (or
208
+ # Mutation) subclass body and returns the per-argument shape table. Argument syntax mirrors
209
+ # `field` except the nullability axis is named `required:` (default `false` — per graphql-ruby's
210
+ # `argument` default; the OPPOSITE polarity of `field`'s `null:`).
230
211
  #
231
212
  # argument :name, String, required: true
232
213
  # argument :tags, [String], required: false
@@ -270,10 +251,9 @@ module Rigor
270
251
  end
271
252
  private_class_method :parse_argument_call
272
253
 
273
- # Mirror of `extract_nullability` but reads the `required:`
274
- # kwarg, defaulting to `false` (graphql-ruby's argument
275
- # default — the OPPOSITE polarity of `field`'s `null:` /
276
- # nullability default).
254
+ # Mirror of `extract_nullability` but reads the `required:` kwarg, defaulting to `false`
255
+ # (graphql-ruby's argument default — the OPPOSITE polarity of `field`'s `null:` / nullability
256
+ # default).
277
257
  # rubocop:disable Naming/PredicateMethod -- extractor returns the literal required value
278
258
  def extract_required_flag(args)
279
259
  kwargs = args.last
@@ -293,12 +273,10 @@ module Rigor
293
273
  # rubocop:enable Naming/PredicateMethod
294
274
  private_class_method :extract_required_flag
295
275
 
296
- # `field :name, Type, null: false` shape. The first
297
- # positional is a Symbol (field name); the second is a
298
- # constant reference (GraphQL type) OR a single-element
299
- # ArrayNode (`[Type]`) for GraphQL list types; `null:` is
300
- # the nullability keyword (defaults to TRUE per
301
- # graphql-ruby's field defaults so we mirror that).
276
+ # `field :name, Type, null: false` shape. The first positional is a Symbol (field name); the
277
+ # second is a constant reference (GraphQL type) OR a single-element ArrayNode (`[Type]`) for
278
+ # GraphQL list types; `null:` is the nullability keyword (defaults to TRUE per graphql-ruby's
279
+ # field defaults so we mirror that).
302
280
  def parse_field_call(node)
303
281
  args = node.arguments&.arguments
304
282
  return nil if args.nil? || args.size < 2
@@ -320,13 +298,10 @@ module Rigor
320
298
  end
321
299
  private_class_method :parse_field_call
322
300
 
323
- # Resolves the `Type` positional argument to a
324
- # `{type: "ClassName", list: bool}` tuple. ArrayNode
325
- # forms (`[String]` / `[Types::User]`) unwrap the single
326
- # element and mark `list: true`. Bare constant refs are
327
- # not lists. Returns nil for unrecognised shapes (string
328
- # types `"User"`, Proc lazy types, etc.) so callers drop
329
- # the field.
301
+ # Resolves the `Type` positional argument to a `{type: "ClassName", list: bool}` tuple.
302
+ # ArrayNode forms (`[String]` / `[Types::User]`) unwrap the single element and mark `list: true`.
303
+ # Bare constant refs are not lists. Returns nil for unrecognised shapes (string types `"User"`,
304
+ # Proc lazy types, etc.) so callers drop the field.
330
305
  def resolve_field_type(node)
331
306
  if node.is_a?(Prism::ArrayNode)
332
307
  element = node.elements.first
@@ -354,9 +329,8 @@ module Rigor
354
329
  end
355
330
  private_class_method :resolve_constant_type
356
331
 
357
- # Defaults to `true` (matches graphql-ruby's `field`
358
- # default nullability). Looks for an explicit `null:`
359
- # keyword and reads its boolean literal.
332
+ # Defaults to `true` (matches graphql-ruby's `field` default nullability). Looks for an explicit
333
+ # `null:` keyword and reads its boolean literal.
360
334
  # rubocop:disable Naming/PredicateMethod -- extractor returns the literal nullability value
361
335
  def extract_nullability(args)
362
336
  kwargs = args.last
@@ -376,9 +350,8 @@ module Rigor
376
350
  # rubocop:enable Naming/PredicateMethod
377
351
  private_class_method :extract_nullability
378
352
 
379
- # Returns the constant chain as an Array of String
380
- # segments (`["GraphQL", "Schema", "Object"]`). Empty
381
- # array for unrecognised node kinds.
353
+ # Returns the constant chain as an Array of String segments (`["GraphQL", "Schema", "Object"]`).
354
+ # Empty array for unrecognised node kinds.
382
355
  def constant_path_segments(node)
383
356
  case node
384
357
  when Prism::ConstantReadNode then [node.name.to_s]
@@ -397,9 +370,8 @@ module Rigor
397
370
  end
398
371
  private_class_method :constant_path_segments
399
372
 
400
- # Joined `::`-form of {.constant_path_segments}. Returns
401
- # nil for unrecognised node kinds (so callers can short-
402
- # circuit).
373
+ # Joined `::`-form of {.constant_path_segments}. Returns nil for unrecognised node kinds (so
374
+ # callers can short-circuit).
403
375
  def constant_name_for(node)
404
376
  segments = constant_path_segments(node)
405
377
  segments.empty? ? nil : segments.join("::")
@@ -12,51 +12,41 @@ module Rigor
12
12
  # [Rails plugins roadmap](../../../../../docs/design/20260508-rails-plugins-roadmap.md)
13
13
  # § "3D".
14
14
  #
15
- # Recognises `class T < GraphQL::Schema::Object` subclasses
16
- # and walks every `field :name, Type, null: false` declaration
17
- # inside, publishing the resulting field-type map as the
18
- # `:graphql_type_table` cross-plugin fact (ADR-9). The macro
19
- # expansion library survey at
20
- # [docs/notes/20260515-macro-expansion-library-survey.md](../../../../../docs/notes/20260515-macro-expansion-library-survey.md)
21
- # § "GraphQL-Ruby" documents WHY this is a pure metadata-recorder
22
- # plugin rather than an ADR-16 substrate consumer: graphql-ruby's
23
- # `field` DSL emits NO Ruby methods (it just records a
24
- # `Schema::Field` on the class's `own_fields`). The user writes
25
- # resolver methods themselves; rigor's value here is producing a
26
- # static type table downstream consumers can cross-reference.
15
+ # Recognises `class T < GraphQL::Schema::Object` subclasses and walks every `field :name, Type,
16
+ # null: false` declaration inside, publishing the resulting field-type map as the
17
+ # `:graphql_type_table` cross-plugin fact (ADR-9). The macro expansion library survey at
18
+ # docs/notes/20260515-macro-expansion-library-survey.md § "GraphQL-Ruby" documents WHY this is a
19
+ # pure metadata-recorder plugin rather than an ADR-16
20
+ # substrate consumer: graphql-ruby's `field` DSL emits NO Ruby methods (it just records a
21
+ # `Schema::Field` on the class's `own_fields`). The user writes resolver methods themselves; rigor's
22
+ # value here is producing a static type table downstream consumers can cross-reference.
27
23
  #
28
24
  # ## What downstream consumers DO with the published facts
29
25
  #
30
- # The tables are the substrate for two future capabilities
31
- # (demand-driven, not yet implemented):
26
+ # The tables are the substrate for two future capabilities (demand-driven, not yet implemented):
32
27
  #
33
- # - Resolver-method check: for each `field :name, Type` whose
34
- # `name` is also defined as a Ruby method on the class, verify
35
- # the method's return type matches `Type`'s underlying class.
36
- # - Schema-query result typing: a future `rigor-graphql-execute`
37
- # plugin could type `Schema.execute(query).to_h` against the
38
- # queried fields.
28
+ # - Resolver-method check: for each `field :name, Type` whose `name` is also defined as a Ruby method
29
+ # on the class, verify the method's return type matches `Type`'s underlying class.
30
+ # - Schema-query result typing: a future `rigor-graphql-execute` plugin could type
31
+ # `Schema.execute(query).to_h` against the queried fields.
39
32
  #
40
33
  # ## What's recognised
41
34
  #
42
- # - `class T < GraphQL::Schema::Object` subclasses (including
43
- # nested namespaces); `field :name, Type, null: ...`
44
- # declarations with constant-reference or list-array types
45
- # and GraphQL→Ruby scalar mapping.
35
+ # - `class T < GraphQL::Schema::Object` subclasses (including nested namespaces); `field :name, Type,
36
+ # null: ...` declarations with constant-reference or list-array types and GraphQL→Ruby scalar
37
+ # mapping.
46
38
  # - `class T < GraphQL::Schema::Enum`; `value "ACTIVE"` calls.
47
- # - `class T < GraphQL::Schema::InputObject` /
48
- # `GraphQL::Schema::Mutation`; `argument :name, Type,
39
+ # - `class T < GraphQL::Schema::InputObject` / `GraphQL::Schema::Mutation`; `argument :name, Type,
49
40
  # required: ...` declarations.
50
41
  # - No user-facing diagnostics yet.
51
42
  #
52
43
  # ## Deferred (demand-driven)
53
44
  #
54
45
  # - **`resolver:` / `mutation:` reroute** recognition.
55
- # - **String type expressions** (`field :foo, "User"`) — defeats
56
- # static resolution by design (graphql-ruby's `BuildType.parse_type`
57
- # constantizes at runtime); a future slice could surface these
58
- # as `graphql.string-type` `:info` diagnostics pointing the
59
- # user at the constant-reference form for static typing.
46
+ # - **String type expressions** (`field :foo, "User"`) — defeats static resolution by design
47
+ # (graphql-ruby's `BuildType.parse_type` constantizes at runtime); a future slice could surface
48
+ # these as `graphql.string-type` `:info` diagnostics pointing the user at the constant-reference
49
+ # form for static typing.
60
50
  class Graphql < Rigor::Plugin::Base
61
51
  manifest(
62
52
  id: "graphql",
@@ -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-graphql` 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/graphql.rb` performs at load time.
3
+ # Gem entry point. Required by Rigor's plugin loader when `.rigor.yml` lists `rigor-graphql` 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/graphql.rb` performs at load time.
8
6
  require_relative "rigor/plugin/graphql"
@@ -5,16 +5,12 @@ require "prism"
5
5
  module Rigor
6
6
  module Plugin
7
7
  class Hanami < Rigor::Plugin::Base
8
- # Checks the "presence" half of the Hanami::Action
9
- # ADR-28 protocol contract.
8
+ # Checks the "presence" half of the Hanami::Action ADR-28 protocol contract.
10
9
  #
11
- # Every class defined in a file matching a contract's
12
- # `path_glob` must declare `#handle(request, response)`.
13
- # The "provide" halfbinding `Hanami::Action::Request`
14
- # and `Hanami::Action::Response` into the method body
15
- # is handled engine-side by
16
- # `Inference::MethodParameterBinder`. Return type is
17
- # void so no conformance check is performed here.
10
+ # Every class defined in a file matching a contract's `path_glob` must declare `#handle(request,
11
+ # response)`. The "provide" half — binding `Hanami::Action::Request` and `Hanami::Action::Response`
12
+ # into the method body is handled engine-side by `Inference::MethodParameterBinder`. Return type
13
+ # is void so no conformance check is performed here.
18
14
  class ActionChecker
19
15
  FNMATCH_FLAGS = File::FNM_PATHNAME | File::FNM_EXTGLOB
20
16
 
@@ -22,11 +18,10 @@ module Rigor
22
18
  @contracts = contracts
23
19
  end
24
20
 
25
- # Per-`ClassNode` check over the engine-owned walk (ADR-37):
26
- # called once per class node the `node_rule` in `hanami.rb`
27
- # dispatches, against every contract whose `path_glob` matches
28
- # the file. No cross-class-node state is needed, so the checker
29
- # ships no `class_nodes` traversal of its own.
21
+ # Per-`ClassNode` check over the engine-owned walk (ADR-37): called once per class node the
22
+ # `node_rule` in `hanami.rb` dispatches, against every contract whose `path_glob` matches the
23
+ # file. No cross-class-node state is needed, so the checker ships no `class_nodes` traversal of
24
+ # its own.
30
25
  def check_class(class_node, path:)
31
26
  @contracts.filter_map do |contract|
32
27
  next unless path_matches?(contract.path_glob, path)
@@ -42,9 +37,8 @@ module Rigor
42
37
 
43
38
  private
44
39
 
45
- # Contract globs are project-root-relative; the analyzer
46
- # may supply a relative or absolute path, so the glob is
47
- # matched both directly and with a `**/`-prefixed suffix.
40
+ # Contract globs are project-root-relative; the analyzer may supply a relative or absolute path,
41
+ # so the glob is matched both directly and with a `**/`-prefixed suffix.
48
42
  def path_matches?(glob, path)
49
43
  return false if path.nil?
50
44
 
@@ -9,37 +9,28 @@ module Rigor
9
9
  module Plugin
10
10
  # rigor-hanami — enforces the Hanami::Action protocol.
11
11
  #
12
- # Hanami 3.x actions inherit from `Hanami::Action` and
13
- # define a single entry-point:
12
+ # Hanami 3.x actions inherit from `Hanami::Action` and define a single entry-point:
14
13
  #
15
14
  # def handle(request, response)
16
15
  # response.status = 200
17
16
  # response.body = "Hello"
18
17
  # end
19
18
  #
20
- # The method is void — the response is mutated in-place;
21
- # the return value is discarded by the framework.
19
+ # The method is void — the response is mutated in-place; the return value is discarded by the
20
+ # framework.
22
21
  #
23
- # This plugin uses the **ADR-28 path-scoped
24
- # method-protocol contract** to:
22
+ # This plugin uses the **ADR-28 path-scoped method-protocol contract** to:
25
23
  #
26
- # 1. **Provide** `Hanami::Action::Request` and
27
- # `Hanami::Action::Response` as the types of the
28
- # first and second parameters of every `#handle`
29
- # method defined inside `app/actions/**/*.rb`.
30
- # The engine substitutes these types for the usual
31
- # `Dynamic[Top]` fallback, so misuse of `request` or
32
- # `response` inside an action body surfaces as a
33
- # core diagnostic.
24
+ # 1. **Provide** `Hanami::Action::Request` and `Hanami::Action::Response` as the types of the first
25
+ # and second parameters of every `#handle` method defined inside `app/actions/**/*.rb`. The
26
+ # engine substitutes these types for the usual `Dynamic[Top]` fallback, so misuse of `request` or
27
+ # `response` inside an action body surfaces as a core diagnostic.
34
28
  #
35
- # 2. **Check** that every class under `app/actions/`
36
- # defines `#handle`. Missing definitions emit
29
+ # 2. **Check** that every class under `app/actions/` defines `#handle`. Missing definitions emit
37
30
  # `missing-handle-method`.
38
31
  #
39
- # Return-type conformance is NOT checked — Hanami
40
- # actions are void by contract, and checking a void
41
- # return would produce false positives on every `if`
42
- # branch that doesn't end with an explicit `nil`.
32
+ # Return-type conformance is NOT checked — Hanami actions are void by contract, and checking a void
33
+ # return would produce false positives on every `if` branch that doesn't end with an explicit `nil`.
43
34
  #
44
35
  # ## Configuration
45
36
  #
@@ -55,10 +46,8 @@ module Rigor
55
46
  # | action class defines no `#handle` | `:error` | `missing-handle-method` |
56
47
  # | `request` / `response` misuse in body | core engine diagnostic |
57
48
  #
58
- # A misused `request` or `response` (e.g.
59
- # `request.no_such_method`) surfaces as a standard
60
- # engine `call.undefined-method` diagnostic once the
61
- # plugin provides the parameter types.
49
+ # A misused `request` or `response` (e.g. `request.no_such_method`) surfaces as a standard engine
50
+ # `call.undefined-method` diagnostic once the plugin provides the parameter types.
62
51
  class Hanami < Rigor::Plugin::Base
63
52
  manifest(
64
53
  id: "hanami",
@@ -91,20 +80,17 @@ module Rigor
91
80
  end
92
81
  end
93
82
 
94
- # ADR-28: override so the per-project `action_path` config
95
- # reaches both the engine's parameter-provision tier and
96
- # this plugin's check half.
83
+ # ADR-28: override so the per-project `action_path` config reaches both the engine's
84
+ # parameter-provision tier and this plugin's check half.
97
85
  def protocol_contracts
98
86
  @protocol_contracts || manifest.protocol_contracts
99
87
  end
100
88
 
101
- # ADR-37 — per-class-node validation over the engine-owned walk.
102
- # Each `Prism::ClassNode` is checked against every contract
103
- # independently, so the plugin no longer ships its own `class_nodes`
104
- # traversal; `ActionChecker#check_class` keeps the per-class
105
- # contract logic. (A per-class contract check is exactly what
106
- # `node_rule` is for — the return type is void, so no `scope`
107
- # query is needed.)
89
+ # ADR-37 — per-class-node validation over the engine-owned walk. Each `Prism::ClassNode` is checked
90
+ # against every contract independently, so the plugin no longer ships its own `class_nodes`
91
+ # traversal; `ActionChecker#check_class` keeps the per-class contract logic. (A per-class contract
92
+ # check is exactly what `node_rule` is for — the return type is void, so no `scope` query is
93
+ # needed.)
108
94
  node_rule Prism::ClassNode do |node, _scope, path|
109
95
  contracts = protocol_contracts
110
96
  next [] if contracts.empty?