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,35 +8,26 @@ require_relative "sig_parser"
8
8
  module Rigor
9
9
  module Plugin
10
10
  class Sorbet < Rigor::Plugin::Base
11
- # Walks a parsed Prism program looking for
12
- # `sig { ... }` / `sig do ... end` calls that immediately
13
- # precede a `def` (or a `def self.foo` / `class << self;
14
- # def foo; end`). Each recognised pair is parsed by
15
- # {SigParser} and recorded in the {Catalog} under its
16
- # qualified `(class_name, method_name, kind)` key.
11
+ # Walks a parsed Prism program looking for `sig { ... }` / `sig do ... end` calls that immediately
12
+ # precede a `def` (or a `def self.foo` / `class << self; def foo; end`). Each recognised pair is
13
+ # parsed by {SigParser} and recorded in the {Catalog} under its qualified `(class_name, method_name,
14
+ # kind)` key.
17
15
  #
18
- # Anything we don't recognise (a stray `sig { ... }` not
19
- # followed by a `def`, a `def` with no preceding `sig`,
20
- # malformed sig blocks, etc.) is reported back to the
21
- # caller through `parse_errors:` so the plugin can emit a
22
- # `plugin.sorbet.parse-error` diagnostic. Walking is
23
- # otherwise infallible — a bad sig block does not abort
24
- # the catalog build for the rest of the file.
16
+ # Anything we don't recognise (a stray `sig { ... }` not followed by a `def`, a `def` with no
17
+ # preceding `sig`, malformed sig blocks, etc.) is reported back to the caller through
18
+ # `parse_errors:` so the plugin can emit a `plugin.sorbet.parse-error` diagnostic. Walking is
19
+ # otherwise infallible a bad sig block does not abort the catalog build for the rest of the file.
25
20
  module CatalogWalker
26
- # Detected error during walking. `kind` is one of:
27
- # `:no_block` / `:empty_block` / `:missing_returns_or_void`
28
- # / `:duplicate_sig` / `:dangling_sig`.
21
+ # Detected error during walking. `kind` is one of: `:no_block` / `:empty_block` /
22
+ # `:missing_returns_or_void` / `:duplicate_sig` / `:dangling_sig`.
29
23
  ParseError = Data.define(:kind, :node, :path)
30
24
 
31
25
  module_function
32
26
 
33
27
  # @param root [Prism::Node] the file's program node.
34
- # @param catalog [Catalog] mutable; signatures are
35
- # recorded into it.
36
- # @param path [String] file path used for diagnostic
37
- # provenance.
38
- # @return [Array<ParseError>] errors observed during the
39
- # walk; empty when the file is sig-clean.
28
+ # @param catalog [Catalog] mutable; signatures are recorded into it.
29
+ # @param path [String] file path used for diagnostic provenance.
30
+ # @return [Array<ParseError>] errors observed during the walk; empty when the file is sig-clean.
40
31
  def walk(root:, catalog:, path:)
41
32
  state = State.new(catalog: catalog, path: path)
42
33
  walk_node(root, state, lexical_path: [], in_singleton_class: false)
@@ -64,9 +55,8 @@ module Rigor
64
55
  when Prism::StatementsNode
65
56
  walk_statements(node, state, lexical_path: lexical_path, in_singleton_class: in_singleton_class)
66
57
  when Prism::DefNode
67
- # A `def` not preceded by a `sig` is fine; we just
68
- # don't record anything for it. The interesting case
69
- # is in `walk_statements`, which pairs sig+def.
58
+ # A `def` not preceded by a `sig` is fine; we just don't record anything for it. The
59
+ # interesting case is in `walk_statements`, which pairs sig+def.
70
60
  else
71
61
  node.compact_child_nodes.each do |child|
72
62
  walk_node(child, state, lexical_path: lexical_path, in_singleton_class: in_singleton_class)
@@ -92,13 +82,10 @@ module Rigor
92
82
  end
