rigortype 0.4.5 → 0.4.7
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/gem_overlay/activesupport/core_ext.rbs +16 -13
- data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
- data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
- data/docs/handbook/01-getting-started.md +5 -2
- data/docs/handbook/11-sig-gen.md +61 -5
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/manual/02-cli-reference.md +192 -27
- data/docs/manual/03-configuration.md +38 -4
- data/docs/manual/04-diagnostics.md +1 -1
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/09-editor-integration.md +13 -2
- data/docs/manual/10-mcp-server.md +47 -3
- data/docs/manual/12-caching.md +9 -1
- data/docs/manual/plugins/rigor-actionmailer.md +30 -0
- data/docs/manual/plugins/rigor-actionpack.md +49 -11
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +20 -1
- data/docs/manual/plugins/rigor-devise.md +15 -0
- data/docs/manual/plugins/rigor-rspec.md +2 -1
- data/lib/rigor/analysis/baseline.rb +24 -27
- data/lib/rigor/analysis/buffer_binding.rb +14 -0
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +4 -4
- data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +105 -12
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +143 -15
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +4 -1
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +1 -1
- data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +66 -3
- data/lib/rigor/analysis/check_rules/rebinding_block_callees/dispatch.rb +112 -0
- data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +394 -0
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- data/lib/rigor/analysis/check_rules/ruby_deprecations.rb +7 -0
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
- data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +53 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules.rb +217 -52
- data/lib/rigor/analysis/crash_signature.rb +2 -2
- data/lib/rigor/analysis/dependency_recorder.rb +15 -4
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/edit_replay.rb +154 -0
- data/lib/rigor/analysis/fact_store.rb +3 -3
- data/lib/rigor/analysis/incremental.rb +37 -0
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +2 -2
- data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
- data/lib/rigor/analysis/incremental_session.rb +202 -43
- data/lib/rigor/analysis/path_expansion.rb +41 -0
- data/lib/rigor/analysis/require_relative_closure.rb +100 -0
- data/lib/rigor/analysis/rule_catalog.rb +7 -3
- data/lib/rigor/analysis/run_disclosure_credit.rb +80 -0
- data/lib/rigor/analysis/run_disclosure_ledger.rb +155 -0
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +8 -4
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -1
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +204 -96
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +1 -1
- data/lib/rigor/analysis/runner/pool_coordinator.rb +157 -320
- data/lib/rigor/analysis/runner/project_pre_passes.rb +48 -6
- data/lib/rigor/analysis/runner/run_level_facts.rb +304 -0
- data/lib/rigor/analysis/runner/run_snapshots.rb +44 -54
- data/lib/rigor/analysis/runner/scan_environment.rb +51 -0
- data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
- data/lib/rigor/analysis/runner.rb +233 -120
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +1 -1
- data/lib/rigor/analysis/template_unit_positions.rb +2 -3
- data/lib/rigor/analysis/template_units.rb +9 -1
- data/lib/rigor/analysis/worker_session.rb +38 -33
- data/lib/rigor/bleeding_edge.rb +3 -6
- data/lib/rigor/builtins/imported_refinements.rb +6 -8
- data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
- data/lib/rigor/builtins/regex_refinement.rb +4 -6
- data/lib/rigor/cache/descriptor.rb +6 -5
- data/lib/rigor/cache/incremental_snapshot.rb +60 -18
- data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
- data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
- data/lib/rigor/cache/rbs_constant_table.rb +1 -1
- data/lib/rigor/cache/rbs_descriptor.rb +54 -3
- data/lib/rigor/cache/rbs_environment.rb +4 -1
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
- data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
- data/lib/rigor/cache/store.rb +11 -10
- data/lib/rigor/ci_detector.rb +1 -1
- data/lib/rigor/cli/annotate_command.rb +24 -19
- data/lib/rigor/cli/baseline_command.rb +145 -55
- data/lib/rigor/cli/catalogue.rb +256 -0
- data/lib/rigor/cli/check_command.rb +188 -93
- data/lib/rigor/cli/check_runner_factory.rb +2 -2
- data/lib/rigor/cli/command.rb +15 -1
- data/lib/rigor/cli/coverage_command.rb +21 -8
- data/lib/rigor/cli/coverage_mutation.rb +5 -7
- data/lib/rigor/cli/coverage_renderer.rb +2 -1
- data/lib/rigor/cli/coverage_scan.rb +3 -5
- data/lib/rigor/cli/diff_command.rb +27 -21
- data/lib/rigor/cli/docs_command.rb +1 -2
- data/lib/rigor/cli/doctor_command.rb +12 -12
- data/lib/rigor/cli/effects_command.rb +50 -40
- data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
- data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
- data/lib/rigor/cli/effects_renderer.rb +2 -1
- data/lib/rigor/cli/effects_snapshot_command.rb +18 -17
- data/lib/rigor/cli/explain_command.rb +18 -8
- data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
- data/lib/rigor/cli/json_document.rb +41 -0
- data/lib/rigor/cli/lens_command.rb +17 -13
- data/lib/rigor/cli/lsp_command.rb +250 -23
- data/lib/rigor/cli/mcp_command.rb +14 -8
- data/lib/rigor/cli/mutation_fork_scan.rb +1 -1
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -2
- data/lib/rigor/cli/options.rb +60 -4
- data/lib/rigor/cli/plugin_command.rb +93 -46
- data/lib/rigor/cli/plugins_command.rb +22 -18
- data/lib/rigor/cli/plugins_renderer.rb +34 -8
- data/lib/rigor/cli/protection_fork_scan.rb +2 -2
- data/lib/rigor/cli/protection_renderer.rb +2 -1
- data/lib/rigor/cli/show_bleedingedge_command.rb +23 -16
- data/lib/rigor/cli/sig_gen_command.rb +130 -34
- data/lib/rigor/cli/skill_command.rb +32 -3
- data/lib/rigor/cli/skill_deep_probe.rb +1 -1
- data/lib/rigor/cli/skill_describe.rb +1 -1
- data/lib/rigor/cli/trace_command.rb +14 -9
- data/lib/rigor/cli/triage_command.rb +10 -11
- data/lib/rigor/cli/triage_renderer.rb +2 -1
- data/lib/rigor/cli/type_of_command.rb +18 -12
- data/lib/rigor/cli/type_of_project_scope.rb +24 -0
- data/lib/rigor/cli/type_of_renderer.rb +4 -1
- data/lib/rigor/cli/type_scan_command.rb +14 -9
- data/lib/rigor/cli/type_scan_renderer.rb +2 -1
- data/lib/rigor/cli/unused_command.rb +27 -17
- data/lib/rigor/cli/upgrade_command.rb +5 -0
- data/lib/rigor/cli.rb +209 -54
- data/lib/rigor/config_audit.rb +17 -6
- data/lib/rigor/configuration/severity_profile.rb +5 -5
- data/lib/rigor/configuration.rb +86 -34
- data/lib/rigor/effects/definition_context.rb +0 -2
- data/lib/rigor/effects/envelope.rb +1 -1
- data/lib/rigor/effects/framework_units.rb +1 -1
- data/lib/rigor/effects/mutation_classifier.rb +4 -7
- data/lib/rigor/effects/propagator.rb +1 -1
- data/lib/rigor/effects/registry.rb +2 -2
- data/lib/rigor/effects/snapshot.rb +1 -1
- data/lib/rigor/effects/taint_cause.rb +0 -2
- data/lib/rigor/effects/unit_scan.rb +9 -13
- data/lib/rigor/environment/bundle_sig_discovery.rb +5 -9
- data/lib/rigor/environment/class_registry.rb +3 -3
- data/lib/rigor/environment/core_root.rb +77 -0
- data/lib/rigor/environment/default_libraries.rb +1 -1
- data/lib/rigor/environment/deferred_signature_plan.rb +283 -0
- data/lib/rigor/environment/gated_signature_guard.rb +1 -1
- data/lib/rigor/environment/gated_signature_plan.rb +5 -2
- data/lib/rigor/environment/lockfile_resolver.rb +28 -12
- data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
- data/lib/rigor/environment/member_consistency.rb +56 -7
- data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
- data/lib/rigor/environment/project_path_gems.rb +124 -0
- data/lib/rigor/environment/rbs_hierarchy.rb +6 -15
- data/lib/rigor/environment/rbs_loader.rb +592 -70
- data/lib/rigor/environment/reflection.rb +7 -7
- data/lib/rigor/environment/required_features.rb +136 -32
- data/lib/rigor/environment/ruby_library_roots.rb +122 -0
- data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
- data/lib/rigor/environment.rb +44 -11
- data/lib/rigor/flow_contribution/merger.rb +4 -4
- data/lib/rigor/inference/acceptance.rb +93 -32
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/block_run_count.rb +178 -0
- data/lib/rigor/inference/budget_trace.rb +8 -2
- data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
- data/lib/rigor/inference/callee_mutation_chain.rb +126 -0
- data/lib/rigor/inference/callee_write_census.rb +815 -0
- data/lib/rigor/inference/construction_escape.rb +217 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
- data/lib/rigor/inference/deep_erasure.rb +170 -0
- data/lib/rigor/inference/def_handle.rb +1 -1
- data/lib/rigor/inference/def_node_resolver.rb +1 -1
- data/lib/rigor/inference/def_return_typer.rb +204 -12
- data/lib/rigor/inference/definer_resolution.rb +21 -5
- data/lib/rigor/inference/definers_index.rb +106 -0
- data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
- data/lib/rigor/inference/element_read_widening.rb +49 -3
- data/lib/rigor/inference/error_info.rb +17 -3
- data/lib/rigor/inference/expression_typer.rb +173 -57
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/global_write_census.rb +110 -1
- data/lib/rigor/inference/global_writer_reach.rb +260 -0
- data/lib/rigor/inference/hkt_body.rb +1 -2
- data/lib/rigor/inference/hkt_body_parser.rb +3 -5
- data/lib/rigor/inference/hkt_reducer.rb +2 -2
- data/lib/rigor/inference/hkt_registry.rb +3 -5
- data/lib/rigor/inference/indexed_narrowing.rb +4 -7
- data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
- data/lib/rigor/inference/ivar_writer_census.rb +246 -0
- data/lib/rigor/inference/key_presence_guard.rb +2 -1
- data/lib/rigor/inference/loop_value_record.rb +57 -0
- data/lib/rigor/inference/match_rebinding/frame.rb +65 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +45 -0
- data/lib/rigor/inference/match_rebinding.rb +11 -1
- data/lib/rigor/inference/memo_audit.rb +55 -0
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +113 -12
- data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -3
- data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +39 -27
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +203 -17
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +59 -24
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +83 -20
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +109 -29
- data/lib/rigor/inference/module_function_state/spellings.rb +64 -0
- data/lib/rigor/inference/module_function_state.rb +23 -1
- data/lib/rigor/inference/multi_target_binder.rb +1 -1
- data/lib/rigor/inference/mutation_rejoin.rb +240 -4
- data/lib/rigor/inference/mutation_widening.rb +162 -29
- data/lib/rigor/inference/narrowing.rb +113 -38
- data/lib/rigor/inference/object_mixins.rb +13 -1
- data/lib/rigor/inference/optimistic_origin.rb +1 -1
- data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +1 -1
- data/lib/rigor/inference/project_method_ownership.rb +6 -3
- data/lib/rigor/inference/project_seed_tables.rb +2 -1
- data/lib/rigor/inference/project_yielder.rb +122 -0
- data/lib/rigor/inference/receiver_alias.rb +20 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +3 -1
- data/lib/rigor/inference/refined_dispatch/own_method.rb +2 -1
- data/lib/rigor/inference/remover_holder_scan.rb +233 -0
- data/lib/rigor/inference/remover_narrowing.rb +224 -0
- data/lib/rigor/inference/rewrite_mutation.rb +53 -2
- data/lib/rigor/inference/scope_indexer/certainty.rb +9 -1
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_ivar_types.rb +64 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +205 -0
- data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
- data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
- data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +135 -0
- data/lib/rigor/inference/scope_indexer/unseen_ivar_writers.rb +133 -0
- data/lib/rigor/inference/scope_indexer.rb +614 -146
- data/lib/rigor/inference/self_call_ivar_writes.rb +101 -17
- data/lib/rigor/inference/set_mutation.rb +189 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +1 -2
- data/lib/rigor/inference/statement_evaluator.rb +3036 -278
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_index.rb +23 -0
- data/lib/rigor/inference/synthetic_method_scanner/project_members.rb +130 -0
- data/lib/rigor/inference/synthetic_method_scanner.rb +73 -14
- data/lib/rigor/inference/toplevel_def_census.rb +59 -0
- data/lib/rigor/inference/unknown_store_widening.rb +103 -4
- data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -1
- data/lib/rigor/language_server/buffer_table.rb +9 -3
- data/lib/rigor/language_server/completion_provider.rb +26 -20
- data/lib/rigor/language_server/config_warning_announcer.rb +28 -0
- data/lib/rigor/language_server/diagnostic_publisher.rb +28 -9
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -2
- data/lib/rigor/language_server/hover_provider.rb +1 -7
- data/lib/rigor/language_server/hover_renderer.rb +6 -8
- data/lib/rigor/language_server/project_context.rb +98 -4
- data/lib/rigor/language_server/publish_batcher.rb +3 -4
- data/lib/rigor/language_server/selection_range_provider.rb +2 -4
- data/lib/rigor/language_server/server.rb +12 -6
- data/lib/rigor/language_server/signature_help_provider.rb +4 -4
- data/lib/rigor/lens/plugins.rb +1 -1
- data/lib/rigor/mcp/server.rb +82 -3
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- data/lib/rigor/plugin/base.rb +26 -11
- data/lib/rigor/plugin/bundled_catalog.rb +1 -12
- data/lib/rigor/plugin/io_boundary.rb +1 -1
- data/lib/rigor/plugin/loader.rb +25 -8
- data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
- data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
- data/lib/rigor/plugin/manifest.rb +38 -16
- data/lib/rigor/plugin/registry.rb +41 -3
- data/lib/rigor/plugin/services.rb +2 -3
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
- data/lib/rigor/plugin/trust_policy.rb +2 -2
- data/lib/rigor/plugin/type_node_resolver.rb +2 -4
- data/lib/rigor/plugin.rb +6 -8
- data/lib/rigor/plugin_gap_advisory.rb +5 -1
- data/lib/rigor/project_environment.rb +16 -2
- data/lib/rigor/protection/analysis_guard.rb +1 -1
- data/lib/rigor/protection/discovery_seed.rb +96 -2
- data/lib/rigor/protection/mutator.rb +1 -1
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
- data/lib/rigor/rbs_extended/reporter.rb +27 -3
- data/lib/rigor/rbs_extended.rb +2 -3
- data/lib/rigor/reflection/constant_ancestors.rb +5 -3
- data/lib/rigor/rooted_glob.rb +25 -2
- data/lib/rigor/run_scoped_slots.rb +38 -0
- data/lib/rigor/runtime/jit.rb +1 -1
- data/lib/rigor/scope/discovery_index.rb +19 -6
- data/lib/rigor/scope/resolution_chain.rb +3 -1
- data/lib/rigor/scope.rb +379 -36
- data/lib/rigor/sig_gen/classification.rb +4 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
- data/lib/rigor/sig_gen/generator.rb +668 -84
- data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
- data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
- data/lib/rigor/sig_gen/layout_index.rb +3 -3
- data/lib/rigor/sig_gen/method_candidate.rb +23 -4
- data/lib/rigor/sig_gen/observation_collector.rb +7 -6
- data/lib/rigor/sig_gen/observed_call.rb +1 -1
- data/lib/rigor/sig_gen/path_mapper.rb +6 -0
- data/lib/rigor/sig_gen/rbs_validity.rb +1 -1
- data/lib/rigor/sig_gen/renderer.rb +21 -12
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +15 -2
- data/lib/rigor/sig_gen/type_elaborator.rb +21 -5
- data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
- data/lib/rigor/sig_gen/write_reporter.rb +63 -14
- data/lib/rigor/sig_gen/write_result.rb +23 -12
- data/lib/rigor/sig_gen/writer.rb +217 -37
- data/lib/rigor/type/acceptance_router.rb +2 -2
- data/lib/rigor/type/accepts_result.rb +1 -1
- data/lib/rigor/type/app.rb +4 -6
- data/lib/rigor/type/combinator.rb +13 -16
- data/lib/rigor/type/constant.rb +48 -7
- data/lib/rigor/type/hash_shape.rb +26 -40
- data/lib/rigor/type/struct_instance.rb +3 -2
- data/lib/rigor/type/union.rb +1 -1
- data/lib/rigor/type_node/identifier.rb +1 -2
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node/string_literal.rb +1 -2
- data/lib/rigor/type_node/symbol_literal.rb +1 -2
- data/lib/rigor/type_node/union.rb +3 -6
- data/lib/rigor/value_semantics.rb +1 -10
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +89 -70
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +25 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +79 -19
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +6 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +2 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +0 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +5 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +7 -12
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +19 -6
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +26 -13
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +5 -3
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +1 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +6 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +5 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +7 -6
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +4 -5
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -2
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +102 -11
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -2
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +3 -4
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +6 -8
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +5 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +1 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +17 -21
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +3 -2
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer.rb +1 -1
- data/sig/rigor/analysis/baseline.rbs +0 -2
- data/sig/rigor/analysis/fact_store.rbs +1 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
- data/sig/rigor/cli/diff_command.rbs +1 -1
- data/sig/rigor/cli/explain_command.rbs +1 -1
- data/sig/rigor/effects/file_collection.rbs +5 -6
- data/sig/rigor/effects/registry.rbs +8 -6
- data/sig/rigor/effects/summary.rbs +6 -5
- data/sig/rigor/environment.rbs +5 -3
- data/sig/rigor/inference/optimistic_origin.rbs +11 -0
- data/sig/rigor/inference.rbs +10 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +1 -0
- data/sig/rigor/plugin/effect_ancestry.rbs +6 -4
- data/sig/rigor/plugin/effect_attribution.rbs +25 -10
- data/sig/rigor/plugin/effect_edge.rbs +12 -5
- data/sig/rigor/plugin/effect_entry_points.rbs +8 -3
- data/sig/rigor/plugin/io_boundary.rbs +6 -5
- data/sig/rigor/plugin/manifest.rbs +16 -18
- data/sig/rigor/plugin/protocol_contract.rbs +6 -9
- data/sig/rigor/plugin/registry.rbs +1 -1
- data/sig/rigor/scope.rbs +51 -50
- data/sig/rigor/type.rbs +0 -6
- data/skills/rigor-plugin-author/SKILL.md +4 -4
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
- data/skills/rigor-plugin-review/SKILL.md +3 -3
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +12 -1
- metadata +55 -3
data/lib/rigor/cli/options.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../analysis/buffer_binding"
|
|
4
|
+
require_relative "../configuration_error"
|
|
4
5
|
|
|
5
6
|
module Rigor
|
|
6
7
|
class CLI
|
|
@@ -10,37 +11,92 @@ module Rigor
|
|
|
10
11
|
# `--instead-of` flag pair and the buffer-binding resolution that validates it. That resolution used to be copied
|
|
11
12
|
# verbatim into both `Rigor::CLI` and `TypeOfCommand`, so a fix to one (the paired-flag check, the readability
|
|
12
13
|
# check, the error wording) could silently miss the other. Centralising it keeps the two editor-mode entry points in
|
|
13
|
-
# lockstep.
|
|
14
|
+
# lockstep. It also owns the `--config` check (#1992), so every command and the MCP tools that pass `--config`
|
|
15
|
+
# read the same rule.
|
|
14
16
|
module Options
|
|
15
17
|
module_function
|
|
16
18
|
|
|
19
|
+
# The formats `parser` accepts for `--format`, or nil when {.add_format} did not define the switch. A
|
|
20
|
+
# block-validated switch carries no list of its own, so {.add_format} leaves it on the parser for the CLI
|
|
21
|
+
# catalogue (ADR-126 WD3) to read.
|
|
22
|
+
def accepted_formats(parser)
|
|
23
|
+
parser.instance_variable_get(:@rigor_accepted_formats)
|
|
24
|
+
end
|
|
25
|
+
|
|
17
26
|
# Defines the standard `--config=PATH` flag on `parser`, writing the path into `options[:config]`. Used by every
|
|
18
27
|
# subcommand that loads a `.rigor.yml`; the few whose `--config` help text is intentionally bespoke (`diff`,
|
|
19
|
-
# `mcp`, `show-bleedingedge`) keep their own `parser.on`
|
|
28
|
+
# `mcp`, `show-bleedingedge`) keep their own `parser.on` and call {.config_path} for the same check.
|
|
20
29
|
def add_config(parser, options)
|
|
21
|
-
parser.on("--config=PATH", "Path to the Rigor configuration file")
|
|
30
|
+
parser.on("--config=PATH", "Path to the Rigor configuration file") do |value|
|
|
31
|
+
options[:config] = config_path(value)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Defines `--format=FORMAT` restricted to `formats`, so an unsupported value is an `OptionParser::InvalidArgument`
|
|
36
|
+
# raised while the options are parsed (#2502). A check left to the renderer ran the whole analysis first, and a
|
|
37
|
+
# renderer that read only `format == "json"` printed text for any other value. The match is exact: an
|
|
38
|
+
# OptionParser enum would also complete a prefix (`--format=j`), which then turns ambiguous the day a second
|
|
39
|
+
# format shares it. The renderers' own "unsupported format" branches remain as a guard against internal drift.
|
|
40
|
+
def add_format(parser, options, description, formats: %w[text json])
|
|
41
|
+
parser.instance_variable_set(:@rigor_accepted_formats, formats.dup.freeze)
|
|
42
|
+
parser.on("--format=FORMAT", description) do |value|
|
|
43
|
+
raise OptionParser::InvalidArgument, value unless formats.include?(value)
|
|
44
|
+
|
|
45
|
+
options[:format] = value
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# The `--config` value, checked where the flag is read (#1992). A named file that is not there is an error, not
|
|
50
|
+
# a request for the defaults: the run would otherwise analyse under a configuration the caller did not ask for,
|
|
51
|
+
# and an MCP caller would get a result for it with no sign anything was missing. Auto-discovery, with no
|
|
52
|
+
# `--config` given, is not affected.
|
|
53
|
+
def config_path(value)
|
|
54
|
+
raise ConfigurationError, "empty config path" if value.strip.empty?
|
|
55
|
+
raise ConfigurationError, "config path is a directory: #{value}" if File.directory?(value)
|
|
56
|
+
raise ConfigurationError, "config file not found: #{value}" unless File.exist?(value)
|
|
57
|
+
raise ConfigurationError, "config file not readable: #{value}" unless File.readable?(value)
|
|
58
|
+
|
|
59
|
+
value
|
|
22
60
|
end
|
|
23
61
|
|
|
24
62
|
# Defines the `--tmp-file` / `--instead-of` editor-mode flag pair on `parser`, writing into `options`.
|
|
25
63
|
def add_editor_mode(parser, options)
|
|
26
64
|
parser.on("--tmp-file=PATH",
|
|
27
65
|
"Editor mode: read source bytes from PATH instead of --instead-of (paired)") do |value|
|
|
66
|
+
note_repeated_editor_flag(options, :tmp_file, "--tmp-file")
|
|
28
67
|
options[:tmp_file] = value
|
|
29
68
|
end
|
|
30
69
|
parser.on("--instead-of=PATH",
|
|
31
70
|
"Editor mode: the logical project path the buffer represents (paired with --tmp-file)") do |value|
|
|
71
|
+
note_repeated_editor_flag(options, :instead_of, "--instead-of")
|
|
32
72
|
options[:instead_of] = value
|
|
33
73
|
end
|
|
34
74
|
end
|
|
35
75
|
|
|
76
|
+
# Issue #2233 — editor mode binds one buffer per run, so a second `--tmp-file` or `--instead-of` cannot be
|
|
77
|
+
# honoured beside the first. Overwriting it dropped the first pair with no message; the repeat is recorded here
|
|
78
|
+
# and rejected by {resolve_buffer_binding} as a usage error instead.
|
|
79
|
+
def note_repeated_editor_flag(options, key, flag)
|
|
80
|
+
return if options[key].nil?
|
|
81
|
+
|
|
82
|
+
(options[:repeated_editor_flags] ||= []) << flag
|
|
83
|
+
end
|
|
84
|
+
|
|
36
85
|
# Resolves the editor-mode buffer binding from parsed `options`: returns nil when neither editor-mode flag is set,
|
|
37
86
|
# a `Analysis::BufferBinding` when the pair is valid, or `:usage_error` (after writing the reason to `err`) when
|
|
38
|
-
# the flags are unpaired or the temp file is unreadable.
|
|
87
|
+
# a flag is repeated, the flags are unpaired, or the temp file is unreadable.
|
|
39
88
|
def resolve_buffer_binding(options, err:)
|
|
40
89
|
tmp = options[:tmp_file]
|
|
41
90
|
instead = options[:instead_of]
|
|
42
91
|
return nil if tmp.nil? && instead.nil?
|
|
43
92
|
|
|
93
|
+
repeated = Array(options[:repeated_editor_flags])
|
|
94
|
+
unless repeated.empty?
|
|
95
|
+
err.puts("#{repeated.join(' and ')} given more than once; editor mode takes one buffer per run " \
|
|
96
|
+
"(a single --tmp-file / --instead-of pair)")
|
|
97
|
+
return :usage_error
|
|
98
|
+
end
|
|
99
|
+
|
|
44
100
|
if tmp.nil? || instead.nil?
|
|
45
101
|
err.puts("--tmp-file and --instead-of must appear together")
|
|
46
102
|
return :usage_error
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "optparse"
|
|
4
|
+
|
|
3
5
|
require_relative "command"
|
|
4
6
|
require_relative "../rooted_glob"
|
|
5
7
|
|
|
@@ -7,7 +9,7 @@ module Rigor
|
|
|
7
9
|
class CLI
|
|
8
10
|
# `rigor plugin` (singular) — discover and read the plugin source bundled with the `rigortype` gem.
|
|
9
11
|
#
|
|
10
|
-
# Rigor ships
|
|
12
|
+
# Rigor ships production plugins under `plugins/` and tutorial plugins under `examples/`. When Rigor is
|
|
11
13
|
# installed via `mise` / `gem install` the gem checkout is on disk, so a plugin author (or an AI coding agent
|
|
12
14
|
# following the `rigor-plugin-author` skill) can read a real, working plugin as a worked example — instead of
|
|
13
15
|
# guessing the `Rigor::Plugin::Base` surface from prose. This command outputs the absolute paths so they can be
|
|
@@ -17,75 +19,122 @@ module Rigor
|
|
|
17
19
|
# configured in *your* `.rigor.yml`. This command (singular) browses the plugins bundled in the *toolchain*.
|
|
18
20
|
# Mnemonic: "plugins" = my config; "plugin" = the catalogue I can learn from.
|
|
19
21
|
#
|
|
20
|
-
#
|
|
22
|
+
# Grammar (mirrors `rigor docs` and `rigor skill`, ADR-126 WD4): the positional slot is always a plugin *name*;
|
|
23
|
+
# alternative outputs are flags, so a plugin can never be shadowed by a verb.
|
|
21
24
|
#
|
|
22
|
-
# - `rigor plugin
|
|
23
|
-
#
|
|
24
|
-
# - `rigor plugin path <name>` — one-line absolute path to the
|
|
25
|
-
# plugin's directory (Read-tool input).
|
|
26
|
-
# - `rigor plugin print <name>` — a header (dir / lib / sig / README
|
|
27
|
-
# paths) followed by the plugin's main
|
|
25
|
+
# - `rigor plugin` — every bundled plugin + example, name + absolute directory path (the default).
|
|
26
|
+
# - `rigor plugin <name>` — a header (dir / lib / sig / README paths) followed by the plugin's main
|
|
28
27
|
# `lib/<name>.rb` source body.
|
|
29
|
-
# - `rigor plugin
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
# author can read the public plugin
|
|
33
|
-
#
|
|
28
|
+
# - `rigor plugin --path <name>` — one-line absolute path to the plugin's directory (Read-tool input).
|
|
29
|
+
# - `rigor plugin --list` — the same listing as no argument.
|
|
30
|
+
# - `rigor plugin --root` — the rigortype gem root and its key subdirectories (lib/, plugins/, examples/,
|
|
31
|
+
# skills/, sig/), so an author can read the public plugin API
|
|
32
|
+
# (`lib/rigor/plugin.rb`) directly.
|
|
34
33
|
#
|
|
35
|
-
# `
|
|
34
|
+
# The verbs `list`, `path <name>`, `print <name>` and `root` are the pre-ADR-126 spelling. They still work for one
|
|
35
|
+
# minor, print the same stdout and return the same status, and add one deprecation notice on stderr naming the
|
|
36
|
+
# replacement.
|
|
36
37
|
#
|
|
37
38
|
# **Docker / cross-filesystem note.** Every path printed is resolved at runtime from this file's location, so it is
|
|
38
39
|
# correct *on the filesystem where `rigor` runs*. If you run `rigor` inside a container but read files from the host
|
|
39
40
|
# (or vice versa), the paths will not resolve — read them from the same environment that ran the command (`rigor
|
|
40
|
-
# plugin
|
|
41
|
+
# plugin <name>` inlines the body for exactly this case: it works with no file-reading tool at all).
|
|
41
42
|
class PluginCommand < Command
|
|
42
43
|
USAGE = <<~USAGE
|
|
43
|
-
Usage: rigor plugin <
|
|
44
|
+
Usage: rigor plugin [<name>] [--path <name>] [--list] [--root]
|
|
44
45
|
|
|
45
46
|
Browse the plugins bundled in the rigortype toolchain (worked
|
|
46
47
|
examples for authoring your own). For the activation status of
|
|
47
48
|
the plugins in your .rigor.yml, use `rigor plugins` (plural).
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
path
|
|
52
|
-
|
|
53
|
-
root
|
|
50
|
+
rigor plugin List bundled + example plugins
|
|
51
|
+
rigor plugin <name> Print <name>'s main lib source, with a header
|
|
52
|
+
rigor plugin --path <name> Print the absolute directory path of <name>
|
|
53
|
+
rigor plugin --list List bundled + example plugins
|
|
54
|
+
rigor plugin --root Print the gem root + key subdirectories
|
|
54
55
|
|
|
55
56
|
Examples:
|
|
56
|
-
rigor plugin
|
|
57
|
-
rigor plugin
|
|
58
|
-
rigor plugin
|
|
59
|
-
rigor plugin root
|
|
57
|
+
rigor plugin
|
|
58
|
+
rigor plugin rigor-activesupport-core-ext
|
|
59
|
+
rigor plugin --path rigor-activerecord
|
|
60
|
+
rigor plugin --root
|
|
60
61
|
USAGE
|
|
61
62
|
|
|
63
|
+
# The pre-ADR-126 verbs, each with the spelling that replaces it.
|
|
64
|
+
DEPRECATED_VERBS = {
|
|
65
|
+
"list" => "rigor plugin --list",
|
|
66
|
+
"path" => "rigor plugin --path <name>",
|
|
67
|
+
"print" => "rigor plugin <name>",
|
|
68
|
+
"root" => "rigor plugin --root"
|
|
69
|
+
}.freeze
|
|
70
|
+
|
|
62
71
|
# The bundled plugins/examples/source live at `<gem_root>/...`. From `lib/rigor/cli/plugin_command.rb` the gem
|
|
63
72
|
# root is three directories up (matching SkillCommand::SKILLS_ROOT).
|
|
64
73
|
GEM_ROOT = File.expand_path("../../..", __dir__).freeze
|
|
65
74
|
PLUGINS_ROOT = File.join(GEM_ROOT, "plugins").freeze
|
|
66
75
|
EXAMPLES_ROOT = File.join(GEM_ROOT, "examples").freeze
|
|
67
76
|
|
|
77
|
+
# The parser this command parses with, built without parsing anything: the catalogue reads its flags from it
|
|
78
|
+
# (ADR-126 WD3). `options` is only written by the switches' blocks.
|
|
79
|
+
def option_parser(options = {})
|
|
80
|
+
OptionParser.new do |opts|
|
|
81
|
+
opts.banner = USAGE.lines.first.chomp
|
|
82
|
+
opts.on("--path NAME", "Print the absolute directory path of a plugin") { |value| options[:path] = value }
|
|
83
|
+
opts.on("--list", "List bundled + example plugins") { options[:list] = true }
|
|
84
|
+
opts.on("--root", "Print the gem root + key subdirectories") { options[:root] = true }
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
68
88
|
# @return CLI exit status.
|
|
69
89
|
def run
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
case subcommand
|
|
73
|
-
when "list" then run_list
|
|
74
|
-
when "path" then run_path
|
|
75
|
-
when "print" then run_print
|
|
76
|
-
when "root" then run_root
|
|
77
|
-
when "-h", "--help", "help"
|
|
90
|
+
if @argv.intersect?(%w[-h --help])
|
|
78
91
|
@out.puts(USAGE)
|
|
79
|
-
0
|
|
80
|
-
else
|
|
81
|
-
@err.puts("Unknown subcommand: #{subcommand}")
|
|
82
|
-
@err.puts(USAGE)
|
|
83
|
-
Rigor::CLI::EXIT_USAGE
|
|
92
|
+
return 0
|
|
84
93
|
end
|
|
94
|
+
|
|
95
|
+
verb = @argv.first
|
|
96
|
+
return run_deprecated_verb(@argv.shift) if DEPRECATED_VERBS.key?(verb)
|
|
97
|
+
return run_help if verb == "help"
|
|
98
|
+
|
|
99
|
+
run_grammar
|
|
85
100
|
end
|
|
86
101
|
|
|
87
102
|
private
|
|
88
103
|
|
|
104
|
+
def run_help
|
|
105
|
+
@out.puts(USAGE)
|
|
106
|
+
0
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def run_grammar
|
|
110
|
+
options = {}
|
|
111
|
+
begin
|
|
112
|
+
option_parser(options).parse!(@argv)
|
|
113
|
+
rescue OptionParser::ParseError => e
|
|
114
|
+
return usage_error(e.message)
|
|
115
|
+
end
|
|
116
|
+
return run_path(options[:path]) if options.key?(:path)
|
|
117
|
+
return run_root if options[:root]
|
|
118
|
+
return run_list if options[:list]
|
|
119
|
+
|
|
120
|
+
name = @argv.shift
|
|
121
|
+
name.nil? ? run_list : run_print(name)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def run_deprecated_verb(verb)
|
|
125
|
+
@err.puts("rigor: `rigor plugin #{verb}` is deprecated and will be removed in the next minor release; " \
|
|
126
|
+
"use `#{DEPRECATED_VERBS.fetch(verb)}`.")
|
|
127
|
+
# The verbs' stdout stays byte-identical to what they printed before the grammar changed, so its prose still
|
|
128
|
+
# names the verb spelling.
|
|
129
|
+
@legacy = true
|
|
130
|
+
case verb
|
|
131
|
+
when "list" then run_list
|
|
132
|
+
when "path" then run_path(@argv.shift)
|
|
133
|
+
when "print" then run_print(@argv.shift)
|
|
134
|
+
else run_root
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
89
138
|
def run_list
|
|
90
139
|
plugins = discover(PLUGINS_ROOT)
|
|
91
140
|
examples = discover(EXAMPLES_ROOT)
|
|
@@ -113,8 +162,7 @@ module Rigor
|
|
|
113
162
|
end
|
|
114
163
|
end
|
|
115
164
|
|
|
116
|
-
def run_path
|
|
117
|
-
name = @argv.shift
|
|
165
|
+
def run_path(name)
|
|
118
166
|
return usage_error("`path` requires a plugin name") if name.nil?
|
|
119
167
|
|
|
120
168
|
plugin = find(name)
|
|
@@ -124,8 +172,7 @@ module Rigor
|
|
|
124
172
|
0
|
|
125
173
|
end
|
|
126
174
|
|
|
127
|
-
def run_print
|
|
128
|
-
name = @argv.shift
|
|
175
|
+
def run_print(name)
|
|
129
176
|
return usage_error("`print` requires a plugin name") if name.nil?
|
|
130
177
|
|
|
131
178
|
plugin = find(name)
|
|
@@ -159,7 +206,7 @@ module Rigor
|
|
|
159
206
|
0
|
|
160
207
|
end
|
|
161
208
|
|
|
162
|
-
# The header that precedes the plugin body when an author runs `rigor plugin
|
|
209
|
+
# The header that precedes the plugin body when an author runs `rigor plugin <name>`. `# `-prefixed so the
|
|
163
210
|
# combined output stays readable; the Ruby body below it is unchanged.
|
|
164
211
|
def render_print_header(plugin)
|
|
165
212
|
dir = plugin.fetch(:path)
|
|
@@ -176,7 +223,7 @@ module Rigor
|
|
|
176
223
|
# The main source body is below; read the other files from the
|
|
177
224
|
# paths above. To suppress `call.undefined-method` for methods a
|
|
178
225
|
# DSL generates, study how an RBS-bundle plugin ships `sig/` (see
|
|
179
|
-
#
|
|
226
|
+
# `#{@legacy ? 'rigor plugin print' : 'rigor plugin'} rigor-activesupport-core-ext`).
|
|
180
227
|
HEADER
|
|
181
228
|
end
|
|
182
229
|
|
|
@@ -211,12 +258,12 @@ module Rigor
|
|
|
211
258
|
def docker_note
|
|
212
259
|
"\nNote: paths are local to where `rigor` runs. If you run rigor in a " \
|
|
213
260
|
"container,\nread these files from the same filesystem (use " \
|
|
214
|
-
"
|
|
261
|
+
"`#{@legacy ? 'rigor plugin print' : 'rigor plugin <name>'}` to inline a body)."
|
|
215
262
|
end
|
|
216
263
|
|
|
217
264
|
def name_error(name)
|
|
218
265
|
@err.puts("Unknown plugin: #{name}")
|
|
219
|
-
@err.puts("Run `rigor plugin list` to see the bundled plugins.")
|
|
266
|
+
@err.puts("Run `rigor plugin --list` to see the bundled plugins.")
|
|
220
267
|
1
|
|
221
268
|
end
|
|
222
269
|
|
|
@@ -30,7 +30,7 @@ module Rigor
|
|
|
30
30
|
# - every manifest-declared extension surface
|
|
31
31
|
# (`open_receivers:` / `owns_receivers:` / `produces:` /
|
|
32
32
|
# `consumes:` / `block_as_methods:` / `heredoc_templates:` /
|
|
33
|
-
# `trait_registries:` /
|
|
33
|
+
# `trait_registries:` / `per_invocation_classes:` /
|
|
34
34
|
# `type_node_resolvers:` / `hkt_registrations:` /
|
|
35
35
|
# `hkt_definitions:` / `protocol_contracts:` /
|
|
36
36
|
# `source_rbs_synthesizer:`);
|
|
@@ -80,6 +80,20 @@ module Rigor
|
|
|
80
80
|
0
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
+
# The parser this command parses with, built without parsing anything: the catalogue reads the
|
|
84
|
+
# flags and formats from it (ADR-126 WD3). `options` is only written by the switches' blocks.
|
|
85
|
+
def option_parser(options = {})
|
|
86
|
+
OptionParser.new do |opts|
|
|
87
|
+
opts.banner = USAGE
|
|
88
|
+
Options.add_config(opts, options)
|
|
89
|
+
Options.add_format(opts, options, "Output format: text (default) or json")
|
|
90
|
+
opts.on("--strict", "Exit 1 if any plugin failed to load (CI gate)") { options[:strict] = true }
|
|
91
|
+
opts.on("--capabilities", "Emit the per-plugin extension-protocol catalogue (ADR-37)") do
|
|
92
|
+
options[:capabilities] = true
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
83
97
|
private
|
|
84
98
|
|
|
85
99
|
# Picks the renderer view. `--capabilities` switches to the focused extension-protocol catalogue (ADR-37 §
|
|
@@ -97,25 +111,10 @@ module Rigor
|
|
|
97
111
|
|
|
98
112
|
def parse_options
|
|
99
113
|
options = { config: nil, format: "text", strict: false, capabilities: false }
|
|
100
|
-
|
|
101
|
-
opts.banner = USAGE
|
|
102
|
-
Options.add_config(opts, options)
|
|
103
|
-
opts.on("--format=FORMAT", "Output format: text (default) or json") { |v| options[:format] = v }
|
|
104
|
-
opts.on("--strict", "Exit 1 if any plugin failed to load (CI gate)") { options[:strict] = true }
|
|
105
|
-
opts.on("--capabilities", "Emit the per-plugin extension-protocol catalogue (ADR-37)") do
|
|
106
|
-
options[:capabilities] = true
|
|
107
|
-
end
|
|
108
|
-
end.parse!(@argv)
|
|
109
|
-
validate!(options)
|
|
114
|
+
option_parser(options).parse!(@argv)
|
|
110
115
|
options
|
|
111
116
|
end
|
|
112
117
|
|
|
113
|
-
def validate!(options)
|
|
114
|
-
return if %w[text json].include?(options.fetch(:format))
|
|
115
|
-
|
|
116
|
-
raise OptionParser::InvalidArgument, "unsupported format: #{options.fetch(:format)}"
|
|
117
|
-
end
|
|
118
|
-
|
|
119
118
|
# Runs the plugin loader against the project configuration and returns a row per declared plugin entry. Each row
|
|
120
119
|
# is a plain Hash with the fields enumerated in the class docstring so the renderer (text + JSON) reads from a
|
|
121
120
|
# single shape.
|
|
@@ -204,7 +203,7 @@ module Rigor
|
|
|
204
203
|
.merge(load_error: nil)
|
|
205
204
|
end
|
|
206
205
|
|
|
207
|
-
# ADR-37 narrow extension protocols. Unlike the
|
|
206
|
+
# ADR-37 narrow extension protocols. Unlike the declarative manifest fields, these are class-level DSLs
|
|
208
207
|
# (`node_rule` / `dynamic_return` / `narrowing_facts`), so they are read off the plugin class rather than the
|
|
209
208
|
# manifest. The gate values — node types, receiver class names, specified method names — are the greppable,
|
|
210
209
|
# enumerable surface the capability catalogue exposes.
|
|
@@ -233,6 +232,8 @@ module Rigor
|
|
|
233
232
|
signature_paths: signature_path_rows(plugin),
|
|
234
233
|
open_receivers: manifest.open_receivers.dup,
|
|
235
234
|
owns_receivers: manifest.owns_receivers.dup,
|
|
235
|
+
per_invocation_classes: manifest.per_invocation_classes.dup,
|
|
236
|
+
per_invocation_non_action_macros: manifest.per_invocation_non_action_macros.map(&:to_s),
|
|
236
237
|
produces: manifest.produces.map(&:to_s),
|
|
237
238
|
consumes: manifest.consumes.map { |c| "#{c.plugin_id}/#{c.name}#{'?' if c.optional}" },
|
|
238
239
|
source_rbs_synthesizer: !manifest.source_rbs_synthesizer.nil?
|
|
@@ -265,6 +266,8 @@ module Rigor
|
|
|
265
266
|
signature_paths: [],
|
|
266
267
|
open_receivers: [],
|
|
267
268
|
owns_receivers: [],
|
|
269
|
+
per_invocation_classes: [],
|
|
270
|
+
per_invocation_non_action_macros: [],
|
|
268
271
|
produces: [],
|
|
269
272
|
consumes: [],
|
|
270
273
|
block_as_methods: 0,
|
|
@@ -345,6 +348,7 @@ module Rigor
|
|
|
345
348
|
config: {},
|
|
346
349
|
signature_paths: [],
|
|
347
350
|
open_receivers: [], owns_receivers: [], produces: [], consumes: [],
|
|
351
|
+
per_invocation_classes: [], per_invocation_non_action_macros: [],
|
|
348
352
|
block_as_methods: 0, heredoc_templates: 0, trait_registries: 0,
|
|
349
353
|
type_node_resolvers: 0,
|
|
350
354
|
hkt_registrations: 0, hkt_definitions: 0,
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "json"
|
|
4
|
+
require_relative "json_document"
|
|
4
5
|
|
|
5
6
|
module Rigor
|
|
6
7
|
class CLI
|
|
7
8
|
# Renderer for `rigor plugins`. Produces a human-readable text table and a JSON representation from the same row
|
|
8
|
-
# shape (the Hash
|
|
9
|
+
# shape (the Hash {Rigor::CLI::PluginsCommand#build_rows} returns).
|
|
9
10
|
#
|
|
10
11
|
# The two formats carry the same content; JSON is meant for tooling (SKILLs, CI, editor integrations) while text is
|
|
11
12
|
# for interactive inspection. Rows are printed in the order the loader resolved them.
|
|
@@ -44,7 +45,7 @@ module Rigor
|
|
|
44
45
|
"available" => @inflection.fetch(:available)
|
|
45
46
|
}
|
|
46
47
|
end
|
|
47
|
-
JSON.pretty_generate(payload)
|
|
48
|
+
JSON.pretty_generate(JsonDocument.stamp(payload, mode: "status"))
|
|
48
49
|
end
|
|
49
50
|
|
|
50
51
|
# ADR-37 § "Machine-readable capability catalogue" — the focused per-plugin extension-protocol dump. Only loaded
|
|
@@ -53,10 +54,13 @@ module Rigor
|
|
|
53
54
|
# type-specifier methods, and produced / consumed facts.
|
|
54
55
|
def capabilities_json
|
|
55
56
|
JSON.pretty_generate(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
JsonDocument.stamp(
|
|
58
|
+
{
|
|
59
|
+
"configuration" => @configuration_path,
|
|
60
|
+
"capabilities" => loaded_rows.map { |row| capabilities_json_for(row) }
|
|
61
|
+
},
|
|
62
|
+
mode: "capabilities"
|
|
63
|
+
)
|
|
60
64
|
)
|
|
61
65
|
end
|
|
62
66
|
|
|
@@ -185,12 +189,26 @@ module Rigor
|
|
|
185
189
|
lines = []
|
|
186
190
|
lines << " open_receivers: #{row[:open_receivers].join(', ')}" if row[:open_receivers].any?
|
|
187
191
|
lines << " owns_receivers: #{row[:owns_receivers].join(', ')}" if row[:owns_receivers].any?
|
|
192
|
+
lines.concat(per_invocation_lines(row))
|
|
188
193
|
lines << " produces: #{row[:produces].join(', ')}" if row[:produces].any?
|
|
189
194
|
lines << " consumes: #{row[:consumes].join(', ')}" if row[:consumes].any?
|
|
190
195
|
lines.concat(narrow_protocol_lines(row))
|
|
191
196
|
lines
|
|
192
197
|
end
|
|
193
198
|
|
|
199
|
+
# ADR-125 — the per-invocation classes a plugin vouches for, and the macros whose arguments name a method that is
|
|
200
|
+
# not one of their actions.
|
|
201
|
+
def per_invocation_lines(row)
|
|
202
|
+
lines = []
|
|
203
|
+
if row[:per_invocation_classes].any?
|
|
204
|
+
lines << " per_invocation_classes: #{row[:per_invocation_classes].join(', ')}"
|
|
205
|
+
end
|
|
206
|
+
if row[:per_invocation_non_action_macros].any?
|
|
207
|
+
lines << " per_invocation_non_action_macros: #{row[:per_invocation_non_action_macros].join(', ')}"
|
|
208
|
+
end
|
|
209
|
+
lines
|
|
210
|
+
end
|
|
211
|
+
|
|
194
212
|
# ADR-37 narrow extension protocols (node_rule / dynamic_return / narrowing_facts). Surfaced in the full report
|
|
195
213
|
# alongside the declarative surfaces; `--capabilities` is the focused view.
|
|
196
214
|
def narrow_protocol_lines(row)
|
|
@@ -249,8 +267,6 @@ module Rigor
|
|
|
249
267
|
"signature_paths" => row[:signature_paths].map do |sp|
|
|
250
268
|
{ "path" => sp[:path], "exists" => sp[:exists], "rbs_files" => sp[:rbs_files] }
|
|
251
269
|
end,
|
|
252
|
-
"open_receivers" => row[:open_receivers],
|
|
253
|
-
"owns_receivers" => row[:owns_receivers],
|
|
254
270
|
"produces" => row[:produces],
|
|
255
271
|
"consumes" => row[:consumes],
|
|
256
272
|
"block_as_methods" => row[:block_as_methods],
|
|
@@ -265,6 +281,16 @@ module Rigor
|
|
|
265
281
|
"dynamic_return_receivers" => row[:dynamic_return_receivers],
|
|
266
282
|
"narrowing_facts_methods" => row[:narrowing_facts_methods],
|
|
267
283
|
"load_error" => row[:load_error]
|
|
284
|
+
}.merge(receiver_json(row))
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# ADR-125 — the per-invocation keys are additive; an empty array when the plugin declares none.
|
|
288
|
+
def receiver_json(row)
|
|
289
|
+
{
|
|
290
|
+
"open_receivers" => row[:open_receivers],
|
|
291
|
+
"owns_receivers" => row[:owns_receivers],
|
|
292
|
+
"per_invocation_classes" => row[:per_invocation_classes],
|
|
293
|
+
"per_invocation_non_action_macros" => row[:per_invocation_non_action_macros]
|
|
268
294
|
}
|
|
269
295
|
end
|
|
270
296
|
|
|
@@ -31,8 +31,8 @@ module Rigor
|
|
|
31
31
|
# @return one entry per path.
|
|
32
32
|
def run(paths:, scanner:, environment:, configuration:, workers:)
|
|
33
33
|
# Force the full RBS load on the parent so children copy-on-write inherit a warm environment rather
|
|
34
|
-
# than each rebuilding it after the fork (mirrors the check fork pool's parent-side prewarm).
|
|
35
|
-
#
|
|
34
|
+
# than each rebuilding it after the fork (mirrors the check fork pool's parent-side prewarm).
|
|
35
|
+
# Skipped when the run is sequential.
|
|
36
36
|
environment.rbs_loader&.prewarm if Inference::ForkMap.parallel?([workers, paths.size].min)
|
|
37
37
|
|
|
38
38
|
payloads = Inference::ForkMap.call(items: paths, workers: workers) do |slice|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "json"
|
|
4
4
|
|
|
5
5
|
require_relative "../inference/dynamic_origin"
|
|
6
|
+
require_relative "json_document"
|
|
6
7
|
|
|
7
8
|
module Rigor
|
|
8
9
|
class CLI
|
|
@@ -32,7 +33,7 @@ module Rigor
|
|
|
32
33
|
private
|
|
33
34
|
|
|
34
35
|
def render_json(report)
|
|
35
|
-
@out.puts(JSON.pretty_generate(report.to_h))
|
|
36
|
+
@out.puts(JSON.pretty_generate(JsonDocument.stamp(report.to_h, mode: "protection")))
|
|
36
37
|
end
|
|
37
38
|
|
|
38
39
|
def render_text(report)
|
|
@@ -6,6 +6,8 @@ require "optionparser"
|
|
|
6
6
|
require_relative "../bleeding_edge"
|
|
7
7
|
require_relative "../configuration"
|
|
8
8
|
require_relative "command"
|
|
9
|
+
require_relative "options"
|
|
10
|
+
require_relative "json_document"
|
|
9
11
|
|
|
10
12
|
module Rigor
|
|
11
13
|
class CLI
|
|
@@ -34,19 +36,23 @@ module Rigor
|
|
|
34
36
|
0
|
|
35
37
|
end
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def
|
|
40
|
-
options = { format: "text", config: nil }
|
|
39
|
+
# The parser this command parses with, built without parsing anything: the catalogue reads the
|
|
40
|
+
# flags and formats from it (ADR-126 WD3). `options` is only written by the switches' blocks.
|
|
41
|
+
def option_parser(options = {})
|
|
41
42
|
OptionParser.new do |opt|
|
|
42
43
|
opt.banner = USAGE
|
|
43
|
-
|
|
44
|
-
options[:format] = fmt
|
|
45
|
-
end
|
|
44
|
+
Options.add_format(opt, options, "Output format (text | json). Default: text.")
|
|
46
45
|
opt.on("--config=PATH", "Path to a .rigor.yml (default: auto-discovery).") do |path|
|
|
47
|
-
options[:config] = path
|
|
46
|
+
options[:config] = Options.config_path(path)
|
|
48
47
|
end
|
|
49
|
-
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def parse_options
|
|
54
|
+
options = { format: "text", config: nil }
|
|
55
|
+
option_parser(options).parse!(@argv)
|
|
50
56
|
options
|
|
51
57
|
end
|
|
52
58
|
|
|
@@ -59,13 +65,14 @@ module Rigor
|
|
|
59
65
|
|
|
60
66
|
def render_json(configuration)
|
|
61
67
|
selector = configuration.bleeding_edge
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
payload = {
|
|
69
|
+
"overlay" => BleedingEdge.features.map(&:to_h),
|
|
70
|
+
"selector" => configuration.to_h.fetch("bleeding_edge"),
|
|
71
|
+
"active" => BleedingEdge.active_features(selector).map(&:id),
|
|
72
|
+
"unknown_selected" => BleedingEdge.unknown_selected_ids(selector),
|
|
73
|
+
"graduated" => BleedingEdge::GRADUATED
|
|
74
|
+
}
|
|
75
|
+
@out.puts(JSON.pretty_generate(JsonDocument.stamp(payload)))
|
|
69
76
|
end
|
|
70
77
|
|
|
71
78
|
def render_text(configuration)
|