rigortype 0.3.8 → 0.4.0
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.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/data/capability_roles/capability_roles.rbs +36 -0
- data/data/core_overlay/enumerable.rbs +51 -0
- data/data/core_overlay/enumerator.rbs +84 -0
- data/data/core_overlay/hash_rbs3.rbs +41 -0
- data/data/core_overlay/process.rbs +40 -0
- data/data/core_overlay/string_io.rbs +33 -0
- data/data/effects/core.yml +3 -3
- data/data/gem_overlay/activesupport/core_ext.rbs +236 -17
- data/docs/handbook/02-everyday-types.md +17 -14
- data/docs/handbook/03-narrowing.md +132 -14
- data/docs/handbook/04-tuples-and-shapes.md +8 -6
- data/docs/handbook/05-methods-and-blocks.md +1 -1
- data/docs/handbook/06-classes.md +2 -2
- data/docs/handbook/07-rbs-and-extended.md +19 -1
- data/docs/handbook/10-sorbet.md +9 -10
- data/docs/handbook/11-sig-gen.md +525 -13
- data/docs/handbook/README.md +1 -1
- data/docs/handbook/appendix-elixir.md +2 -2
- data/docs/handbook/appendix-go.md +2 -2
- data/docs/handbook/appendix-java-csharp.md +2 -2
- data/docs/handbook/appendix-mypy.md +2 -2
- data/docs/handbook/appendix-phpstan.md +1 -1
- data/docs/handbook/appendix-rust.md +1 -1
- data/docs/handbook/appendix-type-theory.md +4 -4
- data/docs/handbook/appendix-typescript.md +1 -1
- data/docs/llms.txt +2 -0
- data/docs/manual/02-cli-reference.md +95 -6
- data/docs/manual/03-configuration.md +18 -0
- data/docs/manual/04-diagnostics.md +15 -0
- data/docs/manual/07-plugins.md +15 -5
- data/docs/manual/08-skills.md +23 -2
- data/docs/manual/10-mcp-server.md +3 -2
- data/docs/manual/11-ci.md +9 -0
- data/docs/manual/15-type-protection-coverage.md +8 -0
- data/docs/manual/16-rbs-extended-annotations.md +46 -8
- data/docs/manual/18-removing-dead-code.md +10 -8
- data/docs/manual/19-effect-labels.md +10 -0
- data/docs/manual/plugins/README.md +7 -0
- data/docs/manual/plugins/rigor-actioncable.md +8 -1
- data/docs/manual/plugins/rigor-actionmailer.md +7 -0
- data/docs/manual/plugins/rigor-actionpack.md +261 -0
- data/docs/manual/plugins/rigor-active-model-serializers.md +153 -0
- data/docs/manual/plugins/rigor-activejob.md +7 -0
- data/docs/manual/plugins/rigor-activerecord.md +138 -4
- data/docs/manual/plugins/rigor-activestorage.md +8 -1
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +43 -7
- data/docs/manual/plugins/rigor-grape.md +106 -0
- data/docs/manual/plugins/rigor-graphql.md +23 -2
- data/docs/manual/plugins/rigor-pundit.md +8 -1
- data/docs/manual/plugins/rigor-rails-i18n.md +10 -5
- data/docs/manual/plugins/rigor-rails-routes.md +7 -0
- data/docs/manual/plugins/rigor-rbs-inline.md +229 -1
- data/docs/manual/plugins/rigor-sidekiq.md +8 -1
- data/docs/manual/plugins/rigor-sorbet.md +21 -5
- data/exe/rigor +19 -4
- data/lib/rigor/analysis/baseline.rb +3 -3
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +167 -2
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +189 -0
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +6 -4
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
- data/lib/rigor/analysis/check_rules/rule_ids.rb +21 -6
- data/lib/rigor/analysis/check_rules/rule_walk.rb +41 -5
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/source_arity.rb +323 -0
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +146 -0
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +38 -4
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
- data/lib/rigor/analysis/check_rules.rb +718 -79
- data/lib/rigor/analysis/crash_signature.rb +8 -24
- data/lib/rigor/analysis/dependency_recorder.rb +23 -0
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
- data/lib/rigor/analysis/erb_template_detector.rb +1 -2
- data/lib/rigor/analysis/fact_store.rb +15 -1
- data/lib/rigor/analysis/incremental.rb +42 -0
- data/lib/rigor/analysis/incremental_session.rb +117 -26
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
- data/lib/rigor/analysis/project_scan.rb +11 -1
- data/lib/rigor/analysis/reachability/graph.rb +3 -5
- data/lib/rigor/analysis/reachability/plugin_roots.rb +5 -5
- data/lib/rigor/analysis/reachability/project_files.rb +2 -2
- data/lib/rigor/analysis/reachability/scan.rb +5 -5
- data/lib/rigor/analysis/reachability/scan_cache.rb +3 -2
- data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
- data/lib/rigor/analysis/result.rb +1 -3
- data/lib/rigor/analysis/rule_catalog.rb +131 -1
- data/lib/rigor/analysis/run_cache_key.rb +21 -12
- data/lib/rigor/analysis/run_cache_probe.rb +3 -5
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +340 -33
- data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +18 -14
- data/lib/rigor/analysis/runner/pool_coordinator.rb +357 -97
- data/lib/rigor/analysis/runner/project_pre_passes.rb +38 -13
- data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
- data/lib/rigor/analysis/runner.rb +407 -51
- data/lib/rigor/analysis/template_unit_collector.rb +303 -0
- data/lib/rigor/analysis/template_unit_paths.rb +91 -0
- data/lib/rigor/analysis/template_unit_positions.rb +293 -0
- data/lib/rigor/analysis/template_units.rb +399 -0
- data/lib/rigor/analysis/worker_session.rb +62 -20
- data/lib/rigor/bleeding_edge.rb +10 -24
- data/lib/rigor/builtins/hkt_builtins.rb +3 -2
- data/lib/rigor/builtins/imported_refinements.rb +159 -21
- data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
- data/lib/rigor/builtins/regex_refinement.rb +21 -13
- data/lib/rigor/builtins/static_return_refinements.rb +7 -6
- data/lib/rigor/cache/annotation_location.rb +2 -4
- data/lib/rigor/cache/descriptor.rb +63 -20
- data/lib/rigor/cache/engine_source.rb +33 -5
- data/lib/rigor/cache/file_digest.rb +9 -1
- data/lib/rigor/cache/incremental_snapshot.rb +72 -6
- data/lib/rigor/cache/rbs_cache_producer.rb +4 -0
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
- data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
- data/lib/rigor/cache/rbs_constant_table.rb +0 -3
- data/lib/rigor/cache/rbs_descriptor.rb +112 -14
- data/lib/rigor/cache/rbs_environment.rb +8 -4
- data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
- data/lib/rigor/cache/store.rb +76 -32
- data/lib/rigor/ci_detector.rb +1 -0
- data/lib/rigor/cli/annotate_command.rb +5 -6
- data/lib/rigor/cli/check_command.rb +57 -10
- data/lib/rigor/cli/check_invocation.rb +6 -11
- data/lib/rigor/cli/check_runner_factory.rb +1 -5
- data/lib/rigor/cli/coverage_command.rb +1 -1
- data/lib/rigor/cli/coverage_mutation.rb +12 -3
- data/lib/rigor/cli/coverage_scan.rb +1 -5
- data/lib/rigor/cli/diff_command.rb +1 -1
- data/lib/rigor/cli/doc_links.rb +4 -4
- data/lib/rigor/cli/docs_command.rb +6 -6
- data/lib/rigor/cli/doctor_command.rb +85 -38
- data/lib/rigor/cli/effects_command.rb +2 -2
- data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
- data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
- data/lib/rigor/cli/explain_command.rb +34 -1
- data/lib/rigor/cli/fused_protection_report.rb +7 -1
- data/lib/rigor/cli/lsp_command.rb +1 -1
- data/lib/rigor/cli/mcp_command.rb +1 -1
- data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
- data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
- data/lib/rigor/cli/mutation_protection_report.rb +7 -1
- data/lib/rigor/cli/plugin_command.rb +4 -4
- data/lib/rigor/cli/plugins_command.rb +2 -1
- data/lib/rigor/cli/prism_colorizer.rb +1 -2
- data/lib/rigor/cli/protection_fork_scan.rb +6 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
- data/lib/rigor/cli/sig_gen_command.rb +212 -31
- data/lib/rigor/cli/skill_command.rb +2 -2
- data/lib/rigor/cli/skill_deep_probe.rb +4 -4
- data/lib/rigor/cli/skill_describe.rb +35 -35
- data/lib/rigor/cli/trace_command.rb +4 -4
- data/lib/rigor/cli/trace_renderer.rb +5 -6
- data/lib/rigor/cli/triage_command.rb +1 -1
- data/lib/rigor/cli/type_of_command.rb +29 -11
- data/lib/rigor/cli/type_of_renderer.rb +20 -9
- data/lib/rigor/cli/type_of_template_probe.rb +189 -0
- data/lib/rigor/cli/type_scan_command.rb +4 -4
- data/lib/rigor/cli/unused_command.rb +11 -3
- data/lib/rigor/cli/upgrade_command.rb +1 -1
- data/lib/rigor/cli.rb +89 -9
- data/lib/rigor/config_audit.rb +30 -5
- data/lib/rigor/configuration/severity_profile.rb +25 -9
- data/lib/rigor/configuration.rb +109 -7
- data/lib/rigor/effects/ancestry_recorder.rb +191 -0
- data/lib/rigor/effects/attribution.rb +12 -5
- data/lib/rigor/effects/callee_rule.rb +368 -0
- data/lib/rigor/effects/catalog.rb +7 -4
- data/lib/rigor/effects/collector.rb +11 -5
- data/lib/rigor/effects/config_envelopes.rb +15 -10
- data/lib/rigor/effects/definition_context.rb +179 -0
- data/lib/rigor/effects/definition_lines.rb +25 -6
- data/lib/rigor/effects/effect_table.rb +11 -4
- data/lib/rigor/effects/entry_points.rb +0 -2
- data/lib/rigor/effects/envelope_check.rb +9 -9
- data/lib/rigor/effects/envelope_index.rb +20 -8
- data/lib/rigor/effects/file_collection.rb +59 -5
- data/lib/rigor/effects/framework_units.rb +5 -7
- data/lib/rigor/effects/identity.rb +20 -6
- data/lib/rigor/effects/inline_anchor.rb +7 -7
- data/lib/rigor/effects/label_intent.rb +3 -4
- data/lib/rigor/effects/liskov_check.rb +8 -8
- data/lib/rigor/effects/local_ownership.rb +38 -12
- data/lib/rigor/effects/method_key.rb +22 -1
- data/lib/rigor/effects/mutation_classifier.rb +23 -12
- data/lib/rigor/effects/plugin_facts.rb +47 -37
- data/lib/rigor/effects/propagator.rb +297 -19
- data/lib/rigor/effects/registry.rb +1 -1
- data/lib/rigor/effects/scanner.rb +122 -73
- data/lib/rigor/effects/signature_sources.rb +4 -6
- data/lib/rigor/effects/snapshot.rb +12 -11
- data/lib/rigor/effects/snapshot_diff.rb +1 -1
- data/lib/rigor/effects/summary.rb +27 -4
- data/lib/rigor/effects/unit_scan.rb +393 -38
- data/lib/rigor/effects/unknown_label_check.rb +3 -8
- data/lib/rigor/effects/unknown_label_report.rb +3 -4
- data/lib/rigor/effects/visibility.rb +101 -0
- data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
- data/lib/rigor/environment/class_registry.rb +3 -1
- data/lib/rigor/environment/failure_slot.rb +2 -2
- data/lib/rigor/environment/installed_gem_set.rb +85 -0
- data/lib/rigor/environment/lockfile_resolver.rb +68 -4
- data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
- data/lib/rigor/environment/member_consistency.rb +298 -0
- data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
- data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
- data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
- data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
- data/lib/rigor/environment/rbs_loader.rb +674 -79
- data/lib/rigor/environment.rb +174 -24
- data/lib/rigor/flow_contribution/merger.rb +0 -2
- data/lib/rigor/flow_contribution.rb +11 -13
- data/lib/rigor/hashing/xxh3.rb +264 -0
- data/lib/rigor/inference/acceptance.rb +292 -17
- data/lib/rigor/inference/block_auto_splat.rb +216 -0
- data/lib/rigor/inference/block_call_timing.rb +338 -0
- data/lib/rigor/inference/block_parameter_binder.rb +75 -30
- data/lib/rigor/inference/block_repetition.rb +71 -0
- data/lib/rigor/inference/body_fixpoint.rb +7 -6
- data/lib/rigor/inference/budget_trace.rb +3 -6
- data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
- data/lib/rigor/inference/builtins/string_catalog.rb +1 -1
- data/lib/rigor/inference/captured_locals.rb +389 -18
- data/lib/rigor/inference/closure_escape_analyzer.rb +159 -17
- data/lib/rigor/inference/content_join.rb +200 -27
- data/lib/rigor/inference/coverage_scanner.rb +4 -6
- data/lib/rigor/inference/def_return_typer.rb +11 -7
- data/lib/rigor/inference/define_method_block_self.rb +64 -0
- data/lib/rigor/inference/dynamic_origin.rb +1 -1
- data/lib/rigor/inference/element_read_widening.rb +180 -0
- data/lib/rigor/inference/error_info.rb +196 -0
- data/lib/rigor/inference/expression_typer.rb +2124 -468
- data/lib/rigor/inference/external_ancestor_resolution.rb +267 -0
- data/lib/rigor/inference/fork_map.rb +5 -7
- data/lib/rigor/inference/fresh_frame_blocks.rb +127 -0
- data/lib/rigor/inference/global_write_census.rb +239 -0
- data/lib/rigor/inference/guard_rebinding.rb +447 -0
- data/lib/rigor/inference/hash_lookup_mutation.rb +88 -0
- data/lib/rigor/inference/hkt_reducer.rb +2 -3
- data/lib/rigor/inference/hkt_registry.rb +4 -7
- data/lib/rigor/inference/index_write_widening.rb +16 -7
- data/lib/rigor/inference/indexed_narrowing.rb +61 -9
- data/lib/rigor/inference/jump_targets.rb +82 -0
- data/lib/rigor/inference/last_line/implicit_self.rb +210 -0
- data/lib/rigor/inference/last_line/self_evidence.rb +329 -0
- data/lib/rigor/inference/last_line.rb +340 -0
- data/lib/rigor/inference/last_status.rb +144 -0
- data/lib/rigor/inference/macro_block_self_type.rb +168 -21
- data/lib/rigor/inference/match_rebinding/calls.rb +281 -0
- data/lib/rigor/inference/match_rebinding/frame.rb +148 -0
- data/lib/rigor/inference/match_rebinding/operands.rb +236 -0
- data/lib/rigor/inference/match_rebinding/self_calls.rb +83 -0
- data/lib/rigor/inference/match_rebinding.rb +392 -0
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +87 -33
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +147 -0
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/hash_transform_keys_folding.rb +191 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +6 -2
- data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
- data/lib/rigor/inference/method_dispatcher/match_data_folding.rb +159 -0
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +165 -111
- data/lib/rigor/inference/method_dispatcher/process_folding.rb +56 -0
- data/lib/rigor/inference/method_dispatcher/proven_overload.rb +72 -0
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +1046 -69
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +142 -0
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +129 -54
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -6
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +225 -25
- data/lib/rigor/inference/method_parameter_binder.rb +12 -9
- data/lib/rigor/inference/multi_target_binder.rb +341 -55
- data/lib/rigor/inference/mutation_rejoin.rb +171 -0
- data/lib/rigor/inference/mutation_widening.rb +156 -85
- data/lib/rigor/inference/narrowing.rb +835 -162
- data/lib/rigor/inference/operand_effects.rb +167 -0
- data/lib/rigor/inference/operand_walk.rb +88 -0
- data/lib/rigor/inference/optimistic_origin.rb +153 -16
- data/lib/rigor/inference/origin_lookup.rb +2 -3
- data/lib/rigor/inference/parameter_inference_collector.rb +7 -7
- data/lib/rigor/inference/pre_eval_constants.rb +6 -5
- data/lib/rigor/inference/precision_scanner.rb +3 -4
- data/lib/rigor/inference/project_method_ownership.rb +136 -0
- data/lib/rigor/inference/project_patched_methods.rb +9 -4
- data/lib/rigor/inference/project_patched_scanner.rb +10 -6
- data/lib/rigor/inference/protection_scanner.rb +1 -2
- data/lib/rigor/inference/range_constant.rb +57 -0
- data/lib/rigor/inference/rbs_type_translator.rb +49 -11
- data/lib/rigor/inference/receiver_alias.rb +93 -4
- data/lib/rigor/inference/receiver_blind_block.rb +219 -0
- data/lib/rigor/inference/refinement_mutation.rb +76 -0
- data/lib/rigor/inference/repeated_or_writes.rb +463 -0
- data/lib/rigor/inference/return_barrier.rb +54 -0
- data/lib/rigor/inference/rewrite_mutation.rb +120 -0
- data/lib/rigor/inference/scope_indexer.rb +5228 -627
- data/lib/rigor/inference/statement_evaluator.rb +3452 -595
- data/lib/rigor/inference/stored_block_call.rb +54 -0
- data/lib/rigor/inference/string_mutation.rb +97 -0
- data/lib/rigor/inference/struct_fold_safety.rb +6 -7
- data/lib/rigor/inference/synthetic_method_index.rb +1 -2
- data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
- data/lib/rigor/inference/unknown_store_widening.rb +200 -0
- data/lib/rigor/inference/unthreaded_rebinds.rb +282 -0
- data/lib/rigor/inference/version_guard.rb +28 -21
- data/lib/rigor/inference/void_origin.rb +3 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -4
- data/lib/rigor/language_server/buffer_table.rb +4 -4
- data/lib/rigor/language_server/completion_provider.rb +1 -1
- data/lib/rigor/language_server/debouncer.rb +0 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +7 -6
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -1
- data/lib/rigor/language_server/hover_provider.rb +1 -1
- data/lib/rigor/language_server/hover_renderer.rb +4 -4
- data/lib/rigor/language_server/incremental_sync.rb +6 -6
- data/lib/rigor/language_server/project_context.rb +7 -8
- data/lib/rigor/language_server/publish_batcher.rb +2 -2
- data/lib/rigor/language_server/selection_range_provider.rb +2 -2
- data/lib/rigor/language_server/server.rb +9 -9
- data/lib/rigor/language_server/signature_help_provider.rb +1 -1
- data/lib/rigor/language_server/uri.rb +1 -1
- data/lib/rigor/mcp/server.rb +2 -1
- data/lib/rigor/plugin/base.rb +258 -26
- data/lib/rigor/plugin/box_probe.rb +91 -0
- data/lib/rigor/plugin/bundled_catalog.rb +167 -0
- data/lib/rigor/plugin/effect_attribution.rb +65 -11
- data/lib/rigor/plugin/effect_entry_points.rb +3 -3
- data/lib/rigor/plugin/fact_store.rb +6 -6
- data/lib/rigor/plugin/io_boundary.rb +93 -8
- data/lib/rigor/plugin/isolation.rb +29 -6
- data/lib/rigor/plugin/loader.rb +33 -13
- data/lib/rigor/plugin/macro/block_as_method.rb +45 -6
- data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
- data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
- data/lib/rigor/plugin/manifest.rb +94 -11
- data/lib/rigor/plugin/registry.rb +76 -12
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +21 -4
- data/lib/rigor/plugin/template_unit.rb +196 -0
- data/lib/rigor/plugin/trust_policy.rb +2 -4
- data/lib/rigor/plugin/type_node_resolver.rb +3 -3
- data/lib/rigor/plugin.rb +1 -0
- data/lib/rigor/plugin_gap_advisory.rb +96 -0
- data/lib/rigor/project_environment.rb +138 -0
- data/lib/rigor/protection/analysis_guard.rb +93 -14
- data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
- data/lib/rigor/protection/dependency_closure.rb +5 -8
- data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
- data/lib/rigor/protection/discovery_seed.rb +14 -12
- data/lib/rigor/protection/kill_signature.rb +1 -4
- data/lib/rigor/protection/measurement_integrity.rb +1 -3
- data/lib/rigor/protection/mutation_cache.rb +7 -11
- data/lib/rigor/protection/mutation_scanner.rb +12 -15
- data/lib/rigor/protection/mutator.rb +2 -1
- data/lib/rigor/protection/test_suite_oracle.rb +5 -5
- data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/reporter.rb +36 -10
- data/lib/rigor/rbs_extended.rb +88 -21
- data/lib/rigor/reflection/constant_ancestors.rb +97 -0
- data/lib/rigor/reflection/constant_path.rb +139 -0
- data/lib/rigor/reflection.rb +93 -110
- data/lib/rigor/runtime/jit.rb +6 -8
- data/lib/rigor/scope/discovery_index.rb +165 -2
- data/lib/rigor/scope.rb +1041 -32
- data/lib/rigor/sig_gen/alias_index.rb +289 -0
- data/lib/rigor/sig_gen/classification.rb +22 -5
- data/lib/rigor/sig_gen/declaration_equivalence.rb +127 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +202 -0
- data/lib/rigor/sig_gen/generator.rb +673 -53
- data/lib/rigor/sig_gen/inline_declarations.rb +184 -0
- data/lib/rigor/sig_gen/layout_index.rb +3 -4
- data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
- data/lib/rigor/sig_gen/method_candidate.rb +47 -3
- data/lib/rigor/sig_gen/observation_collector.rb +18 -16
- data/lib/rigor/sig_gen/path_mapper.rb +4 -6
- data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
- data/lib/rigor/sig_gen/renderer.rb +128 -14
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +154 -0
- data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
- data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
- data/lib/rigor/sig_gen/write_result.rb +19 -3
- data/lib/rigor/sig_gen/writer.rb +189 -41
- data/lib/rigor/sig_gen.rb +3 -0
- data/lib/rigor/signature_path_audit.rb +153 -6
- data/lib/rigor/source/literals.rb +0 -23
- data/lib/rigor/source/node_children.rb +0 -2
- data/lib/rigor/source/node_locator.rb +5 -11
- data/lib/rigor/source/node_walker.rb +0 -6
- data/lib/rigor/source/parameter_envelope.rb +72 -0
- data/lib/rigor/source.rb +1 -0
- data/lib/rigor/triage/catalogue.rb +1 -3
- data/lib/rigor/triage.rb +3 -5
- data/lib/rigor/type/accepts_result.rb +24 -6
- data/lib/rigor/type/combinator.rb +144 -5
- data/lib/rigor/type/data_class.rb +2 -2
- data/lib/rigor/type/data_instance.rb +4 -4
- data/lib/rigor/type/difference.rb +1 -0
- data/lib/rigor/type/float_range.rb +128 -0
- data/lib/rigor/type/hash_shape.rb +6 -6
- data/lib/rigor/type/integer_range.rb +13 -8
- data/lib/rigor/type/nominal.rb +8 -2
- data/lib/rigor/type/refined.rb +4 -3
- data/lib/rigor/type/struct_class.rb +3 -3
- data/lib/rigor/type/struct_instance.rb +4 -4
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/generic.rb +1 -1
- data/lib/rigor/type_node/range_literal.rb +25 -0
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node.rb +1 -0
- data/lib/rigor/version.rb +1 -1
- data/lib/rigor.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/effects.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +10 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +14 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +32 -30
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +96 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/effects.rb +65 -8
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +270 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +402 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +370 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_units.rb +132 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +264 -3
- data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
- data/plugins/rigor-actionpack/sig/action_view.rbs +43 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +220 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_index.rb +56 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +263 -0
- data/plugins/rigor-active-model-serializers/lib/rigor-active-model-serializers.rb +7 -0
- data/plugins/rigor-active-model-serializers/sig/active_model_serializers.rbs +45 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +2 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +10 -5
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +35 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +72 -12
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +383 -7
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +14 -6
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +322 -41
- data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +206 -31
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +26 -14
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +290 -19
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +3 -0
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +2 -0
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +1 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
- data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +141 -0
- data/plugins/rigor-grape/lib/rigor-grape.rb +6 -0
- data/plugins/rigor-grape/sig/grape.rbs +263 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +78 -5
- data/plugins/rigor-graphql/sig/graphql.rbs +485 -0
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +2 -3
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +12 -7
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +36 -37
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +0 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +5 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +3 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +10 -17
- data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
- data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline/same_line_annotations.rb +149 -0
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +324 -18
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
- data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +2 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +10 -5
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +3 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +51 -6
- data/plugins/rigor-sorbet/sig/sorbet.rbs +277 -0
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
- data/sig/rigor/analysis/baseline.rbs +69 -7
- data/sig/rigor/analysis/fact_store.rbs +1 -1
- data/sig/rigor/analysis/project_scan.rbs +74 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
- data/sig/rigor/cache.rbs +2 -3
- data/sig/rigor/effects/config_envelopes.rbs +100 -0
- data/sig/rigor/effects/effect_table.rbs +60 -0
- data/sig/rigor/effects/envelope.rbs +97 -0
- data/sig/rigor/effects/envelope_index.rbs +33 -0
- data/sig/rigor/effects/file_collection.rbs +81 -0
- data/sig/rigor/effects/label.rbs +26 -0
- data/sig/rigor/effects/label_set.rbs +46 -0
- data/sig/rigor/effects/method_key.rbs +24 -0
- data/sig/rigor/effects/origin.rbs +50 -0
- data/sig/rigor/effects/plugin_facts.rbs +141 -0
- data/sig/rigor/effects/registry.rbs +68 -0
- data/sig/rigor/effects/summary.rbs +50 -0
- data/sig/rigor/effects/taint_cause.rbs +12 -0
- data/sig/rigor/environment.rbs +17 -10
- data/sig/rigor/inference/optimistic_origin.rbs +10 -0
- data/sig/rigor/inference.rbs +9 -14
- data/sig/rigor/plugin/additional_initializer.rbs +36 -0
- data/sig/rigor/plugin/base.rbs +30 -7
- data/sig/rigor/plugin/effect_ancestry.rbs +27 -0
- data/sig/rigor/plugin/effect_attribution.rbs +66 -0
- data/sig/rigor/plugin/effect_edge.rbs +33 -0
- data/sig/rigor/plugin/effect_entry_points.rbs +25 -0
- data/sig/rigor/plugin/io_boundary.rbs +4 -0
- data/sig/rigor/plugin/loader.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +51 -11
- data/sig/rigor/plugin/protocol_contract.rbs +68 -0
- data/sig/rigor/plugin/registry.rbs +62 -1
- data/sig/rigor/plugin.rbs +1 -1
- data/sig/rigor/rbs_extended.rbs +1 -1
- data/sig/rigor/reflection.rbs +9 -6
- data/sig/rigor/scope.rbs +148 -12
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +21 -0
- data/sig/rigor/source.rbs +4 -4
- data/sig/rigor/testing.rbs +10 -4
- data/sig/rigor/type.rbs +33 -1
- data/sig/rigor.rbs +39 -20
- data/skills/rigor-ask/SKILL.md +16 -11
- data/skills/rigor-baseline-reduce/SKILL.md +4 -6
- data/skills/rigor-ci-setup/SKILL.md +17 -21
- data/skills/rigor-doctor/SKILL.md +24 -22
- data/skills/rigor-doctor/references/01-checks.md +97 -33
- data/skills/rigor-editor-setup/SKILL.md +6 -4
- data/skills/rigor-mcp-setup/SKILL.md +5 -4
- data/skills/rigor-monkeypatch-resolve/SKILL.md +5 -3
- data/skills/rigor-next-steps/SKILL.md +4 -2
- data/skills/rigor-plugin-author/SKILL.md +19 -23
- data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +9 -8
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +25 -27
- data/skills/rigor-plugin-author/references/03-test-and-ship.md +6 -8
- data/skills/rigor-plugin-review/SKILL.md +6 -4
- data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +2 -1
- data/skills/rigor-plugin-tune/SKILL.md +4 -2
- data/skills/rigor-project-init/SKILL.md +15 -10
- data/skills/rigor-project-init/references/01-detect.md +13 -9
- data/skills/rigor-project-init/references/02-configure.md +43 -14
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +14 -14
- data/skills/rigor-project-init/references/04-sig-uplift.md +27 -14
- data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
- data/skills/rigor-protection-uplift/SKILL.md +4 -6
- data/skills/rigor-rbs-setup/SKILL.md +4 -2
- data/skills/rigor-type-oracle/SKILL.md +226 -0
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +265 -0
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +125 -0
- data/skills/rigor-unused-adjudicate/SKILL.md +12 -7
- data/skills/rigor-upgrade/SKILL.md +13 -8
- metadata +133 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
|
@@ -20,9 +20,9 @@ module Rigor
|
|
|
20
20
|
@project_context = project_context
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
# @param positions
|
|
23
|
+
# @param positions — LSP `Position[]` — each
|
|
24
24
|
# `{ line:, character: }` 0-based.
|
|
25
|
-
# @return
|
|
25
|
+
# @return one `SelectionRange` per
|
|
26
26
|
# position, or nil when the URI / buffer isn't resolvable.
|
|
27
27
|
def provide(uri, positions)
|
|
28
28
|
path, entry = buffer_for(uri)
|
|
@@ -42,11 +42,11 @@ module Rigor
|
|
|
42
42
|
:signature_help_provider, :folding_range_provider,
|
|
43
43
|
:selection_range_provider, :project_context
|
|
44
44
|
|
|
45
|
-
# @param completion_provider
|
|
45
|
+
# @param completion_provider —
|
|
46
46
|
# resolves `textDocument/completion`. Nil → `MethodNotFound`.
|
|
47
|
-
# @param signature_help_provider
|
|
47
|
+
# @param signature_help_provider —
|
|
48
48
|
# resolves `textDocument/signatureHelp`. Nil → `MethodNotFound`.
|
|
49
|
-
# @param project_context
|
|
49
|
+
# @param project_context — the per-session cache of
|
|
50
50
|
# `Environment` + `Cache::Store` the providers read on every request. When present,
|
|
51
51
|
# `workspace/didChangeWatchedFiles` and `workspace/didChangeConfiguration` invalidate the cache; nil
|
|
52
52
|
# means "no project context": each request rebuilds env from scratch (mainly for specs and backward
|
|
@@ -69,7 +69,7 @@ module Rigor
|
|
|
69
69
|
@project_context = project_context
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
-
# @return
|
|
72
|
+
# @return true once the client has called `exit` and
|
|
73
73
|
# the server has set its terminal exit code. The CLI loop
|
|
74
74
|
# reads this between dispatches to know when to stop.
|
|
75
75
|
def exited?
|
|
@@ -78,10 +78,10 @@ module Rigor
|
|
|
78
78
|
|
|
79
79
|
# Routes one LSP method call.
|
|
80
80
|
#
|
|
81
|
-
# @param method
|
|
82
|
-
# @param params
|
|
81
|
+
# @param method — the LSP method name (e.g. "initialize").
|
|
82
|
+
# @param params — the LSP `params` payload (Hash for
|
|
83
83
|
# request / notification methods; nil for the empty case).
|
|
84
|
-
# @return
|
|
84
|
+
# @return one of:
|
|
85
85
|
# - the response result Hash for request methods,
|
|
86
86
|
# - nil for notification methods,
|
|
87
87
|
# - { error: { code:, message: } } for state / shape errors.
|
|
@@ -191,8 +191,8 @@ module Rigor
|
|
|
191
191
|
caps
|
|
192
192
|
end
|
|
193
193
|
|
|
194
|
-
# `initialized` is a notification — no response body.
|
|
195
|
-
# `workspace/didChangeWatchedFiles`
|
|
194
|
+
# `initialized` is a notification — no response body. Nothing is registered here yet: the server handles
|
|
195
|
+
# `workspace/didChangeWatchedFiles` when a client sends it, but does not register for it dynamically.
|
|
196
196
|
def handle_initialized
|
|
197
197
|
nil
|
|
198
198
|
end
|
|
@@ -44,7 +44,7 @@ module Rigor
|
|
|
44
44
|
@project_context = project_context
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
-
# @return
|
|
47
|
+
# @return LSP `SignatureHelp` payload or nil
|
|
48
48
|
# when the cursor isn't inside a resolvable method call.
|
|
49
49
|
def provide(uri:, line:, character:, context: nil)
|
|
50
50
|
_ = context # Trigger info accepted but not routed in v1.
|
|
@@ -14,7 +14,7 @@ module Rigor
|
|
|
14
14
|
FILE_SCHEME = "file://"
|
|
15
15
|
private_constant :FILE_SCHEME
|
|
16
16
|
|
|
17
|
-
# @return
|
|
17
|
+
# @return absolute filesystem path for a
|
|
18
18
|
# `file://` URI, or nil for unsupported schemes.
|
|
19
19
|
def to_path(uri)
|
|
20
20
|
return nil unless uri.is_a?(String) && uri.start_with?(FILE_SCHEME)
|
data/lib/rigor/mcp/server.rb
CHANGED
|
@@ -94,7 +94,7 @@ module Rigor
|
|
|
94
94
|
type: "string",
|
|
95
95
|
enum: %w[untyped observed],
|
|
96
96
|
description: "Parameter policy: untyped (default) or observed " \
|
|
97
|
-
"(harvests call-site argument types from
|
|
97
|
+
"(harvests call-site argument types from the configured test_paths)"
|
|
98
98
|
},
|
|
99
99
|
config: { type: "string", description: "Path to .rigor.yml (optional)" }
|
|
100
100
|
}
|
|
@@ -134,6 +134,7 @@ module Rigor
|
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
].freeze
|
|
137
|
+
Ractor.make_shareable(TOOLS)
|
|
137
138
|
|
|
138
139
|
def initialize(config_path: nil, err: $stderr)
|
|
139
140
|
@config_path = config_path
|
data/lib/rigor/plugin/base.rb
CHANGED
|
@@ -8,6 +8,7 @@ require "prism"
|
|
|
8
8
|
require_relative "manifest"
|
|
9
9
|
require_relative "node_context"
|
|
10
10
|
require_relative "../analysis/diagnostic"
|
|
11
|
+
require_relative "../cache/engine_source"
|
|
11
12
|
# `producer generation_cap:` defaults to (and validates against) `Cache::Store::UNBOUNDED_GENERATIONS`, and a
|
|
12
13
|
# plugin class body can be evaluated before anything else pulled the cache layer in.
|
|
13
14
|
require_relative "../cache/store"
|
|
@@ -40,6 +41,13 @@ module Rigor
|
|
|
40
41
|
#
|
|
41
42
|
# Rigor::Plugin.register(MyRailsPlugin)
|
|
42
43
|
class Base # rubocop:disable Metrics/ClassLength
|
|
44
|
+
# Issue #701 — the kind wrapper a `dynamic_return receivers:` entry may carry, spelled as RBS spells a
|
|
45
|
+
# class object and as the `call.undefined-method` message prints one, so an author writes the gate the
|
|
46
|
+
# way the diagnostic they are answering names the receiver. Declared on the class rather than inside
|
|
47
|
+
# `class << self` because both the load-time validator and the per-dispatch gate read it.
|
|
48
|
+
SINGLETON_RECEIVER_PATTERN = /\Asingleton\((.+)\)\z/
|
|
49
|
+
private_constant :SINGLETON_RECEIVER_PATTERN
|
|
50
|
+
|
|
43
51
|
class << self
|
|
44
52
|
# Declares the plugin's manifest. Called once at class definition time — the resulting {Manifest} is
|
|
45
53
|
# cached on the class so {Rigor::Plugin::Loader} reads it without constructing the plugin.
|
|
@@ -241,8 +249,21 @@ module Rigor
|
|
|
241
249
|
# end
|
|
242
250
|
#
|
|
243
251
|
# `receivers:` is a non-empty Array of class names; the engine calls the block only when the call's
|
|
244
|
-
# receiver type's class equals or inherits from one of them (via `Environment#class_ordering`)
|
|
245
|
-
# MAY be omitted — then the rule is receiver-independent and
|
|
252
|
+
# receiver type's class equals or inherits from one of them (via `Environment#class_ordering`) AND
|
|
253
|
+
# is of the kind the entry names. It MAY be omitted — then the rule is receiver-independent and
|
|
254
|
+
# fires on `methods:` alone.
|
|
255
|
+
#
|
|
256
|
+
# Issue #701 — an entry carries the receiver KIND in RBS's own spelling. `"Widget"` matches an
|
|
257
|
+
# INSTANCE receiver; `"singleton(Widget)"` matches the class object itself; a rule that wants both
|
|
258
|
+
# lists both:
|
|
259
|
+
#
|
|
260
|
+
# dynamic_return receivers: ["Widget"], methods: [:price] # Widget#price
|
|
261
|
+
# dynamic_return receivers: ["singleton(Widget)"], methods: [:build] # Widget.build
|
|
262
|
+
# dynamic_return receivers: ["Widget", "singleton(Widget)"] # either
|
|
263
|
+
#
|
|
264
|
+
# An instance rule that also answered on the class typed a call the plugin never modelled, and since
|
|
265
|
+
# #653 a plugin's answer suppresses `call.undefined-method` — so the class-level miss went silent on
|
|
266
|
+
# the say-so of a rule written for the instance.
|
|
246
267
|
#
|
|
247
268
|
# `methods:` is an Array of Symbol method names. When provided, the block is skipped unless
|
|
248
269
|
# `call_node.name` is in the list — declarative and cheaper than an in-block guard (the engine
|
|
@@ -261,6 +282,9 @@ module Rigor
|
|
|
261
282
|
# # found; the block does the precise per-call lookup
|
|
262
283
|
# end
|
|
263
284
|
#
|
|
285
|
+
# A callable's entries carry the kind the same way — `-> { names.map { "singleton(#{_1})" } }` for a
|
|
286
|
+
# discovered set of class-level receivers.
|
|
287
|
+
#
|
|
264
288
|
# The callable runs through `instance_exec`, so it reads the plugin's own `#prepare`-built indexes.
|
|
265
289
|
# It MUST be idempotent and post-`#prepare`-safe — reference a lazily-built / memoised index (as
|
|
266
290
|
# activestorage's `attachment_index` and activerecord's `model_index` are), never a value captured at
|
|
@@ -376,11 +400,23 @@ module Rigor
|
|
|
376
400
|
# ADR-52 slice 3 — a run-time callable is resolved per instance after `#prepare`; its shape is
|
|
377
401
|
# checked at resolution time.
|
|
378
402
|
return if receivers.respond_to?(:call)
|
|
379
|
-
return if receivers.is_a?(Array) && !receivers.empty? &&
|
|
403
|
+
return if receivers.is_a?(Array) && !receivers.empty? &&
|
|
404
|
+
receivers.all? { |r| valid_dynamic_return_receiver?(r) }
|
|
380
405
|
|
|
381
406
|
raise ArgumentError,
|
|
382
407
|
"Plugin::Base.dynamic_return receivers: must be a non-empty Array of class-name Strings " \
|
|
383
|
-
"
|
|
408
|
+
"(\"Foo\" for an instance receiver, \"singleton(Foo)\" for the class itself) or a " \
|
|
409
|
+
"callable, got #{receivers.inspect}"
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
# Issue #701 — the entry grammar: a non-empty class name, optionally wrapped in RBS's own
|
|
413
|
+
# `singleton(...)`. A bare `singleton()` is rejected rather than read as a class literally named
|
|
414
|
+
# that, so a typo in the kind wrapper fails at load instead of never matching.
|
|
415
|
+
def valid_dynamic_return_receiver?(entry)
|
|
416
|
+
return false unless entry.is_a?(String) && !entry.empty?
|
|
417
|
+
return SINGLETON_RECEIVER_PATTERN.match?(entry) if entry.start_with?("singleton(")
|
|
418
|
+
|
|
419
|
+
true
|
|
384
420
|
end
|
|
385
421
|
|
|
386
422
|
def validate_dynamic_return_methods!(methods)
|
|
@@ -397,7 +433,8 @@ module Rigor
|
|
|
397
433
|
end
|
|
398
434
|
|
|
399
435
|
private :validate_dynamic_return_gate!, :validate_dynamic_return_receivers!,
|
|
400
|
-
:validate_dynamic_return_methods!, :validate_dynamic_return_file_methods
|
|
436
|
+
:validate_dynamic_return_methods!, :validate_dynamic_return_file_methods!,
|
|
437
|
+
:valid_dynamic_return_receiver?
|
|
401
438
|
|
|
402
439
|
# ADR-37 slice 2 — declares a predicate/assertion narrowing contribution, method-gated. The narrow
|
|
403
440
|
# successor to the `post_return_facts` slot of the deleted `flow_contribution_for` hook (ADR-52 WD3):
|
|
@@ -456,6 +493,10 @@ module Rigor
|
|
|
456
493
|
@fact_cache = {}
|
|
457
494
|
@producer_value_cache = {}
|
|
458
495
|
@producer_errors = {}
|
|
496
|
+
# Issue #1051 — the run-scoped disclosure table ({#disclose_once}, and since #1060 {#emit_once}).
|
|
497
|
+
# Allocated here for the same reason as the three memos above: a registration is a Hash-content
|
|
498
|
+
# mutation, so it stays sound on a plugin that freezes itself in `initialize`.
|
|
499
|
+
@run_disclosures = {}
|
|
459
500
|
end
|
|
460
501
|
|
|
461
502
|
# Override in subclasses to wire any state the plugin needs from the injected service container.
|
|
@@ -509,6 +550,38 @@ module Rigor
|
|
|
509
550
|
[]
|
|
510
551
|
end
|
|
511
552
|
|
|
553
|
+
# #392 — the **template-unit** transform: compile one non-Ruby project file into the Ruby the engine
|
|
554
|
+
# analyses (ADR-16 Tier D, revived; design note § 11.3). The engine globs the manifest's
|
|
555
|
+
# `template_globs:`, reads each match's bytes, and calls this once per match on the PARENT, before any
|
|
556
|
+
# analysis — so the transform runs exactly once per file per run and its result marshals into every
|
|
557
|
+
# fork-pool worker.
|
|
558
|
+
#
|
|
559
|
+
# Returns an `Array<Rigor::Plugin::TemplateUnit>`; an empty array declines the file, which is the
|
|
560
|
+
# right answer for a match the plugin's glob caught but its compiler cannot read. The default returns
|
|
561
|
+
# `[]`, so a plugin that declares no `template_globs:` is never asked and one that does may still
|
|
562
|
+
# decline per file.
|
|
563
|
+
#
|
|
564
|
+
# A raise isolates exactly like `#diagnostics_for_file`: the file contributes no unit and the run
|
|
565
|
+
# continues. The hook is the one place a template-unit plugin runs code — everything downstream is
|
|
566
|
+
# the frozen value object — which is what keeps the engine's analysis of a unit identical to its
|
|
567
|
+
# analysis of a `.rb` file.
|
|
568
|
+
def template_units_for_file(path:, source:) # rubocop:disable Lint/UnusedMethodArgument
|
|
569
|
+
[]
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
# #1047 — called once at the start of every template-unit COLLECTION PASS over this plugin's claim,
|
|
573
|
+
# before any `#template_units_for_file` of that pass and whether or not the pass ends up calling it
|
|
574
|
+
# (a warm pass may carry every unit and compile none). A plugin whose transform reads state gathered
|
|
575
|
+
# ACROSS its claimed files — rigor-actionpack seeds a partial's locals from the render sites in every
|
|
576
|
+
# view — memoises that state on its instance, and a long-lived owner keeps the instance across
|
|
577
|
+
# passes; this is where such a plugin learns that the memo may now be stale. The collector's
|
|
578
|
+
# per-call order is not a substitute: a warm pass offers only the editor's buffer, so no sequence of
|
|
579
|
+
# paths can tell one pass from the next.
|
|
580
|
+
#
|
|
581
|
+
# The default does nothing. A raise is swallowed: the pass continues, and the plugin's own
|
|
582
|
+
# revalidation simply does not run for it.
|
|
583
|
+
def template_units_pass_started; end
|
|
584
|
+
|
|
512
585
|
# ADR-37 slice 1 — runs the plugin's declared {.node_rule}s over one file and returns their
|
|
513
586
|
# diagnostics. The engine owns the single AST walk here so plugin authors never hand-roll a traversal:
|
|
514
587
|
# every node reachable from `root` is offered to each rule whose `node_type` it satisfies
|
|
@@ -545,19 +618,15 @@ module Rigor
|
|
|
545
618
|
|
|
546
619
|
# ADR-37 slice 2 — the return type contributed by this plugin's {.dynamic_return} rules for a call, or
|
|
547
620
|
# nil. The engine calls this from `MethodDispatcher`; a rule fires only when `receiver_type`'s class
|
|
548
|
-
# equals or inherits from one of its declared `receivers
|
|
549
|
-
# Failures isolate to nil.
|
|
621
|
+
# equals or inherits from one of its declared `receivers:` AND is of that entry's kind (#701). First
|
|
622
|
+
# non-nil wins (declaration order). Failures isolate to nil.
|
|
550
623
|
def dynamic_return_type(call_node:, scope:, receiver_type:)
|
|
551
624
|
rules = self.class.dynamic_returns
|
|
552
625
|
return nil if rules.empty? || receiver_type.nil?
|
|
553
626
|
|
|
554
|
-
# `class_name` is nil for a receiver carrier with no nominal class (a refinement dimension, an
|
|
555
|
-
# inferred shape) — fine for a receiver-less (methods-only) rule (ADR-52 WD2), which gates on the
|
|
556
|
-
# method name alone and reads the receiver shape inside its own block.
|
|
557
|
-
class_name = dynamic_return_receiver_class_name(receiver_type)
|
|
558
627
|
environment = scope&.environment
|
|
559
628
|
rules.each do |rule|
|
|
560
|
-
next unless dynamic_return_rule_applies?(rule, call_node,
|
|
629
|
+
next unless dynamic_return_rule_applies?(rule, call_node, receiver_type, environment, scope)
|
|
561
630
|
|
|
562
631
|
result = instance_exec(call_node, scope, &rule[:block])
|
|
563
632
|
return result if result
|
|
@@ -587,6 +656,27 @@ module Rigor
|
|
|
587
656
|
[]
|
|
588
657
|
end
|
|
589
658
|
|
|
659
|
+
# ADR-113 WD4 (#1082) — enumerates the members this plugin synthesizes on `class_name`, for the
|
|
660
|
+
# `rigor lens` declaration map. A `dynamic_return` rule answers a TYPE for one call site at a time;
|
|
661
|
+
# nothing on that path can say "`User` has `email`, `name`, …", which is the fact grep can never
|
|
662
|
+
# find on a DSL-generated member. This hook is that per-class enumeration.
|
|
663
|
+
#
|
|
664
|
+
# Returns an `Array` of `{name:, kind:, type:}` Hashes. `name` is the member name as a caller spells
|
|
665
|
+
# it (String); `kind` is a Symbol grouping same-shaped members (`:column_reader`) — the lens
|
|
666
|
+
# collapses a group into one line; `type` is the `Rigor::Type` the plugin commits to for the member
|
|
667
|
+
# itself, or nil where it does not commit to one. It is the member-level answer, not the best a
|
|
668
|
+
# typed call site can do: a plugin that narrows `user.name` to `String` on a written receiver but
|
|
669
|
+
# declines to type the bare `name` read reports `Dynamic[top]` here. A member the plugin claims
|
|
670
|
+
# but deliberately leaves dynamic carries `Rigor::Type::Combinator.untyped` — the explicit
|
|
671
|
+
# `Dynamic[top]` answer, distinct from nil's "no answer".
|
|
672
|
+
#
|
|
673
|
+
# The hook is off the `check` hot path (ADR-52): `rigor lens` invokes it, `check` never does. It may
|
|
674
|
+
# read state `#prepare` built. The default returns `[]` — a plugin that synthesizes no enumerable
|
|
675
|
+
# members has nothing to declare.
|
|
676
|
+
def declared_members(class_name) # rubocop:disable Lint/UnusedMethodArgument
|
|
677
|
+
[]
|
|
678
|
+
end
|
|
679
|
+
|
|
590
680
|
# Builds a `Rigor::Analysis::Diagnostic` positioned at a Prism `node` for return from
|
|
591
681
|
# `#diagnostics_for_file`. Internalises the 1-based `line` / `start_column + 1` convention every plugin
|
|
592
682
|
# otherwise re-derives by hand, so authors pass the node and the message/severity/rule rather than
|
|
@@ -654,12 +744,108 @@ module Rigor
|
|
|
654
744
|
end
|
|
655
745
|
|
|
656
746
|
# ADR-60 WD4 — the `StandardError` a prior {#producer_value} call rescued for `id`, or nil when it
|
|
657
|
-
# succeeded or was never called. Plugins surface it as a load-error
|
|
658
|
-
#
|
|
747
|
+
# succeeded or was never called. Plugins surface it as a run-scoped `load-error` disclosure through
|
|
748
|
+
# {#disclose_once} (#1056) — a failed index is a fact about the run's inputs, not about a file.
|
|
659
749
|
def producer_error(id)
|
|
660
750
|
@producer_errors[id.to_sym]
|
|
661
751
|
end
|
|
662
752
|
|
|
753
|
+
# Issue #1051 — records a PROJECT-GLOBAL disclosure: a notice about the run's inputs ("the schema file
|
|
754
|
+
# is not there, so column checks are off") rather than about a line of a file. Registering the same
|
|
755
|
+
# `key` again — on this instance or on any other instance of this plugin in the same run — is a no-op,
|
|
756
|
+
# so the run emits the row exactly once however many workers, files or call sites reached it.
|
|
757
|
+
#
|
|
758
|
+
# disclose_once(:missing_schema,
|
|
759
|
+
# message: "rigor-activerecord: schema file `db/schema.rb` not found; …",
|
|
760
|
+
# severity: :info, rule: "load-error")
|
|
761
|
+
#
|
|
762
|
+
# Callable from any hook — `#prepare` (the usual place: the disclosure is a fact about the project,
|
|
763
|
+
# known before a single file is read), `#diagnostics_for_file`, or a `node_rule` block. It returns
|
|
764
|
+
# `nil` and emits NOTHING itself: the engine harvests the table after analysis, de-duplicates it by
|
|
765
|
+
# `(plugin id, key)` across the coordinator and every pool worker, and emits one row per surviving
|
|
766
|
+
# pair at `.rigor.yml:1:1` — the position every other run-level plugin row already uses (plugin load
|
|
767
|
+
# errors, `#prepare` raises, `plugin_trust.read-refused`).
|
|
768
|
+
#
|
|
769
|
+
# That position is the point of the facility. The pre-#1051 idiom was an `@emitted` flag consulted from
|
|
770
|
+
# `#diagnostics_for_file`, which positions the row at whichever file the plugin instance happened to
|
|
771
|
+
# see first — per WORKER under `--workers N`, so the row was duplicated, and after #393 the file it
|
|
772
|
+
# landed on could be a template unit's `.erb` path, reading as a claim about a view. A disclosure has
|
|
773
|
+
# no source position to be right about; `.rigor.yml` is where the user configured the input it is
|
|
774
|
+
# about.
|
|
775
|
+
#
|
|
776
|
+
# `severity:` defaults to `:info`, the grade plugins use for "here is what I recognised"; pass
|
|
777
|
+
# `:warning` for "I could not run". `key` is any object with a stable `#to_s` (a Symbol reads best) and
|
|
778
|
+
# is never shown to the user — it is the de-duplication identity, so it must not interpolate anything
|
|
779
|
+
# that differs between workers.
|
|
780
|
+
def disclose_once(key, message:, severity: :info, rule: "load-error")
|
|
781
|
+
id = key.to_s
|
|
782
|
+
return nil if run_registration_taken?(id, batch: false)
|
|
783
|
+
|
|
784
|
+
@run_disclosures[id] = {
|
|
785
|
+
key: id, message: message.to_s, severity: severity.to_sym, rule: rule.to_s
|
|
786
|
+
}.freeze
|
|
787
|
+
nil
|
|
788
|
+
end
|
|
789
|
+
|
|
790
|
+
# Issue #1060 — the POSITIONED sibling of {#disclose_once}: registers a whole batch of fully built
|
|
791
|
+
# diagnostics under `key`, emitted once per run however many plugin instances (fork-pool workers)
|
|
792
|
+
# register it. Each row keeps its own `path` / `line` / `column` — use this when a project-wide scan
|
|
793
|
+
# produces rows that are right about a position (a view template's line) but no single analysed file
|
|
794
|
+
# owns them, so returning them from `#diagnostics_for_file` would repeat them once per worker.
|
|
795
|
+
#
|
|
796
|
+
# emit_once(:view_diagnostics, scan_view_files(index))
|
|
797
|
+
#
|
|
798
|
+
# The question that chooses between the two channels is whether the row has a position it could be
|
|
799
|
+
# right about: a notice about the run's inputs has none and goes through {#disclose_once}
|
|
800
|
+
# (`.rigor.yml:1:1`); a row naming a real file and line comes here and keeps it.
|
|
801
|
+
#
|
|
802
|
+
# De-duplication is by `key` alone and the first registration wins: a later batch under the same key
|
|
803
|
+
# is dropped whole, on this instance or on any other instance in the run, never merged row by row.
|
|
804
|
+
# Every worker scans the same project, so a row-wise union could only add a worker's partial or
|
|
805
|
+
# divergent view. `key` shares one namespace per plugin with {#disclose_once} — the two channels are
|
|
806
|
+
# one registration table — but a key keeps the kind it was first registered as: repeating a key on the
|
|
807
|
+
# same channel is a no-op, and reusing it on the OTHER channel (either order) raises `ArgumentError`
|
|
808
|
+
# rather than silently dropping a genuine batch or disclosure.
|
|
809
|
+
#
|
|
810
|
+
# Callable from `#prepare`, `#diagnostics_for_file`, or a `node_rule` block; returns `nil` and emits
|
|
811
|
+
# nothing itself. The engine stamps `source_family: "plugin.<id>"` exactly as it does for a per-file
|
|
812
|
+
# row, so the qualified rule and the position a baseline keys on are the ones `#diagnostics_for_file`
|
|
813
|
+
# would have produced. Each row is copied and frozen on registration so the batch rides the fork
|
|
814
|
+
# payload and the Ractor `:done` message unchanged; a non-{Rigor::Analysis::Diagnostic} element
|
|
815
|
+
# raises `ArgumentError`, which the engine reports as this plugin's `runtime-error`.
|
|
816
|
+
def emit_once(key, diagnostics)
|
|
817
|
+
id = key.to_s
|
|
818
|
+
return nil if run_registration_taken?(id, batch: true)
|
|
819
|
+
|
|
820
|
+
batch = Array(diagnostics).map do |row|
|
|
821
|
+
next row.dup.freeze if row.is_a?(Rigor::Analysis::Diagnostic)
|
|
822
|
+
|
|
823
|
+
raise ArgumentError, "emit_once expects Rigor::Analysis::Diagnostic rows, got #{row.class}"
|
|
824
|
+
end
|
|
825
|
+
@run_disclosures[id] = { key: id, diagnostics: batch.freeze }.freeze
|
|
826
|
+
nil
|
|
827
|
+
end
|
|
828
|
+
|
|
829
|
+
# Engine-facing reader for {#disclose_once} and {#emit_once}: this instance's registrations in
|
|
830
|
+
# registration order; a positioned batch is the record carrying `:diagnostics`. Plugin authors never
|
|
831
|
+
# call it — {Rigor::Analysis::Runner} and {Rigor::Analysis::WorkerSession} drain it, and the
|
|
832
|
+
# de-duplication across instances happens there.
|
|
833
|
+
def run_disclosure_records
|
|
834
|
+
@run_disclosures.values
|
|
835
|
+
end
|
|
836
|
+
|
|
837
|
+
# #1060 — true when `id` is already registered on the same channel (the repeat is a no-op); raises when
|
|
838
|
+
# it is registered on the other one, because either answer there would drop a genuine registration.
|
|
839
|
+
def run_registration_taken?(id, batch:)
|
|
840
|
+
existing = @run_disclosures[id]
|
|
841
|
+
return false if existing.nil?
|
|
842
|
+
return true if existing.key?(:diagnostics) == batch
|
|
843
|
+
|
|
844
|
+
taken = batch ? "a #disclose_once disclosure" : "an #emit_once batch"
|
|
845
|
+
raise ArgumentError, "run-scoped key #{id.inspect} is already registered as #{taken}; use a distinct key"
|
|
846
|
+
end
|
|
847
|
+
private :run_registration_taken?
|
|
848
|
+
|
|
663
849
|
# Boilerplate-reduction helper (review §1.3): the "did you mean …?" suggestion every
|
|
664
850
|
# diagnostic-emitting plugin otherwise hand-rolls. Returns the closest of `candidates` to `name` via
|
|
665
851
|
# `DidYouMean::SpellChecker` (the same engine Ruby's own `NoMethodError` hints use), or `nil` when
|
|
@@ -673,7 +859,6 @@ module Rigor
|
|
|
673
859
|
DidYouMean::SpellChecker.new(dictionary: dictionary).correct(name.to_s).first
|
|
674
860
|
end
|
|
675
861
|
|
|
676
|
-
# Convenience accessor — `manifest` on the instance returns the class-level manifest declaration.
|
|
677
862
|
def manifest
|
|
678
863
|
self.class.manifest
|
|
679
864
|
end
|
|
@@ -776,7 +961,12 @@ module Rigor
|
|
|
776
961
|
return compute unless store
|
|
777
962
|
|
|
778
963
|
prefixed_id = "plugin.#{manifest.id}.#{producer_id}"
|
|
779
|
-
|
|
964
|
+
begin
|
|
965
|
+
key_descriptor = compose_key_descriptor(descriptor)
|
|
966
|
+
rescue Cache::EngineSource::Unavailable
|
|
967
|
+
# An unidentifiable engine runs the producer uncached rather than keyed without it (issue #1009).
|
|
968
|
+
return compute
|
|
969
|
+
end
|
|
780
970
|
lambda do
|
|
781
971
|
store.fetch_or_validate(
|
|
782
972
|
producer_id: prefixed_id,
|
|
@@ -803,10 +993,9 @@ module Rigor
|
|
|
803
993
|
# survives only as the building block for the rare producer that needs `FileEntry` rows directly;
|
|
804
994
|
# plugin code calls `watch:`.
|
|
805
995
|
#
|
|
806
|
-
# @param roots
|
|
807
|
-
# @param patterns
|
|
996
|
+
# @param roots — search roots (relative to the project root, or absolute paths)
|
|
997
|
+
# @param patterns — glob suffixes joined under each root via `File.join(root, pattern)`.
|
|
808
998
|
# Multiple patterns union into one descriptor (`"**/*.erb", "**/*.html"` etc.).
|
|
809
|
-
# @return [Rigor::Cache::Descriptor]
|
|
810
999
|
def glob_descriptor(roots, *patterns)
|
|
811
1000
|
files = collect_glob_files(Array(roots), patterns)
|
|
812
1001
|
entries = files.map do |path|
|
|
@@ -836,7 +1025,9 @@ module Rigor
|
|
|
836
1025
|
|
|
837
1026
|
# ADR-37 slice 2 — the class name to match a `dynamic_return` `receivers:` entry against, from a
|
|
838
1027
|
# receiver `Type`. Covers the instance (`Nominal[X]`) and class (`Singleton[X]`) shapes; other carriers
|
|
839
|
-
# decline (nil → no match).
|
|
1028
|
+
# decline (nil → no match). Which of the two KINDS the name is then matched as is
|
|
1029
|
+
# {#dynamic_return_receiver_matches?}'s question — the `Result` / `Maybe` carriers are instances of
|
|
1030
|
+
# their class, like `Nominal`.
|
|
840
1031
|
def dynamic_return_receiver_class_name(receiver_type)
|
|
841
1032
|
case receiver_type
|
|
842
1033
|
when Rigor::Type::Nominal, Rigor::Type::Singleton then receiver_type.class_name
|
|
@@ -848,7 +1039,7 @@ module Rigor
|
|
|
848
1039
|
# The gate for one `dynamic_return` rule. Method-name gate first — a Symbol-array probe vs the receiver
|
|
849
1040
|
# ancestry resolution below (ADR-52 WD1); both are pure predicates, so order only affects cost. A
|
|
850
1041
|
# receiver-less rule (ADR-52 WD2) skips the ancestry check entirely and fires on the method name alone.
|
|
851
|
-
def dynamic_return_rule_applies?(rule, call_node,
|
|
1042
|
+
def dynamic_return_rule_applies?(rule, call_node, receiver_type, environment, scope)
|
|
852
1043
|
return false if rule[:methods] && !resolved_dynamic_return_methods(rule).include?(call_node.name)
|
|
853
1044
|
|
|
854
1045
|
if rule[:file_methods]
|
|
@@ -858,11 +1049,29 @@ module Rigor
|
|
|
858
1049
|
return false unless resolved_dynamic_return_file_methods(rule, path).include?(call_node.name)
|
|
859
1050
|
end
|
|
860
1051
|
|
|
861
|
-
|
|
862
|
-
return true if
|
|
1052
|
+
entries = resolved_dynamic_return_receiver_entries(rule)
|
|
1053
|
+
return true if entries.nil?
|
|
1054
|
+
|
|
1055
|
+
dynamic_return_receiver_matches?(entries, receiver_type, environment)
|
|
1056
|
+
end
|
|
1057
|
+
|
|
1058
|
+
# Issue #701 — the receiver gate, kind included. An entry written for an instance (`"Widget"`) must not
|
|
1059
|
+
# answer for the class object: the block was never given the class-level call to model, and since #653
|
|
1060
|
+
# its answer would suppress that call's `call.undefined-method`, hiding a genuine miss on the plugin's
|
|
1061
|
+
# say-so. A rule that wants the class object declares `"singleton(Widget)"`, and one that wants both
|
|
1062
|
+
# lists both.
|
|
1063
|
+
#
|
|
1064
|
+
# A nil class name is a carrier with no nominal class (a refinement dimension, an inferred shape); a
|
|
1065
|
+
# receiver-gated rule cannot speak about it and declines. A receiver-less rule (ADR-52 WD2) never
|
|
1066
|
+
# reaches here.
|
|
1067
|
+
def dynamic_return_receiver_matches?(entries, receiver_type, environment)
|
|
1068
|
+
class_name = dynamic_return_receiver_class_name(receiver_type)
|
|
863
1069
|
return false if class_name.nil?
|
|
864
1070
|
|
|
865
|
-
|
|
1071
|
+
singleton = receiver_type.is_a?(Rigor::Type::Singleton)
|
|
1072
|
+
entries.any? do |name, singleton_entry|
|
|
1073
|
+
singleton_entry == singleton && class_matches_receiver?(class_name, name, environment)
|
|
1074
|
+
end
|
|
866
1075
|
end
|
|
867
1076
|
|
|
868
1077
|
# ADR-52 slice 4 — the rule's method-name set. A static Array is returned as-is (`#include?` over
|
|
@@ -910,6 +1119,24 @@ module Rigor
|
|
|
910
1119
|
Array(instance_exec(&receivers)).map { |c| c.to_s.dup.freeze }.freeze
|
|
911
1120
|
end
|
|
912
1121
|
|
|
1122
|
+
# Issue #701 — the resolved `receivers:` entries as `[class_name, singleton?]` pairs, or nil for a
|
|
1123
|
+
# receiver-less rule. Memoised per rule beside the callable resolution above, so the kind is parsed
|
|
1124
|
+
# once per run rather than once per dispatch (this sits on the per-dispatch plugin path).
|
|
1125
|
+
def resolved_dynamic_return_receiver_entries(rule)
|
|
1126
|
+
receivers = resolved_dynamic_return_receivers(rule)
|
|
1127
|
+
return nil if receivers.nil?
|
|
1128
|
+
|
|
1129
|
+
(@dynamic_return_runtime_cache ||= {})[[:receiver_entries, rule]] ||=
|
|
1130
|
+
receivers.map { |entry| parse_dynamic_return_receiver(entry) }.freeze
|
|
1131
|
+
end
|
|
1132
|
+
|
|
1133
|
+
def parse_dynamic_return_receiver(entry)
|
|
1134
|
+
match = SINGLETON_RECEIVER_PATTERN.match(entry)
|
|
1135
|
+
return [match[1].freeze, true].freeze if match
|
|
1136
|
+
|
|
1137
|
+
[entry, false].freeze
|
|
1138
|
+
end
|
|
1139
|
+
|
|
913
1140
|
# True when `class_name` equals or inherits from `constraint`, matched through
|
|
914
1141
|
# `Environment#class_ordering` (the mechanism `MacroBlockSelfType` / `additional_initializers` use).
|
|
915
1142
|
# Degrades to "no match" on any resolution failure (false-positive-safe).
|
|
@@ -957,8 +1184,13 @@ module Rigor
|
|
|
957
1184
|
# plugin-author-supplied extension carrying IDENTITY inputs (gem-version pins, `ConfigEntry` rows,
|
|
958
1185
|
# configuration-file digests). The IoBoundary read history deliberately does NOT enter the key — it is
|
|
959
1186
|
# recorded post-compute into the dependency descriptor instead (see {#producer_dependency_descriptor}).
|
|
1187
|
+
#
|
|
1188
|
+
# Issue #1009 — the engine's source identity rides in the auto-built half. The manifest version is the
|
|
1189
|
+
# plugin author's promise, and a checkout editing a bundled plugin (or the engine API a producer computes
|
|
1190
|
+
# through) keeps it, so without the identity the next session served the previous build's value into a
|
|
1191
|
+
# run the run-result key had correctly re-analysed.
|
|
960
1192
|
def compose_key_descriptor(extra)
|
|
961
|
-
auto_built = Cache::Descriptor.new(plugins: [plugin_entry])
|
|
1193
|
+
auto_built = Cache::Descriptor.new(plugins: [plugin_entry], configs: Cache::EngineSource.key_config_entries)
|
|
962
1194
|
return auto_built if extra.nil?
|
|
963
1195
|
|
|
964
1196
|
Cache::Descriptor.compose(auto_built, extra)
|
|
@@ -976,7 +1208,7 @@ module Rigor
|
|
|
976
1208
|
Cache::Descriptor.new(
|
|
977
1209
|
files: boundary.files,
|
|
978
1210
|
configs: boundary.configs,
|
|
979
|
-
globs: watch_glob_entries(producer[:watch])
|
|
1211
|
+
globs: (watch_glob_entries(producer[:watch]) + boundary.globs).uniq
|
|
980
1212
|
)
|
|
981
1213
|
end
|
|
982
1214
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rbconfig"
|
|
4
|
+
|
|
5
|
+
module Rigor
|
|
6
|
+
module Plugin
|
|
7
|
+
# Decides, before `exe/rigor` re-execs itself under `RUBY_BOX=1`, whether the running Ruby can host
|
|
8
|
+
# Rigor inside `Ruby::Box` at all. A released Ruby up to and including 4.0.7 cannot: `env_copy()` drops
|
|
9
|
+
# the box a class/module-body proc carries when `Ractor.make_shareable` isolates it, so the first
|
|
10
|
+
# method call inside such a proc dereferences a NULL box and the VM segfaults (Ruby Bug #22260, fixed on
|
|
11
|
+
# CRuby master by `a4ad8e461a`). Rigor defines Ractor-shareable lambdas at module scope as a standing
|
|
12
|
+
# pattern, so on an affected Ruby the `ruby_box` strategy crashes the whole run rather than declining.
|
|
13
|
+
#
|
|
14
|
+
# The check is behavioural, not a version comparison: a child Ruby runs the bug's minimal reproducer
|
|
15
|
+
# under `RUBY_BOX=1` and must print the right answer. That admits a 4.1.0dev build with the fix and a
|
|
16
|
+
# future 4.0.x that backports it, and refuses a development build cut before the fix — none of which a
|
|
17
|
+
# `RUBY_VERSION` bound gets right. The probe costs one short-lived `ruby --disable-gems` process, paid
|
|
18
|
+
# only when a run opts into `ruby_box`.
|
|
19
|
+
#
|
|
20
|
+
# Loaded by `exe/rigor` before anything else, so this file requires nothing from Rigor.
|
|
21
|
+
module BoxProbe
|
|
22
|
+
# A class-body lambda isolated by `Ractor.make_shareable`, then a method dispatch inside it — the
|
|
23
|
+
# four ingredients the crash needs. Exits 2 when `Ruby::Box` is not active (a Ruby without the
|
|
24
|
+
# feature ignores `RUBY_BOX=1`), so a missing feature is told apart from a crash.
|
|
25
|
+
SCRIPT = <<~PROBE
|
|
26
|
+
exit 2 unless defined?(Ruby::Box) && Ruby::Box.respond_to?(:enabled?) && Ruby::Box.enabled?
|
|
27
|
+
module RigorBoxProbe
|
|
28
|
+
RATIONAL = Ractor.make_shareable(lambda { |*args| Rational(*args) })
|
|
29
|
+
end
|
|
30
|
+
print RigorBoxProbe::RATIONAL.call(3, 4).inspect
|
|
31
|
+
PROBE
|
|
32
|
+
|
|
33
|
+
EXPECTED_OUTPUT = "(3/4)"
|
|
34
|
+
|
|
35
|
+
# The reproducer answers in well under a second; a child still running after this is killed and
|
|
36
|
+
# counted as a failure, so a wedged probe cannot hang the launcher (or `rigor lsp` behind it).
|
|
37
|
+
DEADLINE_SECONDS = 10
|
|
38
|
+
|
|
39
|
+
module_function
|
|
40
|
+
|
|
41
|
+
# nil when `ruby` can run Rigor under `RUBY_BOX=1`; otherwise a short reason for the launcher's
|
|
42
|
+
# warning. Only a child killed by a signal is blamed on the bug; any other failure to answer is
|
|
43
|
+
# reported as the probe failing, never as a pass.
|
|
44
|
+
def unsupported_reason(ruby = RbConfig.ruby)
|
|
45
|
+
output, status = run(ruby)
|
|
46
|
+
return "the Ruby::Box probe could not run #{ruby}" if status.nil?
|
|
47
|
+
return nil if status.success? && output == EXPECTED_OUTPUT
|
|
48
|
+
return "Ruby::Box is not available in Ruby #{RUBY_VERSION}" if status.exitstatus == 2
|
|
49
|
+
return "the Ruby::Box probe failed (#{status})" unless status.signaled?
|
|
50
|
+
|
|
51
|
+
"Ruby #{RUBY_VERSION} crashes running Ractor-shareable procs inside Ruby::Box " \
|
|
52
|
+
"(Ruby Bug #22260; a Ruby carrying its fix is needed)"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# `RUBYOPT` is cleared so a `-r` of the caller's (bootsnap, a debugger, a coverage hook) cannot fail
|
|
56
|
+
# to load under `--disable-gems` and be mistaken for the crash; the probe needs nothing but core.
|
|
57
|
+
def run(ruby)
|
|
58
|
+
reader, writer = IO.pipe
|
|
59
|
+
pid = ::Process.spawn({ "RUBY_BOX" => "1", "RUBYOPT" => nil }, ruby, "--disable-gems", "-e", SCRIPT,
|
|
60
|
+
in: File::NULL, out: writer, err: File::NULL)
|
|
61
|
+
writer.close
|
|
62
|
+
output = read_until_eof(reader)
|
|
63
|
+
return [output, ::Process.wait2(pid).last] if output
|
|
64
|
+
|
|
65
|
+
::Process.kill(:KILL, pid)
|
|
66
|
+
::Process.wait(pid)
|
|
67
|
+
[nil, nil]
|
|
68
|
+
rescue ::SystemCallError
|
|
69
|
+
[nil, nil]
|
|
70
|
+
ensure
|
|
71
|
+
writer&.close unless writer&.closed?
|
|
72
|
+
reader&.close
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# The child's whole stdout, or nil when it has not closed it by the deadline.
|
|
76
|
+
def read_until_eof(reader)
|
|
77
|
+
deadline = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) + DEADLINE_SECONDS
|
|
78
|
+
output = +""
|
|
79
|
+
loop do
|
|
80
|
+
remaining = deadline - ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
|
81
|
+
return nil unless remaining.positive? && reader.wait_readable(remaining)
|
|
82
|
+
|
|
83
|
+
chunk = reader.read_nonblock(4096, exception: false)
|
|
84
|
+
return output if chunk.nil?
|
|
85
|
+
|
|
86
|
+
output << chunk if chunk.is_a?(String)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|