93
83
  end
94
84
 
95
- # The pair-finding loop. Walks a `StatementsNode`'s
96
- # children left-to-right; when it encounters a `sig`
97
- # call, it remembers it and consumes the very next
98
- # `def` / `def self.foo` as the target. Anything between
99
- # a sig and its def (a comment is fine — comments aren't
100
- # AST nodes — but a method call would be a problem)
101
- # leaves the sig dangling.
85
+ # The pair-finding loop. Walks a `StatementsNode`'s children left-to-right; when it encounters a
86
+ # `sig` call, it remembers it and consumes the very next `def` / `def self.foo` as the target.
87
+ # Anything between a sig and its def (a comment is fine — comments aren't AST nodes — but a
88
+ # method call would be a problem) leaves the sig dangling.
102
89
  def walk_statements(statements, state, lexical_path:, in_singleton_class:)
103
90
  pending_sig = nil
104
91
 
@@ -107,18 +94,15 @@ module Rigor
107
94
  record_def_with_sig(child, pending_sig, state, lexical_path, in_singleton_class)
108
95
  pending_sig = nil
109
96
  elsif pending_sig && attr_macro_call?(child)
110
- # `sig { returns(T) }` above `attr_reader :name` /
111
- # `attr_accessor` / `attr_writer` types the generated
112
- # accessor(s). This is a first-class Sorbet idiom — not
113
- # a dangling sig — so record the accessor signature(s)
114
- # instead of warning.
97
+ # `sig { returns(T) }` above `attr_reader :name` / `attr_accessor` / `attr_writer` types
98
+ # the generated accessor(s). This is a first-class Sorbet idiom — not a dangling sig — so
99
+ # record the accessor signature(s) instead of warning.
115
100
  record_attr_with_sig(child, pending_sig, state, lexical_path, in_singleton_class)
116
101
  pending_sig = nil
117
102
  elsif pending_sig && (inner_def = visibility_wrapped_def(child))
118
- # `sig { ... }` above `private def foo` /
119
- # `private_class_method def self.foo` / `module_function
120
- # def bar` a visibility macro wrapping the very def the
121
- # sig types. Record the inner def, not a dangling sig.
103
+ # `sig { ... }` above `private def foo` / `private_class_method def self.foo` /
104
+ # `module_function def bar` a visibility macro wrapping the very def the sig types.
105
+ # Record the inner def, not a dangling sig.
122
106
  record_def_with_sig(inner_def, pending_sig, state, lexical_path, in_singleton_class)
123
107
  pending_sig = nil
124
108
  elsif sig_call?(child)
@@ -129,10 +113,8 @@ module Rigor
129
113
  state.record_error(:dangling_sig, pending_sig)
130
114
  pending_sig = nil
131
115
  end
132
- # ADR-11 slice 8 — record `include` / `extend`
133
- # declarations alongside the regular walk so the
134
- # plugin's chain lookup can lift sigs declared
135
- # on a mixed-in module to the host class.
116
+ # ADR-11 slice 8 — record `include` / `extend` declarations alongside the regular walk so
117
+ # the plugin's chain lookup can lift sigs declared on a mixed-in module to the host class.
136
118
  record_mixin_call(child, state, lexical_path) if mixin_call?(child) && !in_singleton_class
137
119
  walk_node(child, state, lexical_path: lexical_path, in_singleton_class: in_singleton_class)
138
120
  end
@@ -141,12 +123,9 @@ module Rigor
141
123
  state.record_error(:dangling_sig, pending_sig) if pending_sig
142
124
  end
143
125
 
144
- # `include Foo` / `extend Foo` / `include Foo, Bar` —
145
- # the `include` and `extend` mixin macros that Tapioca-
146
- # generated DSL RBIs depend on. Recognised when the
147
- # call has no explicit receiver (top-level inside a
148
- # class body) and every argument is a constant
149
- # reference.
126
+ # `include Foo` / `extend Foo` / `include Foo, Bar` — the `include` and `extend` mixin macros that
127
+ # Tapioca-generated DSL RBIs depend on. Recognised when the call has no explicit receiver
128
+ # (top-level inside a class body) and every argument is a constant reference.
150
129
  def mixin_call?(node)
