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
@@ -2,16 +2,12 @@
2
2
 
3
3
  module Rigor
4
4
  module LanguageServer
5
- # Per-session virtual file table. The LSP server maintains the
6
- # canonical view of every open buffer here; analysis (slice 4+)
7
- # reads from this table instead of disk so in-flight edits are
8
- # reflected immediately.
5
+ # Per-session virtual file table. The LSP server maintains the canonical view of every open buffer here;
6
+ # analysis (slice 4+) reads from this table instead of disk so in-flight edits are reflected immediately.
9
7
  #
10
- # Keyed by `DocumentUri` (LSP `file://...` URIs). v1 ships
11
- # FULL text sync (LSP `TextDocumentSyncKind::Full = 1`) so each
12
- # `didChange` carries the entire buffer text there's no
13
- # incremental edit application yet. Incremental sync is slice
14
- # 10 (deferred per the design doc).
8
+ # Keyed by `DocumentUri` (LSP `file://...` URIs). v1 ships FULL text sync (LSP
9
+ # `TextDocumentSyncKind::Full = 1`) so each `didChange` carries the entire buffer text — there's no
10
+ # incremental edit application yet. Incremental sync is slice 10 (deferred per the design doc).
15
11
  class BufferTable
16
12
  # @!attribute uri [String] the LSP DocumentUri (e.g. `file:///abs/path/lib/foo.rb`).
17
13
  # @!attribute bytes [String] the current full text of the buffer.
@@ -22,23 +18,20 @@ module Rigor
22
18
  @entries = {}
23
19
  end
24
20
 
25
- # Records a `textDocument/didOpen` event. Replaces any
26
- # existing entry (LSP clients may re-open a previously closed
27
- # URI; the new version is authoritative).
21
+ # Records a `textDocument/didOpen` event. Replaces any existing entry (LSP clients may re-open a
22
+ # previously closed URI; the new version is authoritative).
28
23
  def open(uri:, bytes:, version:)
29
24
  @entries[uri] = Entry.new(uri: uri, bytes: bytes, version: version)
30
25
  end
31
26
 
32
- # Records a `textDocument/didChange` event under FULL sync.
33
- # The full new buffer text replaces the entry. If the client
34
- # sends a `didChange` for a URI that was never opened (spec
35
- # violation), the entry is still created — defensive.
27
+ # Records a `textDocument/didChange` event under FULL sync. The full new buffer text replaces the entry.
28
+ # If the client sends a `didChange` for a URI that was never opened (spec violation), the entry is still
29
+ # created defensive.
36
30
  def change(uri:, bytes:, version:)
37
31
  @entries[uri] = Entry.new(uri: uri, bytes: bytes, version: version)
38
32
  end
39
33
 
40
- # Records a `textDocument/didClose` event. The entry is
41
- # removed. Subsequent reads via `#[]` return nil.
34
+ # Records a `textDocument/didClose` event. The entry is removed. Subsequent reads via `#[]` return nil.
42
35
  def close(uri:)
43
36
  @entries.delete(uri)
44
37
  end
@@ -21,11 +21,9 @@ require_relative "../type/hash_shape"
21
21
 
22
22
  module Rigor
23
23
  module LanguageServer
24
- # Answers `textDocument/completion` requests. Provides method
25
- # completion for `obj.|` (known-type receiver → RBS-known methods),
26
- # constant-path completion, hash-key completion, Union / Intersection /
27
- # Refined / Shape receiver handling, and parse-recovery fallback for
28
- # malformed buffers (sentinel injection).
24
+ # Answers `textDocument/completion` requests. Provides method completion for `obj.|` (known-type receiver
25
+ # → RBS-known methods), constant-path completion, hash-key completion, Union / Intersection / Refined /
26
+ # Shape receiver handling, and parse-recovery fallback for malformed buffers (sentinel injection).
29
27
  #
30
28
  # LSP `CompletionItemKind` values used:
31
29
  # - 2 = Method, 5 = Field, 7 = Class, 9 = Module, 21 = Constant.
@@ -54,9 +52,8 @@ module Rigor
54
52
  path, entry = buffer_for(uri)
55
53
  return nil if entry.nil?
56
54
 
57
- # Slice B4 — parse recovery. The common mid-edit buffer
58
- # (`obj.` / `Foo::`) doesn't parse cleanly; try inserting
59
- # a sentinel name at the cursor before falling through.
55
+ # Slice B4 — parse recovery. The common mid-edit buffer (`obj.` / `Foo::`) doesn't parse cleanly; try
56
+ # inserting a sentinel name at the cursor before falling through.
60
57
  bytes_to_parse, locate_at = parse_attempt_bytes(entry.bytes, line, character)
