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
@@ -7,17 +7,13 @@ require_relative "buffer_resolution"
7
7
 
8
8
  module Rigor
9
9
  module LanguageServer
10
- # Answers `textDocument/foldingRange` requests. Walks the Prism
11
- # AST and emits one `FoldingRange` per foldable construct:
12
- # `class` / `module` / `def` / `singleton class << self` /
13
- # block (`do…end` or `{…}`). Skips single-line constructs
14
- # (start_line == end_line) since there's nothing to fold.
10
+ # Answers `textDocument/foldingRange` requests. Walks the Prism AST and emits one `FoldingRange` per
11
+ # foldable construct: `class` / `module` / `def` / `singleton class << self` / block (`do…end` or `{…}`).
12
+ # Skips single-line constructs (start_line == end_line) since there's nothing to fold.
15
13
  #
16
- # Ranges are LSP 0-based. `startLine` is the line containing
17
- # the opening keyword (`class`, `def`); `endLine` is the last
18
- # line OF the body — one line before the `end` keyword — so
19
- # collapsed view shows the opener intact and hides the body
20
- # only.
14
+ # Ranges are LSP 0-based. `startLine` is the line containing the opening keyword (`class`, `def`); `endLine`
15
+ # is the last line OF the body — one line before the `end` keyword — so collapsed view shows the opener
16
+ # intact and hides the body only.
21
17
  class FoldingRangeProvider
22
18
  include BufferResolution
23
19
 
@@ -34,9 +30,8 @@ module Rigor
34
30
 
35
31
  parse_result = Prism.parse(entry.bytes, filepath: path,
36
32
  version: @project_context.configuration.target_ruby)
37
- # Tolerate partial parse errors — fold whatever AST Prism
38
- # produced. Editors prefer a stale outline / fold map
39
- # over none.
33
+ # Tolerate partial parse errors — fold whatever AST Prism produced. Editors prefer a stale outline /
34
+ # fold map over none.
40
35
  ranges = []
41
36
  walk(parse_result.value, ranges)
42
37
  ranges
@@ -59,12 +54,9 @@ module Rigor
59
54
  def add_range(node, ranges)
60
55
  loc = node.location
61
56
  start_line = loc.start_line - 1
62
- # `end_line` includes the line of the `end` keyword.
63
- # Folding should hide the body and leave both the opener
64
- # AND the `end` keyword visible so endLine is one line
65
- # before `end`. When the body is a single line, that
66
- # makes start == end (one-line fold), which most editors
67
- # skip; we filter those out.
57
+ # `end_line` includes the line of the `end` keyword. Folding should hide the body and leave both the
58
+ # opener AND the `end` keyword visible so endLine is one line before `end`. When the body is a single
59
+ # line, that makes start == end (one-line fold), which most editors skip; we filter those out.
68
60
  end_line = loc.end_line - 2
69
61
  return if end_line <= start_line
70
62
 
@@ -12,16 +12,14 @@ require_relative "../inference/scope_indexer"
12
12
 
13
13
  module Rigor
14
14
  module LanguageServer
15
- # Answers `textDocument/hover` requests by running the same
16
- # NodeLocator + ScopeIndexer + `Scope#type_of` chain that
17
- # `rigor type-of` already drives. The LSP wraps the result in a
18
- # `Hover` payload with markdown contents.
15
+ # Answers `textDocument/hover` requests by running the same NodeLocator + ScopeIndexer + `Scope#type_of`
16
+ # chain that `rigor type-of` already drives. The LSP wraps the result in a `Hover` payload with markdown
17
+ # contents.
19
18
  #
20
19
  # Per LSP spec § "Position":
21
20
  # - `line` and `character` are 0-based.
22
- # - `character` counts UTF-16 code units; v1 emits byte counts
23
- # for ASCII source (UTF-16 conversion is queued, see design
24
- # doc § "Open questions").
21
+ # - `character` counts UTF-16 code units; v1 emits byte counts for ASCII source (UTF-16 conversion is
22
+ # queued, see design doc § "Open questions").
25
23
  class HoverProvider
26
24
  include BufferResolution
27
25
 
@@ -42,8 +40,7 @@ module Rigor
42
40
  parse_result = Prism.parse(entry.bytes, filepath: path, version: @project_context.configuration.target_ruby)
43
41
  return nil unless parse_result.errors.empty?
44
42
 