151
130
  return false unless node.is_a?(Prism::CallNode)
152
131
  return false unless %i[include extend].include?(node.name)
@@ -191,12 +170,10 @@ module Rigor
191
170
  private_class_method public_class_method module_function
192
171
  ].freeze
193
172
 
194
- # `private def foo` / `private_class_method def self.bar` /
195
- # `module_function def baz` a receiverless visibility macro
196
- # whose single argument is the `def` it wraps. Returns the
197
- # inner `Prism::DefNode` (so a preceding sig types it), or nil.
198
- # The def's own receiver still drives instance-vs-singleton, so
199
- # `private_class_method def self.bar` records as singleton.
173
+ # `private def foo` / `private_class_method def self.bar` / `module_function def baz` — a
174
+ # receiverless visibility macro whose single argument is the `def` it wraps. Returns the inner
175
+ # `Prism::DefNode` (so a preceding sig types it), or nil. The def's own receiver still drives
176
+ # instance-vs-singleton, so `private_class_method def self.bar` records as singleton.
200
177
  def visibility_wrapped_def(node)
201
178
  return nil unless node.is_a?(Prism::CallNode) && node.receiver.nil?
202
179
  return nil unless VISIBILITY_MACROS.include?(node.name)
@@ -209,9 +186,8 @@ module Rigor
209
186
 
210
187
  ATTR_MACROS = %i[attr_reader attr_writer attr_accessor].freeze
211
188
 
212
- # `attr_reader :a, :b` / `attr_writer` / `attr_accessor` —
213
- # a receiverless attribute macro whose arguments are all
214
- # symbol literals (the only shape a preceding `sig` types).
189
+ # `attr_reader :a, :b` / `attr_writer` / `attr_accessor` — a receiverless attribute macro whose
190
+ # arguments are all symbol literals (the only shape a preceding `sig` types).
215
191
  def attr_macro_call?(node)
216
192
  return false unless node.is_a?(Prism::CallNode) && node.receiver.nil?
217
193
  return false unless ATTR_MACROS.include?(node.name)
@@ -220,11 +196,9 @@ module Rigor
220
196
  !args.nil? && !args.empty? && args.all?(Prism::SymbolNode)
221
197
  end
222
198
 
223
- # Records the method signature(s) a `sig` contributes to the
224
- # accessor(s) it precedes: the reader `name` and/or the
225
- # writer `name=`, each carrying the sig's `returns(...)`
226
- # type. `attr_reader` emits the reader, `attr_writer` the
227
- # writer, `attr_accessor` both — for every symbol argument.
199
+ # Records the method signature(s) a `sig` contributes to the accessor(s) it precedes: the reader
200
+ # `name` and/or the writer `name=`, each carrying the sig's `returns(...)` type. `attr_reader`
201
+ # emits the reader, `attr_writer` the writer, `attr_accessor` both — for every symbol argument.
228
202
  def record_attr_with_sig(attr_node, sig_call, state, lexical_path, in_singleton_class)
229
203
  parsed = SigParser.parse(sig_call)
230
204
  if parsed.is_a?(SigParser::ParseError)
@@ -273,10 +247,8 @@ module Rigor
273
247
  in_singleton_class || def_node.receiver.is_a?(Prism::SelfNode)
274
248
  end
275
249
 
276
- # Resolves a constant-path node (`Foo::Bar`,
277
- # `::Foo::Bar`) to its dot-separated name. Returns nil
278
- # for the rare dynamic-prefix shape so the walker
279
- # doesn't guess a qualified name in that case.
250
+ # Resolves a constant-path node (`Foo::Bar`, `::Foo::Bar`) to its dot-separated name. Returns nil
251
+ # for the rare dynamic-prefix shape so the walker doesn't guess a qualified name in that case.
280
252
  def qualified_name_for(node)