61
58
  parse_result = Prism.parse(bytes_to_parse, filepath: path,
62
59
  version: @project_context.configuration.target_ruby)
@@ -69,17 +66,14 @@ module Rigor
69
66
 
70
67
  case node
71
68
  when Prism::CallNode
72
- # `hash[:|` patches to `hash[:KEY_SENTINEL]`, an index
73
- # access call whose argument is the sentinel symbol;
74
- # NodeLocator at the sentinel returns the inner CallNode
75
- # for `[]`. Hash-key completion wins when the call is an
76
- # index access on a HashShape carrier; otherwise fall
77
- # through to method completion.
69
+ # `hash[:|` patches to `hash[:KEY_SENTINEL]`, an index access call whose argument is the sentinel
70
+ # symbol; NodeLocator at the sentinel returns the inner CallNode for `[]`. Hash-key completion wins
71
+ # when the call is an index access on a HashShape carrier; otherwise fall through to method
72
+ # completion.
78
73
  hash_key_completion_for(node, parse_result.value, path) ||
79
74
  method_completion_for(node, parse_result.value, path)
80
75
  when Prism::SymbolNode
81
- # The sentinel symbol's NodeLocator hit; walk up via the
82
- # AST to find the enclosing `[]` call.
76
+ # The sentinel symbol's NodeLocator hit; walk up via the AST to find the enclosing `[]` call.
83
77
  hash_key_completion_for_symbol(node, parse_result.value, path)
84
78
  when Prism::ConstantPathNode
85
79
  constant_path_completion_for(node, path)
@@ -88,34 +82,25 @@ module Rigor
88
82
 
89
83
  private
90
84
 
91
- # Slice B4 — parse recovery. Returns `[bytes, [line, character]]`:
92
- # the source to feed Prism plus the cursor position the
93
- # caller should locate against. When the original buffer
94
- # parses cleanly we return it verbatim; when it doesn't AND
95
- # the cursor sits at a mid-edit `.` / `::` trigger, we
96
- # splice a sentinel identifier into the source so Prism's
97
- # AST captures the receiver / parent constant cleanly.
85
+ # Slice B4 — parse recovery. Returns `[bytes, [line, character]]`: the source to feed Prism plus the
86
+ # cursor position the caller should locate against. When the original buffer parses cleanly we return it
87
+ # verbatim; when it doesn't AND the cursor sits at a mid-edit `.` / `::` trigger, we splice a sentinel
88
+ # identifier into the source so Prism's AST captures the receiver / parent constant cleanly.
98
89
  #
99
- # The sentinel is a syntactically-unique identifier the
100
- # NodeLocator can find without ambiguity. We choose lower /
101
- # upper case based on whether the trigger was `.` (method
102
- # name lowercase identifier) or `::` (constant path
103
- # uppercase identifier).
104
- # Sentinels need to be parseable as their target shape:
105
- # method sentinel is a lower-snake identifier; constant
106
- # sentinel MUST start with an uppercase letter (Ruby
107
- # constants reject `_`-leading names — `Process::__Foo`
108
- # parses as a method call, not a constant path).
90
+ # The sentinel is a syntactically-unique identifier the NodeLocator can find without ambiguity. We
91
+ # choose lower / upper case based on whether the trigger was `.` (method name — lowercase identifier) or
92
+ # `::` (constant path uppercase identifier). Sentinels need to be parseable as their target shape:
93
+ # method sentinel is a lower-snake identifier; constant sentinel MUST start with an uppercase letter
94
+ # (Ruby constants reject `_`-leading names — `Process::__Foo` parses as a method call, not a constant
95
+ # path).
109
96
  SENTINEL_METHOD = "__rigor_lsp_sentinel__"
110
97
  SENTINEL_CONSTANT = "RigorLspSentinelXyz"
111
98
  SENTINEL_HASH_KEY = "__rigor_lsp_key__"
112
99
  private_constant :SENTINEL_METHOD, :SENTINEL_CONSTANT, :SENTINEL_HASH_KEY
113
100
 
114
101
  def parse_attempt_bytes(original_bytes, line, character)
115
- # Cheap probe: try original first. Most editor sessions
116
- # call completion in mid-keystroke where parse fails, but
117
- # some land on a clean buffer (e.g. the trigger fires
118
- # right after the user picked an item).
102
+ # Cheap probe: try original first. Most editor sessions call completion in mid-keystroke where parse
103
+ # fails, but some land on a clean buffer (e.g. the trigger fires right after the user picked an item).
119
104
  if Prism.parse(original_bytes).errors.empty?