45
- # Rigor's NodeLocator uses 1-based line / column; LSP uses
46
- # 0-based. Translate at the boundary.
43
+ # Rigor's NodeLocator uses 1-based line / column; LSP uses 0-based. Translate at the boundary.
47
44
  node = locate_node(source: entry.bytes, root: parse_result.value, line: line + 1, character: character + 1)
48
45
  return nil if node.nil?
49
46
 
@@ -64,8 +61,8 @@ module Rigor
64
61
  end
65
62
 
66
63
  def base_scope(_path)
67
- # Pulls the Environment from the cached ProjectContext so
68
- # each hover avoids repeating the RBS-load build.
64
+ # Pulls the Environment from the cached ProjectContext so each hover avoids repeating the RBS-load
65
+ # build.
69
66
  Scope.empty(environment: @project_context.environment)
70
67
  end
71
68
  end
@@ -9,18 +9,14 @@ require_relative "../type/difference"
9
9
 
10
10
  module Rigor
11
11
  module LanguageServer
12
- # Builds the LSP `Hover.contents` markdown body. Dispatches on
13
- # the hovered Prism node class so each shape (method call,
14
- # constant, local, ivar, literal) gets the most relevant
15
- # type-aware presentation per
12
+ # Builds the LSP `Hover.contents` markdown body. Dispatches on the hovered Prism node class so each shape
13
+ # (method call, constant, local, ivar, literal) gets the most relevant type-aware presentation per
16
14
  # `docs/design/20260517-lsp-hover-completion.md`.
17
15
  class HoverRenderer
18
- # @param node_scope_lookup [#[]] node-to-scope table built
19
- # by `ScopeIndexer.index`. The renderer indexes into it to
20
- # retrieve the receiver's narrow scope when specialising on
21
- # `CallNode`. The lookup never returns nil (the indexer's
22
- # Hash carries `default_scope` as its default value), so
23
- # the renderer trusts the lookup result.
16
+ # @param node_scope_lookup [#[]] node-to-scope table built by `ScopeIndexer.index`. The renderer indexes
17
+ # into it to retrieve the receiver's narrow scope when specialising on `CallNode`. The lookup never
18
+ # returns nil (the indexer's Hash carries `default_scope` as its default value), so the renderer trusts
19
+ # the lookup result.
24
20
  def render(node:, type:, node_scope_lookup:)
25
21
  body = render_body(node, type, node_scope_lookup)
26
22
  result = { contents: { kind: "markdown", value: body } }
@@ -30,11 +26,9 @@ module Rigor
30
26
 
31
27
  private
32
28
 
33
- # Converts a Prism `Location` to an LSP `Range` (0-based
34
- # line, 0-based UTF-16-character column). UTF-16 conversion
35
- # is queued slice E1 emits byte columns which match for
36
- # ASCII source; non-ASCII falls back gracefully because
37
- # clients clamp out-of-range columns to the end of line.
29
+ # Converts a Prism `Location` to an LSP `Range` (0-based line, 0-based UTF-16-character column). UTF-16
30
+ # conversion is queued slice E1 emits byte columns which match for ASCII source; non-ASCII falls back
31
+ # gracefully because clients clamp out-of-range columns to the end of line.
38
32
  def lsp_range_for(node)
39
33
  loc = node.location
40
34
  {
@@ -65,12 +59,10 @@ module Rigor
65
59
  end
66
60
  end
67
61
 
68
- # Renders a `CallNode` hover when the receiver's type can be
69
- # mapped to a known RBS class and the method is declared
70
- # there. Returns nil for shapes the slice-A1 floor doesn't
71
- # handle (implicit `self`, Union / Refined / Shape receivers
72
- # — those land in slices A2-A4 and slice 7); the caller falls
73
- # back to the default body.
62
+ # Renders a `CallNode` hover when the receiver's type can be mapped to a known RBS class and the method
63
+ # is declared there. Returns nil for shapes the slice-A1 floor doesn't handle (implicit `self`, Union /
64
+ # Refined / Shape receivers those land in slices A2-A4 and slice 7); the caller falls back to the
65
+ # default body.
74
66
  def render_call(call_node, return_type, node_scope_lookup)
75
67
  receiver_node = call_node.receiver
76
68
  return nil if receiver_node.nil?
@@ -91,11 +83,9 @@ module Rigor
91
83
  )
92
84
  end
93
85
 