281
253
  case node
282
254
  when Prism::ConstantReadNode then node.name.to_s
@@ -3,19 +3,15 @@
3
3
  module Rigor
4
4
  module Plugin
5
5
  class Sorbet < Rigor::Plugin::Base
6
- # Frozen description of one Sorbet `sig` block as parsed by
7
- # {SigParser}. Holds the return type, parameter shape, and
8
- # modifier list. Call-site argument-type checking and
9
- # override-compatibility validation are deferred; only the
10
- # return-type contribution is active.
6
+ # Frozen description of one Sorbet `sig` block as parsed by {SigParser}. Holds the return type,
7
+ # parameter shape, and modifier list. Call-site argument-type checking and override-compatibility
8
+ # validation are deferred; only the return-type contribution is active.
11
9
  #
12
- # `kind` distinguishes `def foo` (`:instance`) from
13
- # `def self.foo` / `class << self; def foo; end`
10
+ # `kind` distinguishes `def foo` (`:instance`) from `def self.foo` / `class << self; def foo; end`
14
11
  # (`:singleton`).
15
12
  #
16
- # `modifiers` records the `sig`-level flags observed:
17
- # `:abstract`, `:override`, `:overridable`, `:final`.
18
- # Currently stored but not acted on.
13
+ # `modifiers` records the `sig`-level flags observed: `:abstract`, `:override`, `:overridable`,
14
+ # `:final`. Currently stored but not acted on.
19
15
  MethodSignature = Data.define(
20
16
  :class_name, :method_name, :kind, :params, :return_type, :modifiers
21
17
  )
@@ -7,10 +7,8 @@ require_relative "type_translator"
7
7
  module Rigor
8
8
  module Plugin
9
9
  class Sorbet < Rigor::Plugin::Base
10
- # Mini-interpreter for the chained-call expression that
11
- # makes up a Sorbet `sig` block. The block body is always
12
- # a single expression — Sorbet's docs (`sigs.md`) show the
13
- # full grammar:
10
+ # Mini-interpreter for the chained-call expression that makes up a Sorbet `sig` block. The block
11
+ # body is always a single expression — Sorbet's docs (`sigs.md`) show the full grammar:
14
12
  #
15
13
  # sig { params(x: T, y: T).returns(U) }
16
14
  # sig { void }
@@ -22,26 +20,20 @@ module Rigor
22
20
  # .returns(...)
23
21
  # end
24
22
  #
25
- # The parser walks the chain right-to-left, gathering
26
- # whatever it recognises (`params` / `returns` / `void` /
27
- # `abstract` / `override` / `overridable` / `final` /
28
- # `type_parameters` / `checked` / `on_failure`) into a
29
- # frozen result hash stored in {MethodSignature}. Modifiers
30
- # and `type_parameters` are recorded but not yet acted on.
23
+ # The parser walks the chain right-to-left, gathering whatever it recognises (`params` / `returns` /
24
+ # `void` / `abstract` / `override` / `overridable` / `final` / `type_parameters` / `checked` /
25
+ # `on_failure`) into a frozen result hash stored in {MethodSignature}. Modifiers and
26
+ # `type_parameters` are recorded but not yet acted on.
31
27
  #
32
- # The parser is intentionally tolerant — unknown chain
33
- # nodes degrade to "the rest of the chain is opaque" rather
34
- # than raising. The plugin emits a diagnostic
35
- # (`plugin.sorbet.parse-error`) only when the entire chain
36
- # fails to yield either a `returns` or a `void`.
28
+ # The parser is intentionally tolerant — unknown chain nodes degrade to "the rest of the chain is
29
+ # opaque" rather than raising. The plugin emits a diagnostic (`plugin.sorbet.parse-error`) only when
30
+ # the entire chain fails to yield either a `returns` or a `void`.
37
31
  module SigParser