120
105
  [original_bytes, [line, character]]
121
106
  else
@@ -136,23 +121,18 @@ module Rigor
136
121
  return [original_bytes, [line, character]] if sentinel.nil?
137
122
 
138
123
  lines[line] = "#{prefix}#{sentinel}#{suffix}"
139
- # The cursor stays at the same column — Prism's AST now
140
- # has a node whose name occupies [character, character +
141
- # sentinel.size). NodeLocator at that column returns the
142
- # enclosing call / path.
124
+ # The cursor stays at the same column — Prism's AST now has a node whose name occupies [character,
125
+ # character + sentinel.size). NodeLocator at that column returns the enclosing call / path.
143
126
  [lines.join, [line, character]]
144
127
  end
145
128
 
146
129
  def sentinel_for_prefix(prefix)
147
- # Strip trailing whitespace before checking for the
148
- # trigger character; users often type the dot then a
149
- # space mid-edit.
130
+ # Strip trailing whitespace before checking for the trigger character; users often type the dot then
131
+ # a space mid-edit.
150
132
  stripped = prefix.rstrip
151
- # `[:` covers `hash[:|` symbol-key access — splice both
152
- # the key name AND the closing `]` so Prism gets a
153
- # complete index expression. The closing bracket is part
154
- # of the sentinel string for this case (vs the bare
155
- # identifier sentinels for `.` / `::`).
133
+ # `[:` covers `hash[:|` symbol-key access — splice both the key name AND the closing `]` so Prism gets
134
+ # a complete index expression. The closing bracket is part of the sentinel string for this case (vs
135
+ # the bare identifier sentinels for `.` / `::`).
156
136
  return "#{SENTINEL_HASH_KEY}]" if stripped.end_with?("[:")
157
137
  return SENTINEL_METHOD if stripped.end_with?(".")
158
138
  return SENTINEL_CONSTANT if stripped.end_with?("::")
@@ -170,12 +150,10 @@ module Rigor
170
150
  method_completions(receiver_type, receiver_scope)
171
151
  end
172
152
 
173
- # Slice D1 — `hash[:|` hash-key completion. Triggers when
174
- # the cursor is on the synthetic key inside a `[]` call AND
175
- # the receiver's inferred type is a `Type::HashShape`. Returns
176
- # nil for any other receiver carrier so the dispatcher falls
177
- # through to method completion (which still surfaces Hash's
178
- # methods for genuine method calls on a hash receiver).
153
+ # Slice D1 — `hash[:|` hash-key completion. Triggers when the cursor is on the synthetic key inside a
154
+ # `[]` call AND the receiver's inferred type is a `Type::HashShape`. Returns nil for any other receiver
155
+ # carrier so the dispatcher falls through to method completion (which still surfaces Hash's methods for
156
+ # genuine method calls on a hash receiver).
179
157
  def hash_key_completion_for(call_node, root, path)
180
158
  return nil unless call_node.name == :[]
181
159
 
@@ -189,10 +167,9 @@ module Rigor
189
167
  hash_key_items(receiver_type)
190
168
  end
191
169
 
192
- # When NodeLocator returns the sentinel SymbolNode directly
193
- # (rather than the enclosing CallNode), walk up the AST for
194
- # the smallest `[]` call containing the symbol's location.
195
- # Re-walks the root once; cheap for LSP-sized buffers.
170
+ # When NodeLocator returns the sentinel SymbolNode directly (rather than the enclosing CallNode), walk
171
+ # up the AST for the smallest `[]` call containing the symbol's location. Re-walks the root once; cheap
172
+ # for LSP-sized buffers.
196
173
  def hash_key_completion_for_symbol(symbol_node, root, path)
197
174
  call_node = enclosing_index_call(root, symbol_node)
198
175
  return nil if call_node.nil?
@@ -230,12 +207,10 @@ module Rigor
230
207
  end
231
208
  end
232
209
 
233
- # Slice B2 — `Foo::|` constant-path completion. The cursor
234
- # sits on a `ConstantPathNode` whose `parent` resolves to a
235
- # class / module FQN; we enumerate every known class whose
236
- # name is an immediate child of that parent. Top-level
237
- # constants (`::Foo`) and parent-less paths are not yet
238
- # supported (queued for slice 6 follow-up).
210
+ # Slice B2 — `Foo::|` constant-path completion. The cursor sits on a `ConstantPathNode` whose `parent`
211
+ # resolves to a class / module FQN; we enumerate every known class whose name is an immediate child of
212
+ # that parent. Top-level constants (`::Foo`) and parent-less paths are not yet supported (queued for
213
+ # slice 6 follow-up).
239
214
  def constant_path_completion_for(const_path_node, path)