94
- # @return [[RBS::Definition::Method, String, Symbol], nil]
95
- # the resolved method definition, the receiver class name,
96
- # and the dispatch kind (`:instance` or `:singleton`); nil
97
- # when the receiver shape isn't yet supported or the
98
- # method doesn't resolve through the RBS env.
86
+ # @return [[RBS::Definition::Method, String, Symbol], nil] the resolved method definition, the receiver
87
+ # class name, and the dispatch kind (`:instance` or `:singleton`); nil when the receiver shape isn't
88
+ # yet supported or the method doesn't resolve through the RBS env.
99
89
  def lookup_method(receiver_type, method_name, scope)
100
90
  case receiver_type
101
91
  when Type::Singleton
@@ -114,13 +104,10 @@ module Rigor
114
104
  end
115
105
  end
116
106
 
117
- # Maps a receiver carrier to the underlying RBS class name
118
- # for method lookup. v1 handles the two carriers that produce
119
- # well-defined ".methods to enumerate" semantics: `Nominal[C]`
120
- # (the canonical case) and `Constant<v>` (literal scalars,
121
- # where the value's runtime class is the receiver). Other
122
- # carriers fall through to the default hover; slice 7 of the
123
- # design adds Union / Refined / Shape handling.
107
+ # Maps a receiver carrier to the underlying RBS class name for method lookup. v1 handles the two carriers
108
+ # that produce well-defined ".methods to enumerate" semantics: `Nominal[C]` (the canonical case) and
109
+ # `Constant<v>` (literal scalars, where the value's runtime class is the receiver). Other carriers fall
110
+ # through to the default hover; slice 7 of the design adds Union / Refined / Shape handling.
124
111
  def nominal_class_name(type)
125
112
  case type
126
113
  when Type::Nominal then type.class_name
@@ -128,12 +115,9 @@ module Rigor
128
115
  end
129
116
  end
130
117
 
131
- # `Type::Constant<value>`'s `value` is the literal Ruby
132
- # object; map it to the RBS-canonical class name through
133
- # `Object#class`. The cross-runtime mapping mirrors how the
134
- # dispatcher already widens a literal to its nominal class
135
- # at the call site — keep these in sync if a new literal
136
- # carrier lands.
118
+ # `Type::Constant<value>`'s `value` is the literal Ruby object; map it to the RBS-canonical class name
119
+ # through `Object#class`. The cross-runtime mapping mirrors how the dispatcher already widens a literal
120
+ # to its nominal class at the call site keep these in sync if a new literal carrier lands.
137
121
  def constant_to_class_name(value)
138
122
  value.class.name
139
123
  end
@@ -149,21 +133,17 @@ module Rigor
149
133
  body << "#{return_type.describe}\n"
150
134
  body << "```"
151
135
  if (doc = rbs_documentation(definition))
152
- # Close the code fence first so the comment text renders
153
- # as prose, then a fresh fenced block isn't necessary
154
- # plain markdown below the code reads better in clients.
136
+ # Close the code fence first so the comment text renders as prose, then a fresh fenced block isn't
137
+ # necessary plain markdown below the code reads better in clients.
155
138
  body << "\n\n---\n\n#{doc}"
156
139
  end
157
140
  body
158
141
  end
159
142
 
160
- # Returns the concatenated text of every RBS comment attached
161
- # to the method definition, or nil when no comments exist.
162
- # `RBS::Definition::Method#comments` is an Array<AST::Comment>
163
- # with each entry's `.string` carrying the raw text (newline-
164
- # terminated `# foo` lines). Multiple comments are joined
165
- # with a blank line so each upstream `# Foo bar` paragraph
166
- # is preserved.
143
+ # Returns the concatenated text of every RBS comment attached to the method definition, or nil when no
144
+ # comments exist. `RBS::Definition::Method#comments` is an Array<AST::Comment> with each entry's
145
+ # `.string` carrying the raw text (newline-terminated `# foo` lines). Multiple comments are joined with a
146
+ # blank line so each upstream `# Foo bar` paragraph is preserved.
167
147
  def rbs_documentation(definition)
168
148
  comments = definition.respond_to?(:comments) ? definition.comments : nil
169
149
  return nil if comments.nil? || comments.empty?
@@ -172,9 +152,8 @@ module Rigor
172
152
  text.empty? ? nil : text
173
153
  end
174
154
 