38
- # Modifiers we recognise at any position in the chain.
39
- # Stored in `:modifiers` on the parse result.
32
+ # Modifiers we recognise at any position in the chain. Stored in `:modifiers` on the parse result.
40
33
  RECOGNISED_MODIFIERS = %i[abstract override overridable final].freeze
41
34
 
42
- # Sorbet runtime-only chain steps. Recognised so the
43
- # parser doesn't degrade the whole sig when it sees them,
44
- # but their payload is intentionally discarded.
35
+ # Sorbet runtime-only chain steps. Recognised so the parser doesn't degrade the whole sig when it
36
+ # sees them, but their payload is intentionally discarded.
45
37
  RUNTIME_ONLY_STEPS = %i[checked on_failure].freeze
46
38
 
47
39
  ParseResult = Data.define(:return_type, :params, :modifiers, :void) do
@@ -52,8 +44,7 @@ module Rigor
52
44
 
53
45
  module_function
54
46
 
55
- # @param sig_call [Prism::CallNode] the `sig { ... }` /
56
- # `sig do ... end` call.
47
+ # @param sig_call [Prism::CallNode] the `sig { ... }` / `sig do ... end` call.
57
48
  # @return [ParseResult, ParseError]
58
49
  def parse(sig_call)
59
50
  return ParseError.new(reason: :no_block, node: sig_call) if sig_call.block.nil?
@@ -72,11 +63,9 @@ module Rigor
72
63
  end
73
64
  end
74
65
 
75
- # Walks the chain bottom-up. Each chain link is a
76
- # `Prism::CallNode` whose receiver is the next link;
77
- # `params` / `returns` / `void` may appear at any
78
- # position, so we accumulate their effect into a
79
- # mutable hash and freeze on the way out.
66
+ # Walks the chain bottom-up. Each chain link is a `Prism::CallNode` whose receiver is the next
67
+ # link; `params` / `returns` / `void` may appear at any position, so we accumulate their effect
68
+ # into a mutable hash and freeze on the way out.
80
69
  def fold_chain(node, sig_call)
81
70
  accumulator = { return_type: nil, params: {}, modifiers: [], void: false, terminus_kind: nil }
82
71
  current = node
@@ -92,15 +81,13 @@ module Rigor
92
81
  when :params
93
82
  accumulator[:params].merge!(parse_params(current))
94
83
  when :type_parameters
95
- # Recognised to suppress the degraded path;
96
- # payload intentionally discarded (deferred).
84
+ # Recognised to suppress the degraded path; payload intentionally discarded (deferred).
97
85
  when *RECOGNISED_MODIFIERS
98
86
  accumulator[:modifiers] << current.name
99
87
  when *RUNTIME_ONLY_STEPS
100
88
  # Discard payload; runtime-only.
101
89
  else
102
- # Unknown chain link — stop folding and treat
103
- # whatever we accumulated so far as the result.
90
+ # Unknown chain link — stop folding and treat whatever we accumulated so far as the result.
104
91
  break
105
92
  end
106
93
  current = current.receiver
@@ -116,20 +103,16 @@ module Rigor
116
103
  )
117
104
  end
118
105
 
119
- # `void` and `returns(T)` share the slot; if both are
120
- # present (unusual but parseable), `returns(T)` wins
121
- # because Sorbet's static side treats `void` as
122
- # "discard the value" — when the user explicitly named
123
- # `T`, that's the more informative shape.
106
+ # `void` and `returns(T)` share the slot; if both are present (unusual but parseable), `returns(T)`
107
+ # wins because Sorbet's static side treats `void` as "discard the value" — when the user
108
+ # explicitly named `T`, that's the more informative shape.
124
109
  def resolve_return_type(accumulator)
125
110
  accumulator[:return_type] || Rigor::Type::Combinator.untyped
126
111
  end
127
112
 