240
215
  parent_fqn = parent_fqn_of(const_path_node)
241
216
  return nil if parent_fqn.nil?
@@ -248,10 +223,8 @@ module Rigor
248
223
  end
249
224
 
250
225
  def parent_fqn_of(const_path_node)
251
- # ConstantPathNode#parent is the LHS of the `::`. For
252
- # `Foo::Bar` it's the ConstantReadNode for `Foo`; for
253
- # `Foo::Bar::Baz` it's a ConstantPathNode. We render
254
- # either to the dotted FQN string.
226
+ # ConstantPathNode#parent is the LHS of the `::`. For `Foo::Bar` it's the ConstantReadNode for `Foo`;
227
+ # for `Foo::Bar::Baz` it's a ConstantPathNode. We render either to the dotted FQN string.
255
228
  parent = const_path_node.parent
256
229
  return nil if parent.nil?
257
230
 
@@ -268,24 +241,18 @@ module Rigor
268
241
  end
269
242
  end
270
243
 
271
- # Walks `RbsLoader#known_class_names_set` for entries whose
272
- # FQN is `parent_fqn::<one segment>` the immediate children.
273
- # Deeper descendants are filtered out so the popup shows
274
- # only the next-level constants the user can directly write.
275
- # `known_class_names_set` is private on RbsLoader per the
276
- # type-system's internal API discipline; the LSP layer is a
277
- # trusted internal consumer and `send` is the documented
278
- # escape hatch (same pattern as `Type::Refined#canonical_name`
279
- # in slice A4).
244
+ # Walks `RbsLoader#known_class_names_set` for entries whose FQN is `parent_fqn::<one segment>` — the
245
+ # immediate children. Deeper descendants are filtered out so the popup shows only the next-level
246
+ # constants the user can directly write. `known_class_names_set` is private on RbsLoader per the
247
+ # type-system's internal API discipline; the LSP layer is a trusted internal consumer and `send` is the
248
+ # documented escape hatch (same pattern as `Type::Refined#canonical_name` in slice A4).
280
249
  def enumerate_constant_children(parent_fqn, scope)
281
250
  loader = scope.environment.rbs_loader
282
251
  return [] if loader.nil?
283
252
 
284
253
  names = loader.send(:known_class_names_set)
285
- # RBS canonical names carry a leading `::` (so the table
286
- # holds `::Process::Status` etc.). Match against both
287
- # forms so the prefix walk works regardless of which form
288
- # the caller passes.
254
+ # RBS canonical names carry a leading `::` (so the table holds `::Process::Status` etc.). Match
255
+ # against both forms so the prefix walk works regardless of which form the caller passes.
289
256
  prefix = "::#{parent_fqn}::"
290
257
  names.filter_map do |fqn|
291
258
  next nil unless fqn.start_with?(prefix)
@@ -324,21 +291,15 @@ module Rigor
324
291
  Inference::ScopeIndexer.index(root, default_scope: scope)
325
292
  end
326
293
 
327
- # Returns an Array<Hash> of LSP `CompletionItem`s for every
328
- # public method callable on the receiver. Slice B3 extends
329
- # the slice-B1 floor with:
330
- # - `Type::Refined` / `Type::Difference` — enumerate the
331
- # underlying nominal (refinement narrows the value set,
332
- # not the method set).
333
- # - `Type::Tuple` / `Type::HashShape` enumerate the
334
- # nominal ancestor (`Array` / `Hash`); element-type-aware
335
- # completion is queued.
336
- # - `Type::Union` — intersection of methods on each member
337
- # (only methods guaranteed to dispatch on every union case).
338
- # Conservative default per design doc § "Union receiver
339
- # completion".
340
- # - `Type::Intersection` — union of methods on each member
341
- # (anything callable on at least one member).
294
+ # Returns an Array<Hash> of LSP `CompletionItem`s for every public method callable on the receiver.
295
+ # Slice B3 extends the slice-B1 floor with:
296
+ # - `Type::Refined` / `Type::Difference` — enumerate the underlying nominal (refinement narrows the
297
+ # value set, not the method set).
298
+ # - `Type::Tuple` / `Type::HashShape` — enumerate the nominal ancestor (`Array` / `Hash`);
299
+ # element-type-aware completion is queued.
300
+ # - `Type::Union` intersection of methods on each member (only methods guaranteed to dispatch on every
301
+ # union case). Conservative default per design doc § "Union receiver completion".
302
+ # - `Type::Intersection` — union of methods on each member (anything callable on at least one member).
342
303
  def method_completions(receiver_type, scope)