175
- # v1 surfaces the FIRST overload only. Multi-overload
176
- # presentation is queued (design doc § "Out of scope for v2"
177
- # — multi-overload signature display).
155
+ # v1 surfaces the FIRST overload only. Multi-overload presentation is queued (design doc § "Out of scope
156
+ # for v2" multi-overload signature display).
178
157
  def first_method_type(definition)
179
158
  method_type = definition.method_types.first
180
159
  return "(unknown)" if method_type.nil?
@@ -182,11 +161,9 @@ module Rigor
182
161
  method_type.to_s
183
162
  end
184
163
 
185
- # Specialises `ConstantReadNode` (`Foo`) and `ConstantPathNode`
186
- # (`Foo::Bar`) when the inferred type is a `Type::Singleton`
187
- # i.e., the constant refers to a class / module. Returns nil
188
- # for constants pointing at values (`FOO = 42`); those fall
189
- # through to the literal-polish slice (A4).
164
+ # Specialises `ConstantReadNode` (`Foo`) and `ConstantPathNode` (`Foo::Bar`) when the inferred type is a
165
+ # `Type::Singleton` i.e., the constant refers to a class / module. Returns nil for constants pointing
166
+ # at values (`FOO = 42`); those fall through to the literal-polish slice (A4).
190
167
  def render_constant(node, type, node_scope_lookup)
191
168
  return nil unless type.is_a?(Type::Singleton)
192
169
 
@@ -202,10 +179,9 @@ module Rigor
202
179
  body
203
180
  end
204
181
 
205
- # Resolves the source-file location for a class FQN by reading
206
- # the RBS loader's `class_decl_paths` table. Returns nil when
207
- # the table doesn't carry attribution (cache-hit paths replace
208
- # it with a sentinel, see `RunStats.attribution_available?`).
182
+ # Resolves the source-file location for a class FQN by reading the RBS loader's `class_decl_paths` table.
183
+ # Returns nil when the table doesn't carry attribution (cache-hit paths replace it with a sentinel, see
184
+ # `RunStats.attribution_available?`).
209
185
  def defined_in(fqn, scope)
210
186
  loader = scope&.environment&.rbs_loader
211
187
  return nil if loader.nil?
@@ -216,12 +192,9 @@ module Rigor
216
192
  path
217
193
  end
218
194
 
219
- # Specialises local-variable reads / writes / target nodes.
220
- # Surfaces the variable name + narrowed type. "Bound at"
221
- # source-line attribution is queued (Scope#locals tracks
222
- # {name => Type} but not the binding location); when the
223
- # ScopeIndexer grows a side-table for it, slice A3 follow-up
224
- # can fill the row in.
195
+ # Specialises local-variable reads / writes / target nodes. Surfaces the variable name + narrowed type.
196
+ # "Bound at" source-line attribution is queued (Scope#locals tracks {name => Type} but not the binding
197
+ # location); when the ScopeIndexer grows a side-table for it, slice A3 follow-up can fill the row in.
225
198
  def render_local(node, type)
226
199
  body = +"```ruby\n"
227
200
  body << "# Local\n#{node.name}\n\n"
@@ -230,11 +203,9 @@ module Rigor
230
203
  body
231
204
  end
232
205
 
233
- # Specialises instance-variable reads / writes / targets.
234
- # Surfaces the ivar name + narrowed type + the enclosing
235
- # class context derived from the scope's `self_type`. When
236
- # the self_type isn't a Nominal (e.g., top-level main) the
237
- # enclosing-class row is omitted.
206
+ # Specialises instance-variable reads / writes / targets. Surfaces the ivar name + narrowed type + the
207
+ # enclosing class context derived from the scope's `self_type`. When the self_type isn't a Nominal (e.g.,
208
+ # top-level main) the enclosing-class row is omitted.
238
209
  def render_ivar(node, type, node_scope_lookup)
239
210
  scope = node_scope_lookup[node]
240
211
  body = +"```ruby\n"
@@ -249,20 +220,16 @@ module Rigor
249
220
 
250
221
  def enclosing_class_for(scope)
251
222
  self_type = scope.self_type
252
- # Both `Nominal[C]` and `Singleton[C]` carry `class_name`;
253
- # we want the class label either way. Combined branch
254
- # keeps the slice-A3 contract simple.
223
+ # Both `Nominal[C]` and `Singleton[C]` carry `class_name`; we want the class label either way. Combined
224
+ # branch keeps the slice-A3 contract simple.
255
225
  self_type.class_name if self_type.is_a?(Type::Nominal) || self_type.is_a?(Type::Singleton)