128
- # `params(x: Integer, y: T.nilable(String))` — extracts
129
- # the `KeywordHashNode` AST and translates each value.
130
- # The result is `{ Symbol => Rigor::Type }`. Splat /
131
- # double-splat / unrecognised keys degrade silently
132
- # (slice 1 behaviour).
113
+ # `params(x: Integer, y: T.nilable(String))` — extracts the `KeywordHashNode` AST and translates
114
+ # each value. The result is `{ Symbol => Rigor::Type }`. Splat / double-splat / unrecognised keys
115
+ # degrade silently (slice 1 behaviour).
133
116
  def parse_params(call_node)
134
117
  args = call_node.arguments&.arguments || []
135
118
  first = args.first
@@ -3,49 +3,36 @@
3
3
  module Rigor
4
4
  module Plugin
5
5
  class Sorbet < Rigor::Plugin::Base
6
- # Reads Sorbet's `# typed: <level>` magic comment from the
7
- # head of a file. Sorbet's own contract (per
8
- # [`static.md`](https://sorbet.org/docs/static)) requires
9
- # the sigil to appear at the top of the file before any
10
- # Ruby code. We're slightly more lenient here — the sigil
11
- # may appear after a few comment / blank lines (matching
12
- # what Sorbet itself accepts in practice) but we stop
13
- # scanning once we hit a non-comment, non-blank line.
6
+ # Reads Sorbet's `# typed: <level>` magic comment from the head of a file. Sorbet's own contract (per
7
+ # [`static.md`](https://sorbet.org/docs/static)) requires the sigil to appear at the top of the file
8
+ # before any Ruby code. We're slightly more lenient here — the sigil may appear after a few comment
9
+ # / blank lines (matching what Sorbet itself accepts in practice) but we stop scanning once we hit a
10
+ # non-comment, non-blank line.
14
11
  #
15
- # Recognised levels: `:ignore` / `:false` / `:true` /
16
- # `:strict` / `:strong`. Falls back to `:false` (Sorbet's
17
- # default) when no sigil is present, matching how Sorbet
18
- # treats sigil-less files.
12
+ # Recognised levels: `:ignore` / `:false` / `:true` / `:strict` / `:strong`. Falls back to `:false`
13
+ # (Sorbet's default) when no sigil is present, matching how Sorbet treats sigil-less files.
19
14
  #
20
- # ADR-11 slice 5 uses this at catalog-harvest time:
21
- # `# typed: ignore` files are skipped entirely; other
22
- # levels gate both sig contributions (`:true`/`:strict`/
23
- # `:strong` only) and per-call assertion recognition
24
- # (`T.let` / `T.cast` / `T.must` / etc.) via
25
- # `Sorbet#assertion_enforced_here?`.
15
+ # ADR-11 slice 5 uses this at catalog-harvest time: `# typed: ignore` files are skipped entirely;
16
+ # other levels gate both sig contributions (`:true`/`:strict`/`:strong` only) and per-call assertion
17
+ # recognition (`T.let` / `T.cast` / `T.must` / etc.) via `Sorbet#assertion_enforced_here?`.
26
18
  module SigilDetector
27
- # Sorbet's strictness-level names. Stored as symbols to
28
- # match the analyzer's existing convention for level
29
- # identifiers; the `:true` / `:false` symbols here are
30
- # level *names* (the textual sigil values) and are
31
- # intentionally distinct from the `true` / `false`
32
- # boolean literals.
19
+ # Sorbet's strictness-level names. Stored as symbols to match the analyzer's existing convention
20
+ # for level identifiers; the `:true` / `:false` symbols here are level *names* (the textual sigil
21
+ # values) and are intentionally distinct from the `true` / `false` boolean literals.
33
22
  VALID_LEVELS = %i[ignore false true strict strong].freeze
34
23
  DEFAULT_LEVEL = :false # rubocop:disable Lint/BooleanSymbol