343
304
  method_set, kind = enumerate_method_set(receiver_type, scope)
344
305
  return nil if method_set.nil? || method_set.empty?
@@ -350,13 +311,10 @@ module Rigor
350
311
  end
351
312
  end
352
313
 
353
- # Returns `[{Symbol => RBS::Definition::Method}, :instance | :singleton]`
354
- # for the receiver. Composite carriers (Union / Intersection /
355
- # Refined / shape carriers) reduce to instance-method
356
- # enumeration; the receiver-class label that lands in each
357
- # CompletionItem's `detail` still comes from each method's
358
- # own `defs.first.implemented_in`, so the rendered prefix
359
- # stays accurate per-method.
314
+ # Returns `[{Symbol => RBS::Definition::Method}, :instance | :singleton]` for the receiver. Composite
315
+ # carriers (Union / Intersection / Refined / shape carriers) reduce to instance-method enumeration; the
316
+ # receiver-class label that lands in each CompletionItem's `detail` still comes from each method's own
317
+ # `defs.first.implemented_in`, so the rendered prefix stays accurate per-method.
360
318
  def enumerate_method_set(receiver_type, scope)
361
319
  case receiver_type
362
320
  when Type::Singleton
@@ -379,11 +337,9 @@ module Rigor
379
337
  end
380
338
  end
381
339
 
382
- # Union receiver — keep only methods present in EVERY
383
- # member's set. Conservative semantically (every method
384
- # returned is callable on every member) and prevents
385
- # `obj.upcase` from appearing on a `String | Integer`
386
- # union where only one side answers `upcase`.
340
+ # Union receiver — keep only methods present in EVERY member's set. Conservative semantically (every
341
+ # method returned is callable on every member) and prevents `obj.upcase` from appearing on a
342
+ # `String | Integer` union where only one side answers `upcase`.
387
343
  def intersect_member_methods(members, scope)
388
344
  member_sets = members.filter_map { |m| enumerate_method_set(m, scope).first }
389
345
  return nil if member_sets.empty?
@@ -392,9 +348,8 @@ module Rigor
392
348
  member_sets.first.slice(*common_names)
393
349
  end
394
350
 
395
- # Intersection receiver — accumulate every method declared on
396
- # ANY member. A value of type `A & B` is callable through both
397
- # interfaces; the completion popup MAY show either's methods.
351
+ # Intersection receiver — accumulate every method declared on ANY member. A value of type `A & B` is
352
+ # callable through both interfaces; the completion popup MAY show either's methods.
398
353
  def union_member_methods(members, scope)
399
354
  members.filter_map { |m| enumerate_method_set(m, scope).first }.reduce({}, :merge)
400
355
  end
@@ -420,10 +375,8 @@ module Rigor
420
375
  detail: detail,
421
376
  insertText: label,
422
377
  filterText: label,
423
- # Inherited methods rank below own-class methods; the
424
- # `defs.first.implemented_in` carries the declaring
425
- # class. Inheritance-distance ranking is queued (design
426
- # doc § "sortText").
378
+ # Inherited methods rank below own-class methods; the `defs.first.implemented_in` carries the
379
+ # declaring class. Inheritance-distance ranking is queued (design doc § "sortText").
427
380
  sortText: "1_#{label}"
428
381
  }
429
382
  end
@@ -2,23 +2,17 @@
2
2
 
3
3
  module Rigor
4
4
  module LanguageServer
5
- # Per-key debouncer. The LSP uses this to defer
6
- # `publishDiagnostics` until the user stops typing (200ms
7
- # quiet-time floor per LSP UX conventions). Each
8
- # `schedule(uri, delay:)` cancels the previous task for the
9
- # same `uri` and queues a new one — only the LAST task in a
10
- # burst actually runs.
5
+ # Per-key debouncer. The LSP uses this to defer `publishDiagnostics` until the user stops typing (200ms
6
+ # quiet-time floor per LSP UX conventions). Each `schedule(uri, delay:)` cancels the previous task for the
7
+ # same `uri` and queues a new one — only the LAST task in a burst actually runs.
11
8
  #