256
226
  end
257
227
 
258
- # Specialises literal-bearing nodes (Integer / Float / String /
259
- # Symbol / Regex / true / false / nil / Array / Hash). Drops
260
- # the slice-A1 `node:` debug row in favour of a cleaner
261
- # `# Type` + `# Erased` framing, and surfaces the refinement
262
- # / difference name when one is present. For Array / Hash
263
- # the shape carriers (`Tuple<...>` / `HashShape<...>`) already
264
- # describe element types, so the framing is identical to
265
- # primitive literals.
228
+ # Specialises literal-bearing nodes (Integer / Float / String / Symbol / Regex / true / false / nil /
229
+ # Array / Hash). Drops the slice-A1 `node:` debug row in favour of a cleaner `# Type` + `# Erased`
230
+ # framing, and surfaces the refinement / difference name when one is present. For Array / Hash the shape
231
+ # carriers (`Tuple<...>` / `HashShape<...>`) already describe element types, so the framing is identical
232
+ # to primitive literals.
266
233
  def render_literal(_node, type)
267
234
  body = +"```ruby\n"
268
235
  body << "# Type\n#{type.describe}\n"
@@ -274,16 +241,12 @@ module Rigor
274
241
  body
275
242
  end
276
243
 
277
- # Surfaces the canonical kebab-case refinement name when the
278
- # type is a `Refined` or `Difference` carrier with a
279
- # registered canonical_name (e.g. `non-empty-string` /
280
- # `positive-int`). `canonical_name` is private on both
281
- # carriers; the LSP layer is a trusted internal consumer
282
- # and `send` is the documented escape hatch for surfacing
283
- # display-level metadata. Returns nil for unrefined carriers
284
- # and for refinements that don't have a canonical name (those
285
- # are presented through the predicate-id operator form by
286
- # `describe`).
244
+ # Surfaces the canonical kebab-case refinement name when the type is a `Refined` or `Difference` carrier
245
+ # with a registered canonical_name (e.g. `non-empty-string` / `positive-int`). `canonical_name` is
246
+ # private on both carriers; the LSP layer is a trusted internal consumer and `send` is the documented
247
+ # escape hatch for surfacing display-level metadata. Returns nil for unrefined carriers and for
248
+ # refinements that don't have a canonical name (those are presented through the predicate-id operator
249
+ # form by `describe`).
287
250
  def refinement_name_for(type)
288
251
  return nil unless type.is_a?(Type::Refined) || type.is_a?(Type::Difference)
289
252
 
@@ -5,24 +5,20 @@ require "language_server-protocol"
5
5
 
6
6
  module Rigor
7
7
  module LanguageServer
8
- # JSON-RPC dispatch loop. Drains messages from `reader`, routes
9
- # each to `server.dispatch`, and writes responses back through
10
- # `writer`. Stops when either the reader hits EOF (client closed
11
- # its end of the pipe) or the server transitions to `:exited`.
8
+ # JSON-RPC dispatch loop. Drains messages from `reader`, routes each to `server.dispatch`, and writes
9
+ # responses back through `writer`. Stops when either the reader hits EOF (client closed its end of the
10
+ # pipe) or the server transitions to `:exited`.
12
11
  #
13
- # The Loop knows the request / notification distinction from the
14
- # presence of the `id` field on the inbound JSON-RPC envelope:
12
+ # The Loop knows the request / notification distinction from the presence of the `id` field on the inbound
13
+ # JSON-RPC envelope:
15
14
  #
16
- # - Request (`id` present) → ALWAYS gets a response (success or
17
- # error). `Server#dispatch` returning nil for a request maps
18
- # to `result: null` per the LSP shutdown contract.
19
- # - Notification (`id` absent) → NEVER gets a response. The
20
- # dispatcher's return value is discarded.
15
+ # - Request (`id` present) → ALWAYS gets a response (success or error). `Server#dispatch` returning nil
16
+ # for a request maps to `result: null` per the LSP shutdown contract.
17
+ # - Notification (`id` absent) NEVER gets a response. The dispatcher's return value is discarded.
21
18
  #