35
24
  SIGIL_REGEX = /\A\s*#\s*typed\s*:\s*(ignore|false|true|strict|strong)\s*\z/
36
25
 
37
- # Cap on how many lines we scan before giving up. Sorbet
38
- # doesn't formally specify a cap, but the sigil
39
- # convention is "near the top of the file"; 10 lines is
40
- # generous and bounds the parse cost on enormous files.
26
+ # Cap on how many lines we scan before giving up. Sorbet doesn't formally specify a cap, but the
27
+ # sigil convention is "near the top of the file"; 10 lines is generous and bounds the parse cost
28
+ # on enormous files.
41
29
  MAX_HEAD_LINES = 10
42
30
 
43
31
  module_function
44
32
 
45
33
  # @param contents [String] raw file contents.
46
- # @return [Symbol] one of {VALID_LEVELS}; defaults to
47
- # {DEFAULT_LEVEL} for sigil-less or malformed-sigil
48
- # files.
34
+ # @return [Symbol] one of {VALID_LEVELS}; defaults to {DEFAULT_LEVEL} for sigil-less or
35
+ # malformed-sigil files.
49
36
  def detect(contents)
50
37
  return DEFAULT_LEVEL if contents.nil? || contents.empty?
51
38
 
@@ -57,9 +44,8 @@ module Rigor
57
44
 
58
45
  match = SIGIL_REGEX.match(stripped)
59
46
  return match[1].to_sym if match
60
- # First non-blank line that isn't a sigil-shaped
61
- # comment ends the scan: Sorbet's parser stops at
62
- # the first directive-or-code line.
47
+ # First non-blank line that isn't a sigil-shaped comment ends the scan: Sorbet's parser stops
48
+ # at the first directive-or-code line.
63
49
  break unless stripped.start_with?("#")
64
50
  end
65
51
 
@@ -67,27 +53,20 @@ module Rigor
67
53
  end
68
54
 
69
55
  # @param level [Symbol]
70
- # @return [Boolean] true when `# typed: ignore`. The
71
- # harvest pipeline calls this to short-circuit
56
+ # @return [Boolean] true when `# typed: ignore`. The harvest pipeline calls this to short-circuit
72
57
  # walking the file's AST.
73
58
  def ignored?(level)
74
59
  level == :ignore
75
60
  end
76
61
 
77
- # @return [Boolean] true when `level` is at or above the
78
- # `# typed: true` mark. Used by the
79
- # `enforce_sigil` config gate (default `true`): with
80
- # the gate on, only files marked `:true` / `:strict` /
81
- # `:strong` contribute their sigs to the catalog. The
82
- # `:false` (and sigil-less) levels still get walked
83
- # (so RBI files outside the project can be loaded
84
- # regardless), but their sig-derived narrowing is
85
- # suppressed — matching how Sorbet itself only
86
- # enforces type errors at `# typed: true`+. Assertion
87
- # recognisers (`T.let` / `T.cast` / `T.must` /
88
- # `T.bind` / `T.assert_type!`) are NOT gated by this:
89
- # the user wrote them deliberately, so the
90
- # recogniser still fires regardless of sigil.
62
+ # @return [Boolean] true when `level` is at or above the `# typed: true` mark. Used by the
63
+ # `enforce_sigil` config gate (default `true`): with the gate on, only files marked `:true` /
64
+ # `:strict` / `:strong` contribute their sigs to the catalog. The `:false` (and sigil-less)
65
+ # levels still get walked (so RBI files outside the project can be loaded regardless), but their
66
+ # sig-derived narrowing is suppressed matching how Sorbet itself only enforces type errors at
67
+ # `# typed: true`+. Assertion recognisers (`T.let` / `T.cast` / `T.must` / `T.bind` /
68
+ # `T.assert_type!`) are NOT gated by this: the user wrote them deliberately, so the recogniser
69
+ # still fires regardless of sigil.
91
70
  def enforced?(level)
92
71
  %i[true strict strong].include?(level)
93
72
  end