12
- # Threading model: each scheduled task runs in its own Thread
13
- # so the dispatcher loop doesn't block. Concurrent writes to
14
- # STDOUT from the Debouncer's threads + the main dispatch
15
- # loop are serialised by `SynchronizedWriter`.
9
+ # Threading model: each scheduled task runs in its own Thread so the dispatcher loop doesn't block.
10
+ # Concurrent writes to STDOUT from the Debouncer's threads + the main dispatch loop are serialised by
11
+ # `SynchronizedWriter`.
16
12
  #
17
- # Cancellation is cooperative: each task carries a
18
- # `cancelled` flag; new schedules flip the prior task's flag
19
- # and the prior thread skips the block on wake-up. This is
20
- # safer than `Thread#kill` for in-flight Ruby code and good
21
- # enough for the "drop stale debounce" use case.
13
+ # Cancellation is cooperative: each task carries a `cancelled` flag; new schedules flip the prior task's
14
+ # flag and the prior thread skips the block on wake-up. This is safer than `Thread#kill` for in-flight
15
+ # Ruby code and good enough for the "drop stale debounce" use case.
22
16
  class Debouncer
23
17
  Task = Struct.new(:thread, :cancelled)
24
18
 
@@ -27,10 +21,9 @@ module Rigor
27
21
  @mutex = Mutex.new
28
22
  end
29
23
 
30
- # Schedule `block` to run after `delay` seconds, replacing
31
- # any pending task for the same `key`. `delay: 0` makes the
32
- # task fire immediately (still on its own thread); tests
33
- # pair this with `#flush!` for deterministic assertions.
24
+ # Schedule `block` to run after `delay` seconds, replacing any pending task for the same `key`.
25
+ # `delay: 0` makes the task fire immediately (still on its own thread); tests pair this with `#flush!`
26
+ # for deterministic assertions.
34
27
  def schedule(key, delay:, &block)
35
28
  task = Task.new(nil, false)
36
29
 
@@ -55,9 +48,8 @@ module Rigor
55
48
  nil
56
49
  end
57
50
 
58
- # Wait for every pending task to complete. Used by specs to
59
- # synchronise with the async schedule; the production
60
- # `shutdown` path uses `#cancel_all` instead.
51
+ # Wait for every pending task to complete. Used by specs to synchronise with the async schedule; the
52
+ # production `shutdown` path uses `#cancel_all` instead.
61
53
  def flush!
62
54
  threads = @mutex.synchronize { @tasks.values.map(&:thread) }
63
55
  threads.each do |thread|
@@ -67,9 +59,8 @@ module Rigor
67
59
  end
68
60
  end
69
61
 
70
- # Cancel every pending task (sets the flag; the threads
71
- # exit without running the block). Called on `shutdown` so
72
- # in-flight publishes don't write to a closed STDOUT.
62
+ # Cancel every pending task (sets the flag; the threads exit without running the block). Called on
63
+ # `shutdown` so in-flight publishes don't write to a closed STDOUT.
73
64
  def cancel_all
74
65
  @mutex.synchronize do
75
66
  @tasks.each_value { |t| t.cancelled = true }
@@ -8,17 +8,14 @@ require_relative "../analysis/buffer_binding"
8
8
 
9
9
  module Rigor
10
10
  module LanguageServer
11
- # Converts buffer state into `textDocument/publishDiagnostics`
12
- # notifications. Owns the Rigor `Analysis::Runner` orchestration
13
- # for the per-buffer single-file scope path that editor mode v1
14
- # already supports every `publish_for(uri)` call materialises
15
- # a `BufferBinding` from the BufferTable entry, runs the Runner,
16
- # and pushes the resulting LSP `Diagnostic[]` through the writer.
11
+ # Converts buffer state into `textDocument/publishDiagnostics` notifications. Owns the Rigor
12
+ # `Analysis::Runner` orchestration for the per-buffer single-file scope path that editor mode v1 already
13
+ # supports every `publish_for(uri)` call materialises a `BufferBinding` from the BufferTable entry, runs
14
+ # the Runner, and pushes the resulting LSP `Diagnostic[]` through the writer.
17
15
  #
18
- # Debouncing is wired via an optional `Debouncer` injected at
19
- # construction (delay defaults to 200ms quiet-time); without a
20
- # debouncer each call blocks synchronously (primarily for specs).
21
- # Ractor-pool dispatch is queued.
16
+ # Debouncing is wired via an optional `Debouncer` injected at construction (delay defaults to 200ms
17
+ # quiet-time); without a debouncer each call blocks synchronously (primarily for specs). Ractor-pool
18
+ # dispatch is queued.
22
19
  class DiagnosticPublisher