22
- # JSON parse errors at the framing boundary surface as an LSP
23
- # `ParseError` (-32700) response with `id: null` per JSON-RPC
24
- # spec § 5.1; the loop continues so a corrupt frame doesn't
25
- # poison the rest of the session.
19
+ # JSON parse errors at the framing boundary surface as an LSP `ParseError` (-32700) response with
20
+ # `id: null` per JSON-RPC spec § 5.1; the loop continues so a corrupt frame doesn't poison the rest of the
21
+ # session.
26
22
  class Loop
27
23
  def initialize(reader:, writer:, server:)
28
24
  @reader = reader
@@ -52,13 +48,12 @@ module Rigor
52
48
  write_response(id, result)
53
49
  end
54
50
 
55
- # Maps the dispatcher's return value to a JSON-RPC response
56
- # envelope. `Server#dispatch` returns one of three shapes:
51
+ # Maps the dispatcher's return value to a JSON-RPC response envelope. `Server#dispatch` returns one of
52
+ # three shapes:
57
53
  #
58
54
  # - `{ error: {...} }` — surfaced as `{ id, error: {...} }`.
59
55
  # - any other Hash — surfaced as `{ id, result: hash }`.
60
- # - `nil` — surfaced as `{ id, result: null }` (the LSP
61
- # `shutdown` contract).
56
+ # - `nil` — surfaced as `{ id, result: null }` (the LSP `shutdown` contract).
62
57
  def write_response(id, result)
63
58
  if result.is_a?(Hash) && result.key?(:error)
64
59
  @writer.write(id: id, error: result[:error])
@@ -6,49 +6,33 @@ require_relative "../analysis/runner"
6
6
 
7
7
  module Rigor
8
8
  module LanguageServer
9
- # Per-session cache of the project-wide analyzer state the LSP
10
- # reads on every request chiefly the `Environment` (with its
11
- # ~100-300ms RBS env build), a read-only `Cache::Store` that
12
- # lets the runner hit the on-disk RBS cache without writing
13
- # back, and (since the pre-pass cache slice) a frozen
14
- # {Rigor::Analysis::ProjectScan} snapshot covering the
15
- # plugin registry, dependency-source index, and pre-pass
16
- # scanner outputs.
9
+ # Per-session cache of the project-wide analyzer state the LSP reads on every request — chiefly the
10
+ # `Environment` (with its ~100-300ms RBS env build), a read-only `Cache::Store` that lets the runner hit
11
+ # the on-disk RBS cache without writing back, and (since the pre-pass cache slice) a frozen
12
+ # {Rigor::Analysis::ProjectScan} snapshot covering the plugin registry, dependency-source index, and
13
+ # pre-pass scanner outputs.
17
14
  #
18
- # The pre-pass scan lets `DiagnosticPublisher#run_analysis`
19
- # build a `Runner` with `prebuilt:` so per-buffer publishes
20
- # skip plugin `#prepare`, the synthetic-method scanner, the
21
- # project-patched scanner, and the dependency-source walker.
22
- # For projects with substrate plugins / opt-in dependency
23
- # source / sizeable `pre_eval:` configuration this cuts
24
- # publish wall time substantially — for the trivial case
25
- # the savings are small (the per-publish path is already
26
- # ≈2ms once Environment is warm).
15
+ # The pre-pass scan lets `DiagnosticPublisher#run_analysis` build a `Runner` with `prebuilt:` so per-buffer
16
+ # publishes skip plugin `#prepare`, the synthetic-method scanner, the project-patched scanner, and the
17
+ # dependency-source walker. For projects with substrate plugins / opt-in dependency source / sizeable
18
+ # `pre_eval:` configuration this cuts publish wall time substantially — for the trivial case the savings
19
+ # are small (the per-publish path is already ≈2ms once Environment is warm).
27
20
  #
28
21
  # Invalidation:
29
- # - `#invalidate!` drops the cached environment AND project
30
- # scan + bumps the generation counter; the next reader
31
- # rebuilds. Watched-file changes
32
- # (`workspace/didChangeWatchedFiles`) and configuration
33
- # refreshes (`workspace/didChangeConfiguration`) both
34
- # trigger this the next publish observes the new
35
- # project state.
36
- # - The cache store is NOT invalidated on file change — it's
37
- # content-addressed (digests over file contents), so stale
38
- # entries naturally lose their key match. We DO keep a single
39
- # Store instance across the session so the in-process memo
40
- # serves repeat reads cheaply.
22
+ # - `#invalidate!` drops the cached environment AND project scan + bumps the generation counter; the next
23
+ # reader rebuilds. Watched-file changes (`workspace/didChangeWatchedFiles`) and configuration refreshes
24
+ # (`workspace/didChangeConfiguration`) both trigger this — the next publish observes the new project
25
+ # state.
26
+ # - The cache store is NOT invalidated on file change — it's content-addressed (digests over file
27
+ # contents), so stale entries naturally lose their key match. We DO keep a single Store instance across
28
+ # the session so the in-process memo serves repeat reads cheaply.
41
29
  #
42
- # Editor-mode trade-off: the cached `project_scan` was built
43
- # without any `buffer:` binding so scanners observed on-disk
44
- # bytes for every project file (including the file the user
45
- # is editing right now). Edits to a file that itself declares
46
- # `Plugin::Macro::HeredocTemplate` consumers or
47
- # `pre_eval:`-listed methods are not visible until a
48
- # watched-file change triggers `invalidate!`. The common
49
- # editor flow (save → file watch fires → publish) refreshes
50
- # automatically; the rare in-flight edit to a substrate-DSL
51
- # file is the documented edge case.
30
+ # Editor-mode trade-off: the cached `project_scan` was built without any `buffer:` binding so scanners
31
+ # observed on-disk bytes for every project file (including the file the user is editing right now). Edits
32
+ # to a file that itself declares `Plugin::Macro::HeredocTemplate` consumers or `pre_eval:`-listed methods
33
+ # are not visible until a watched-file change triggers `invalidate!`. The common editor flow (save → file
34
+ # watch fires → publish) refreshes automatically; the rare in-flight edit to a substrate-DSL file is the
35
+ # documented edge case.
52
36
  class ProjectContext
53
37
  attr_reader :configuration, :generation
54
38
 
@@ -60,27 +44,20 @@ module Rigor
60
44
  @project_scan = nil
61
45
  end
62
46
 
63
- # Returns the cached `Rigor::Environment` for this session,
64
- # building it on first access. The build includes the
65
- # project's full scan state (plugin registry, dependency-source
66
- # index, synthetic-method / project-patched indexes drawn
67
- # from {#project_scan}) AND every Bundler / RBS-collection
68
- # axis the runner consults at build time, so the resulting
69
- # env is bit-for-bit equivalent to what `Runner.run` would
70
- # have built on its own.
47
+ # Returns the cached `Rigor::Environment` for this session, building it on first access. The build
48
+ # includes the project's full scan state (plugin registry, dependency-source index, synthetic-method /
49
+ # project-patched indexes drawn from {#project_scan}) AND every Bundler / RBS-collection axis the
50
+ # runner consults at build time, so the resulting env is bit-for-bit equivalent to what `Runner.run`
51
+ # would have built on its own.
71
52
  #
72
- # `DiagnosticPublisher` passes this env through
73
- # `Runner.new(environment: …)` so per-buffer publishes share
74
- # one instance instead of repeating the
75
- # `Environment.for_project` build per call (bundler
76
- # discovery, RbsLoader construction, signature_paths
77
- # composition). Subsequent calls return the same instance
78
- # until `#invalidate!` drops the cache.
53
+ # `DiagnosticPublisher` passes this env through `Runner.new(environment: …)` so per-buffer publishes
54
+ # share one instance instead of repeating the `Environment.for_project` build per call (bundler
55
+ # discovery, RbsLoader construction, signature_paths composition). Subsequent calls return the same
56
+ # instance until `#invalidate!` drops the cache.
79
57
  #
80
- # The runner attaches its own per-call reporter pair onto
81
- # the shared env's `Reporters` slot at the start of each
82
- # `#analyze_files` — so diagnostic events stay scoped to a
83
- # single publish and do NOT accumulate across publishes.
58
+ # The runner attaches its own per-call reporter pair onto the shared env's `Reporters` slot at the start
59
+ # of each `#analyze_files` so diagnostic events stay scoped to a single publish and do NOT accumulate
60
+ # across publishes.
84
61
  def environment
85
62
  @environment ||= Environment.for_project(
86
63
  libraries: @configuration.libraries,
@@ -98,35 +75,30 @@ module Rigor
98
75
  )
99
76
  end
100
77
 