23
20
  # Maps Rigor severity symbols to LSP DiagnosticSeverity
24
21
  # integers per spec § "Diagnostic":
@@ -49,12 +46,10 @@ module Rigor
49
46
  @debounce_seconds = debounce_seconds
50
47
  end
51
48
 
52
- # Run analysis for the buffer at `uri` (looked up in the
53
- # BufferTable) and push a `textDocument/publishDiagnostics`
54
- # notification. No-op when the URI isn't a `file://` form or
55
- # the buffer isn't currently open. When a Debouncer is wired,
56
- # the analysis is scheduled async per the configured
57
- # `debounce_seconds`; otherwise it runs inline.
49
+ # Run analysis for the buffer at `uri` (looked up in the BufferTable) and push a
50
+ # `textDocument/publishDiagnostics` notification. No-op when the URI isn't a `file://` form or the
51
+ # buffer isn't currently open. When a Debouncer is wired, the analysis is scheduled async per the
52
+ # configured `debounce_seconds`; otherwise it runs inline.
58
53
  def publish_for(uri)
59
54
  path = Uri.to_path(uri)
60
55
  return if path.nil?
@@ -66,17 +61,14 @@ module Rigor
66
61
  end
67
62
  end
68
63
 
69
- # Publishes an EMPTY diagnostic array for `uri`. The LSP-spec
70
- # idiom for "clear inline markers" called from `didClose`
71
- # so clients drop stale highlights when the user closes a
72
- # buffer.
64
+ # Publishes an EMPTY diagnostic array for `uri`. The LSP-spec idiom for "clear inline markers" — called
65
+ # from `didClose` so clients drop stale highlights when the user closes a buffer.
73
66
  def publish_empty(uri)
74
67
  notify(uri, [])
75
68
  end
76
69
 
77
- # Cancels every in-flight debounced task. Called from
78
- # `Server#handle_shutdown` so pending publishes don't fire
79
- # against a closed STDOUT.
70
+ # Cancels every in-flight debounced task. Called from `Server#handle_shutdown` so pending publishes
71
+ # don't fire against a closed STDOUT.
80
72
  def cancel_pending
81
73
  @debouncer&.cancel_all
82
74
  end
@@ -85,25 +77,20 @@ module Rigor
85
77
 
86
78
  def run_and_notify(uri, path)
87
79
  entry = @buffer_table[uri]
88
- # The buffer may have been closed during the debounce
89
- # window drop the publish; the empty notification from
90
- # didClose already cleared the markers.
80
+ # The buffer may have been closed during the debounce window — drop the publish; the empty
81
+ # notification from didClose already cleared the markers.
91
82
  return if entry.nil?
92
83
 
93
84
  diagnostics = run_analysis(path: path, bytes: entry.bytes)
94
85
  notify(uri, diagnostics)
95
86
  end
96
87
 
97
- # Runs `Analysis::Runner` with a `BufferBinding` so the buffer
98
- # bytes (instead of the on-disk file) drive the parse. The
99
- # `Rigor::Analysis::ProjectScan` cached on the ProjectContext
100
- # is passed through `prebuilt:` so plugin `#prepare`, the
101
- # dependency-source walker, and the synthetic-method /
102
- # project-patched scanners do not re-run per publish. The
103
- # snapshot rebuilds only when `ProjectContext#invalidate!`
104
- # fires (watched-file or configuration change). Returns
105
- # the LSP-shaped Diagnostic Array, ready to serialize into
106
- # the notification's `params.diagnostics` field.
88
+ # Runs `Analysis::Runner` with a `BufferBinding` so the buffer bytes (instead of the on-disk file) drive
89
+ # the parse. The `Rigor::Analysis::ProjectScan` cached on the ProjectContext is passed through
90
+ # `prebuilt:` so plugin `#prepare`, the dependency-source walker, and the synthetic-method /
91
+ # project-patched scanners do not re-run per publish. The snapshot rebuilds only when
92
+ # `ProjectContext#invalidate!` fires (watched-file or configuration change). Returns the LSP-shaped
93
+ # Diagnostic Array, ready to serialize into the notification's `params.diagnostics` field.
107
94
  def run_analysis(path:, bytes:)
108
95
  with_tempfile(bytes) do |tmp|
109
96
  binding = Analysis::BufferBinding.new(logical_path: path, physical_path: tmp.path)
@@ -137,10 +124,9 @@ module Rigor
137
124
  def to_lsp_diagnostic(diagnostic, buffer_path)
138
125
  return nil if diagnostic.path != buffer_path
139
126
 
140
- # Rigor uses 1-based line + 1-based byte column; LSP uses
141
- # 0-based line + 0-based UTF-16 code unit. UTF-16 conversion
142
- # is queued (design doc § "Open questions"); v1 emits byte
143
- # columns which are correct for ASCII source.
127
+ # Rigor uses 1-based line + 1-based byte column; LSP uses 0-based line + 0-based UTF-16 code unit.
128
+ # UTF-16 conversion is queued (design doc § "Open questions"); v1 emits byte columns which are
129
+ # correct for ASCII source.
144
130
  line = (diagnostic.line - 1).clamp(0, Float::INFINITY).to_i
145
131
  character = (diagnostic.column - 1).clamp(0, Float::INFINITY).to_i
146
132
 
@@ -7,11 +7,9 @@ require_relative "buffer_resolution"
7
7
 
8
8
  module Rigor
9
9
  module LanguageServer
10
- # Answers `textDocument/documentSymbol` requests by walking the
11
- # Prism AST and emitting one LSP `DocumentSymbol` per
12
- # `ClassNode` / `ModuleNode` / `DefNode`. Nested classes /
13
- # modules / methods nest in the `children` array so the editor's
14
- # outline tree mirrors the source structure.
10
+ # Answers `textDocument/documentSymbol` requests by walking the Prism AST and emitting one LSP
11
+ # `DocumentSymbol` per `ClassNode` / `ModuleNode` / `DefNode`. Nested classes / modules / methods nest in
12
+ # the `children` array so the editor's outline tree mirrors the source structure.
15
13
  #
16
14
  # SymbolKind mapping (LSP § "SymbolKind"):
17
15
  # - Class (5) — `class Foo`
@@ -41,8 +39,8 @@ module Rigor
41
39
 
42
40
  parse_result = Prism.parse(entry.bytes, filepath: path,
43
41
  version: @project_context.configuration.target_ruby)
44
- # Tolerate partial parse errors: walk what Prism gave us
45
- # anyway. Editors prefer a stale outline over no outline.
42
+ # Tolerate partial parse errors: walk what Prism gave us anyway. Editors prefer a stale outline over
43
+ # no outline.
46
44
  walk_top_level(parse_result.value)
47
45
  end
48
46
 
@@ -97,10 +95,9 @@ module Rigor
97
95
 
98
96
  def def_symbol(node, in_namespace:)
99
97
  name = node.name.to_s
100
- # `def self.foo` (singleton-class) → singleton-method shape.
101
- # We surface the same as instance methods in v1 (LSP kind
102
- # has no distinct "ClassMethod" code); the textual `self.`
103
- # prefix preserves the distinction visually.
98
+ # `def self.foo` (singleton-class) → singleton-method shape. We surface the same as instance methods
99
+ # in v1 (LSP kind has no distinct "ClassMethod" code); the textual `self.` prefix preserves the
100
+ # distinction visually.
104
101
  display_name = node.receiver.is_a?(Prism::SelfNode) ? "self.#{name}" : name
105
102
  {
106
103
  name: display_name,
@@ -111,10 +108,9 @@ module Rigor
111
108
  }
112
109
  end
113
110
 
114
- # Renders a `ConstantReadNode` / `ConstantPathNode` as its
115
- # fully-qualified name string (e.g. `Foo::Bar::Baz`). Returns
116
- # the slot-name source when the node shape is unrecognised so
117
- # the outline still has something to display.
111
+ # Renders a `ConstantReadNode` / `ConstantPathNode` as its fully-qualified name string (e.g.
112
+ # `Foo::Bar::Baz`). Returns the slot-name source when the node shape is unrecognised so the outline
113
+ # still has something to display.
118
114
  def qualified_name_of(node)
119
115
  case node
120
116
  when Prism::ConstantReadNode
@@ -127,10 +123,9 @@ module Rigor
127
123
  end
128
124
  end
129
125
 
130
- # LSP `Range` is 0-based start + end with `character` in
131
- # UTF-16 code units. This implementation emits byte columns
132
- # (correct for ASCII source); UTF-16 conversion stays queued
133
- # per design doc § "Open questions".
126
+ # LSP `Range` is 0-based start + end with `character` in UTF-16 code units. This implementation emits
127
+ # byte columns (correct for ASCII source); UTF-16 conversion stays queued per design doc § "Open
128
+ # questions".
134
129
  def range_from(location)
135
130
  {
136
131
  start: { line: location.start_line - 1, character: location.start_column },