101
- # Returns the per-session read-only `Cache::Store`. Read-only
102
- # so multiple LSP sessions against the same project don't
103
- # race on cache writes — same contract editor mode v1 already
104
- # uses for the CLI `--tmp-file` path.
78
+ # Returns the per-session read-only `Cache::Store`. Read-only so multiple LSP sessions against the same
79
+ # project don't race on cache writes same contract editor mode v1 already uses for the CLI
80
+ # `--tmp-file` path.
105
81
  def cache_store
106
82
  @cache_store ||= Cache::Store.new(root: @configuration.cache_path, read_only: true)
107
83
  end
108
84
 
109
- # Returns the cached {Rigor::Analysis::ProjectScan} for this
110
- # session, building it lazily by spinning up a project-only
111
- # `Runner` (no buffer binding, no `paths` override) and
112
- # calling `#prepare_project_scan`. The cold build pays the
113
- # full pre-pass cost once per generation; every subsequent
85
+ # Returns the cached {Rigor::Analysis::ProjectScan} for this session, building it lazily by spinning up a
86
+ # project-only `Runner` (no buffer binding, no `paths` override) and calling `#prepare_project_scan`. The
87
+ # cold build pays the full pre-pass cost once per generation; every subsequent
114
88
  # `Runner.new(prebuilt: project_scan)` skips it.
115
89
  def project_scan
116
90
  @project_scan ||= build_project_scan
117
91
  end
118
92
 
119
- # Drops every cached collaborator and bumps the generation.
120
- # The next reader rebuilds from scratch. Triggered by
121
- # `workspace/didChangeWatchedFiles` for project source files
122
- # and by `workspace/didChangeConfiguration`.
93
+ # Drops every cached collaborator and bumps the generation. The next reader rebuilds from scratch.
94
+ # Triggered by `workspace/didChangeWatchedFiles` for project source files and by
95
+ # `workspace/didChangeConfiguration`.
123
96
  def invalidate!
124
97
  @generation += 1
125
98
  @environment = nil
126
99
  @project_scan = nil
127
- # Cache store stays — it's content-addressed; a stale env
128
- # build won't be served because the file digest mixed into
129
- # the cache key has changed.
100
+ # Cache store stays — it's content-addressed; a stale env build won't be served because the file
101
+ # digest mixed into the cache key has changed.
130
102
  nil
131
103
  end
132
104
 
@@ -7,12 +7,10 @@ require_relative "buffer_resolution"
7
7
 
8
8
  module Rigor
9
9
  module LanguageServer
10
- # Answers `textDocument/selectionRange` requests. For each
11
- # position, returns a linked list of SelectionRange entries
12
- # innermost first, each pointing at its `parent` (the next-
13
- # wider expression). Editors use this for "expand selection":
14
- # one keystroke moves up the chain, another moves further out,
15
- # all the way to the root.
10
+ # Answers `textDocument/selectionRange` requests. For each position, returns a linked list of
11
+ # SelectionRange entries — innermost first, each pointing at its `parent` (the next-wider expression).
12
+ # Editors use this for "expand selection": one keystroke moves up the chain, another moves further out, all
13
+ # the way to the root.
16
14
  class SelectionRangeProvider
17
15
  include BufferResolution
18
16
 
@@ -43,8 +41,8 @@ module Rigor
43
41
 
44
42
  private
45
43
 
46
- # Walks the AST top-down; each node whose location encloses
47
- # `offset` gets appended to the chain. Returns root→innermost.
44
+ # Walks the AST top-down; each node whose location encloses `offset` gets appended to the chain. Returns
45
+ # root→innermost.
48
46
  def ancestor_chain(node, offset, chain = [])
49
47
  return chain unless node.is_a?(Prism::Node)
50
48
  return chain unless node.location && offset_in?(node.location, offset)
@@ -58,10 +56,9 @@ module Rigor
58
56
  offset.between?(location.start_offset, location.end_offset)
59
57
  end
60
58
 
61
- # Folds the root→innermost chain into the LSP `SelectionRange`
62
- # linked-list shape innermost on the outside (the request's
63
- # return value) with `parent` chained outward. Editor "expand
64
- # selection" follows `.parent` one step per invocation.
59
+ # Folds the root→innermost chain into the LSP `SelectionRange` linked-list shape — innermost on the
60
+ # outside (the request's return value) with `parent` chained outward. Editor "expand selection" follows
61
+ # `.parent` one step per invocation.
65
62
  def build_chain(root, offset)
66
63
  chain = ancestor_chain(root, offset)
67
64
  return nil if chain.empty?