rigortype 0.4.6 → 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 +50 -3
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/manual/02-cli-reference.md +152 -26
- data/docs/manual/03-configuration.md +36 -3
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/09-editor-integration.md +13 -2
- data/docs/manual/10-mcp-server.md +38 -2
- data/docs/manual/12-caching.md +4 -3
- data/docs/manual/plugins/rigor-actionpack.md +19 -11
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +20 -1
- data/lib/rigor/analysis/baseline.rb +24 -27
- 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 +85 -13
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +3 -2
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +0 -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/rebinding_block_callees.rb +3 -3
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- 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 +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules.rb +124 -44
- data/lib/rigor/analysis/crash_signature.rb +2 -2
- data/lib/rigor/analysis/dependency_recorder.rb +4 -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 +93 -6
- data/lib/rigor/analysis/fact_store.rb +3 -3
- 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 +27 -16
- data/lib/rigor/analysis/rule_catalog.rb +2 -2
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +2 -2
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -1
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +193 -93
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +1 -1
- data/lib/rigor/analysis/runner/pool_coordinator.rb +147 -323
- data/lib/rigor/analysis/runner/project_pre_passes.rb +3 -3
- 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/worker_payload.rb +36 -0
- data/lib/rigor/analysis/runner.rb +77 -71
- 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 +24 -31
- 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 +31 -12
- 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 +108 -102
- 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 +25 -20
- 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 +12 -8
- data/lib/rigor/cli/lsp_command.rb +250 -23
- data/lib/rigor/cli/mcp_command.rb +12 -7
- 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 +40 -1
- data/lib/rigor/cli/plugin_command.rb +93 -46
- data/lib/rigor/cli/plugins_command.rb +17 -18
- data/lib/rigor/cli/plugins_renderer.rb +10 -6
- 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 +21 -15
- data/lib/rigor/cli/sig_gen_command.rb +84 -27
- 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 +16 -11
- data/lib/rigor/cli/type_of_project_scope.rb +1 -4
- 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 +1 -1
- data/lib/rigor/configuration/severity_profile.rb +5 -5
- data/lib/rigor/configuration.rb +72 -30
- 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 +480 -67
- data/lib/rigor/environment/reflection.rb +7 -7
- data/lib/rigor/environment/required_features.rb +0 -3
- data/lib/rigor/environment/ruby_library_roots.rb +1 -1
- data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
- data/lib/rigor/environment.rb +41 -10
- 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 +88 -77
- 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 +39 -7
- 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 +46 -4
- data/lib/rigor/inference/definer_resolution.rb +4 -2
- 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/expression_typer.rb +116 -49
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/global_write_census.rb +76 -2
- 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 +51 -0
- data/lib/rigor/inference/key_presence_guard.rb +2 -1
- data/lib/rigor/inference/loop_value_record.rb +8 -4
- data/lib/rigor/inference/match_rebinding/frame.rb +46 -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 +3 -3
- 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 +1 -1
- 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 +9 -7
- 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 +64 -14
- data/lib/rigor/inference/module_function_state.rb +11 -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 +157 -27
- data/lib/rigor/inference/narrowing.rb +47 -33
- 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_yielder.rb +11 -17
- 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 +0 -1
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +58 -15
- 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.rb +137 -46
- data/lib/rigor/inference/self_call_ivar_writes.rb +84 -12
- 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 +2328 -138
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_scanner.rb +5 -6
- 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 +3 -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 +8 -3
- 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 +39 -1
- 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 +7 -3
- 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 +73 -2
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- 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 +9 -13
- data/lib/rigor/plugin/registry.rb +3 -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 +44 -5
- 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 +15 -1
- 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 +365 -31
- 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 +371 -58
- 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 +3 -5
- 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/renderer.rb +15 -9
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +15 -2
- 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 +9 -13
- data/lib/rigor/type/constant.rb +4 -5
- data/lib/rigor/type/hash_shape.rb +21 -40
- 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 +38 -36
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -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 +66 -19
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -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-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 +1 -1
- 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 +1 -1
- 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 +3 -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/sig/rigor/analysis/baseline.rbs +0 -3
- 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/registry.rbs +2 -2
- data/sig/rigor/inference.rbs +4 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +1 -0
- data/sig/rigor/plugin/effect_attribution.rbs +3 -3
- data/sig/rigor/plugin/effect_edge.rbs +1 -1
- data/sig/rigor/plugin/effect_entry_points.rbs +3 -3
- data/sig/rigor/plugin/io_boundary.rbs +2 -1
- data/sig/rigor/plugin/manifest.rbs +9 -9
- data/sig/rigor/plugin/protocol_contract.rbs +3 -7
- data/sig/rigor/scope.rbs +23 -3
- 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 +36 -3
|
@@ -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.
|
|
@@ -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
|
|
|
@@ -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)
|
|
@@ -7,6 +7,7 @@ require_relative "../bleeding_edge"
|
|
|
7
7
|
require_relative "../configuration"
|
|
8
8
|
require_relative "command"
|
|
9
9
|
require_relative "options"
|
|
10
|
+
require_relative "json_document"
|
|
10
11
|
|
|
11
12
|
module Rigor
|
|
12
13
|
class CLI
|
|
@@ -35,19 +36,23 @@ module Rigor
|
|
|
35
36
|
0
|
|
36
37
|
end
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def
|
|
41
|
-
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 = {})
|
|
42
42
|
OptionParser.new do |opt|
|
|
43
43
|
opt.banner = USAGE
|
|
44
|
-
|
|
45
|
-
options[:format] = fmt
|
|
46
|
-
end
|
|
44
|
+
Options.add_format(opt, options, "Output format (text | json). Default: text.")
|
|
47
45
|
opt.on("--config=PATH", "Path to a .rigor.yml (default: auto-discovery).") do |path|
|
|
48
46
|
options[:config] = Options.config_path(path)
|
|
49
47
|
end
|
|
50
|
-
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)
|
|
51
56
|
options
|
|
52
57
|
end
|
|
53
58
|
|
|
@@ -60,13 +65,14 @@ module Rigor
|
|
|
60
65
|
|
|
61
66
|
def render_json(configuration)
|
|
62
67
|
selector = configuration.bleeding_edge
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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)))
|
|
70
76
|
end
|
|
71
77
|
|
|
72
78
|
def render_text(configuration)
|
|
@@ -38,7 +38,11 @@ module Rigor
|
|
|
38
38
|
|
|
39
39
|
VALID_MODES = %w[print diff write check].freeze
|
|
40
40
|
VALID_PARAM_POLICIES = %w[untyped observed observed-strict].freeze
|
|
41
|
-
|
|
41
|
+
|
|
42
|
+
# The per-target outcomes `--write` refuses: the file is left untouched and the run exits 1. `--check` counts
|
|
43
|
+
# them apart from the targets it would update, since `--write` does not fix a refused file.
|
|
44
|
+
TARGET_REFUSALS = %i[skipped_invalid_rbs skipped_invalid_encoding skipped_unparseable].freeze
|
|
45
|
+
private_constant :TARGET_REFUSALS
|
|
42
46
|
|
|
43
47
|
# The skip reasons {#report_skipped} counts. The four left out each have a report of their own
|
|
44
48
|
# ({#report_unrenderable}, {#report_unresolvable_superclasses}, {#report_inline_declared}, and the refusal
|
|
@@ -61,17 +65,18 @@ module Rigor
|
|
|
61
65
|
|
|
62
66
|
observation_roots = observation_roots(configuration, options)
|
|
63
67
|
observations = collect_observations(configuration, options, observation_roots, paths)
|
|
68
|
+
mode = options.fetch(:mode).to_sym
|
|
64
69
|
generator = SigGen::Generator.new(configuration: configuration, paths: paths,
|
|
65
70
|
observations: observations,
|
|
66
71
|
include_private: options.fetch(:include_private),
|
|
67
72
|
effect_annotator: effect_annotator(configuration, paths, options),
|
|
68
|
-
overwrite: options.fetch(:overwrite)
|
|
73
|
+
overwrite: options.fetch(:overwrite),
|
|
74
|
+
target_writer: target_writer(configuration, options, mode))
|
|
69
75
|
candidates = generator.run
|
|
70
|
-
mode = options.fetch(:mode).to_sym
|
|
71
76
|
|
|
72
77
|
status = case mode
|
|
73
|
-
when :write then dispatch_write(candidates, configuration, options)
|
|
74
|
-
when :check then dispatch_check(candidates, configuration, options)
|
|
78
|
+
when :write then dispatch_write(candidates, configuration, options, generator)
|
|
79
|
+
when :check then dispatch_check(candidates, configuration, options, generator)
|
|
75
80
|
else
|
|
76
81
|
dispatch_print_or_diff(candidates, mode, options, observation_roots, path_errors)
|
|
77
82
|
0
|
|
@@ -86,7 +91,12 @@ module Rigor
|
|
|
86
91
|
report_inline_declared(candidates, options)
|
|
87
92
|
report_withheld_annotations(candidates, options)
|
|
88
93
|
report_unrenderable(generator.unrenderable)
|
|
89
|
-
report_unresolvable_superclasses(generator.unresolvable_superclasses)
|
|
94
|
+
report_unresolvable_superclasses(generator.unresolvable_superclasses, generator)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# The parser `run` parses with, built without parsing anything (the catalogue reads it, ADR-126 WD3).
|
|
98
|
+
def option_parser(options = {})
|
|
99
|
+
build_option_parser(options)
|
|
90
100
|
end
|
|
91
101
|
|
|
92
102
|
private
|
|
@@ -217,16 +227,36 @@ module Rigor
|
|
|
217
227
|
# declaring it would fail to build and take the class's type coverage with it. Naming the unresolved
|
|
218
228
|
# superclass is the actionable part — it is usually one gem's missing RBS standing between the user
|
|
219
229
|
# and signatures for a whole layer of their app.
|
|
220
|
-
|
|
221
|
-
|
|
230
|
+
#
|
|
231
|
+
# Issue #2437 — a superclass the project declares in source but this run wrote nothing for gets different
|
|
232
|
+
# advice: no gem RBS is missing; the superclass's own methods could not be typed, or were not written.
|
|
233
|
+
def report_unresolvable_superclasses(unresolvable, generator)
|
|
234
|
+
reports = unresolvable.map { |name, superclass| [name, *generator.unresolvable_superclass_report(superclass)] }
|
|
235
|
+
project, external = reports.partition { |_name, kind, _root| kind == :project }
|
|
236
|
+
.map { |rows| rows.to_h { |name, _kind, root| [name, root] } }
|
|
237
|
+
report_unwritten_project_superclasses(project)
|
|
238
|
+
return if external.empty?
|
|
222
239
|
|
|
223
240
|
@err.puts(
|
|
224
|
-
"rigor sig-gen: skipped #{
|
|
241
|
+
"rigor sig-gen: skipped #{external.size} class(es) whose superclass no loaded RBS declares. " \
|
|
225
242
|
"A signature for them would fail to build (`RBS::NoSuperclassFoundError`) and would leave the " \
|
|
226
243
|
"class LESS typed than it is now. Provide RBS for the superclass — `rbs collection install`, the " \
|
|
227
244
|
"gem's own `sig/`, or a `signature_paths:` entry — and re-run."
|
|
228
245
|
)
|
|
229
|
-
|
|
246
|
+
external.sort.each { |class_name, superclass| @err.puts(" #{class_name} < #{superclass}") }
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def report_unwritten_project_superclasses(project)
|
|
250
|
+
return if project.empty?
|
|
251
|
+
|
|
252
|
+
@err.puts(
|
|
253
|
+
"rigor sig-gen: skipped #{project.size} class(es) whose superclass this run wrote no signature for. " \
|
|
254
|
+
"The superclass is declared in the project's source, but its own methods could not be typed, or its " \
|
|
255
|
+
"`sig/` file could not be written, so a signature for these subclasses would fail to build " \
|
|
256
|
+
"(`RBS::NoSuperclassFoundError`). Annotate or type the superclass's methods, fix its `sig/` file, or " \
|
|
257
|
+
"hand-write its declaration, and re-run."
|
|
258
|
+
)
|
|
259
|
+
project.sort.each { |class_name, superclass| @err.puts(" #{class_name} < #{superclass}") }
|
|
230
260
|
end
|
|
231
261
|
|
|
232
262
|
# #1991 — the path errors `rigor check` reports for the arguments: a path that is neither a directory nor a
|
|
@@ -265,18 +295,19 @@ module Rigor
|
|
|
265
295
|
end
|
|
266
296
|
|
|
267
297
|
# @return exit status — non-zero when a file the user asked to write could not be written.
|
|
268
|
-
def dispatch_write(candidates, configuration, options)
|
|
269
|
-
|
|
298
|
+
def dispatch_write(candidates, configuration, options, generator)
|
|
299
|
+
writer = build_writer(configuration, options)
|
|
300
|
+
results = writer.write_all(candidates, header_spelling: generator.method(:generic_header_spelling))
|
|
270
301
|
|
|
271
302
|
refused = refused_candidates(candidates)
|
|
272
|
-
SigGen::Renderer.new(out: @out).render_write(results: results, format: options.fetch(:format), refused: refused
|
|
303
|
+
SigGen::Renderer.new(out: @out).render_write(results: results, format: options.fetch(:format), refused: refused,
|
|
304
|
+
left_bare: writer.left_bare)
|
|
273
305
|
SigGen::Renderer.refusal_lines(refused).each { |line| @err.puts(line) }
|
|
274
|
-
# A refused write (an assembled file that does not parse, an existing target that is not valid UTF-8
|
|
275
|
-
# or a method whose inline and `sig/` declarations disagree without `--overwrite`) means the
|
|
276
|
-
# write and did not get one, so the command must not report success — a green
|
|
277
|
-
# would otherwise mean nothing.
|
|
278
|
-
|
|
279
|
-
results.any? { |result| refusals.include?(result.action) } || !refused.empty? ? 1 : 0
|
|
306
|
+
# A refused write (an assembled file that does not parse, an existing target that is not valid UTF-8 or
|
|
307
|
+
# does not parse, or a method whose inline and `sig/` declarations disagree without `--overwrite`) means the
|
|
308
|
+
# user asked for a write and did not get one, so the command must not report success — a green
|
|
309
|
+
# `sig-gen --write` in CI would otherwise mean nothing.
|
|
310
|
+
results.any? { |result| TARGET_REFUSALS.include?(result.action) } || !refused.empty? ? 1 : 0
|
|
280
311
|
end
|
|
281
312
|
|
|
282
313
|
# ADR-112 WD4 — the methods whose inline and `sig/` declarations disagree, refused because the run did not
|
|
@@ -292,11 +323,15 @@ module Rigor
|
|
|
292
323
|
# --overwrite` would apply it.
|
|
293
324
|
#
|
|
294
325
|
# @return 1 when `sig/` is out of date (or a write would be refused), 0 when it is current.
|
|
295
|
-
|
|
296
|
-
|
|
326
|
+
#
|
|
327
|
+
# A generic header held back from the repair (#2605) is reported, but does not fail the check: `--write`
|
|
328
|
+
# cannot change it, and it was failing the project's env build before the run as well.
|
|
329
|
+
def dispatch_check(candidates, configuration, options, generator)
|
|
330
|
+
writer = build_writer(configuration, options, dry_run: true)
|
|
331
|
+
results = writer.write_all(candidates, header_spelling: generator.method(:generic_header_spelling))
|
|
297
332
|
refused = refused_candidates(candidates)
|
|
298
333
|
SigGen::Renderer.new(out: @out).render_check(results: results, format: options.fetch(:format),
|
|
299
|
-
refused: refused)
|
|
334
|
+
refused: refused, left_bare: writer.left_bare)
|
|
300
335
|
stale = SigGen::Renderer.out_of_date(results)
|
|
301
336
|
return 0 if stale.empty? && refused.empty?
|
|
302
337
|
|
|
@@ -305,16 +340,40 @@ module Rigor
|
|
|
305
340
|
end
|
|
306
341
|
|
|
307
342
|
def report_check_failure(stale, refused)
|
|
308
|
-
|
|
309
|
-
|
|
343
|
+
pending, refusals = stale.partition { |result| !TARGET_REFUSALS.include?(result.action) }
|
|
344
|
+
unless pending.empty?
|
|
345
|
+
@err.puts("rigor sig-gen --check: #{pending.size} signature file(s) out of date; " \
|
|
310
346
|
"run `rigor sig-gen --write` with the same options to update them.")
|
|
311
347
|
end
|
|
348
|
+
@err.puts(refused_targets_line(refusals)) unless refusals.empty?
|
|
312
349
|
return if refused.empty?
|
|
313
350
|
|
|
314
351
|
@err.puts("rigor sig-gen --check: #{refused.size} method(s) whose inline declaration and sig/ copy " \
|
|
315
352
|
"disagree; make them agree, or pass --overwrite to replace the sig/ member.")
|
|
316
353
|
end
|
|
317
354
|
|
|
355
|
+
# A target `--write` refuses is not one it would update: it is counted apart from the out-of-date targets. Where
|
|
356
|
+
# the existing file is the user's to repair (encoding, parse), it is named for a hand fix. An invalid generated
|
|
357
|
+
# file is a Rigor rendering bug (its REFUSED line says so), so it is counted but never listed as a hand fix.
|
|
358
|
+
def refused_targets_line(results)
|
|
359
|
+
by_hand = results.reject { |result| result.action == :skipped_invalid_rbs }
|
|
360
|
+
.map { |result| result.target_path.to_s }
|
|
361
|
+
head = "rigor sig-gen --check: #{results.size} signature file(s) refused; `--write` does not update them"
|
|
362
|
+
if by_hand.empty?
|
|
363
|
+
return "#{head}. The generated RBS did not parse: a Rigor bug, see its REFUSED line on stdout."
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
"#{head}. Repair by hand: #{by_hand.join(', ')}."
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
# #2461 — only a run that writes or compares files asks whether the writer will accept each target: where a
|
|
370
|
+
# target lands depends on the working directory, and print, diff and JSON output must not.
|
|
371
|
+
def target_writer(configuration, options, mode)
|
|
372
|
+
return nil unless %i[write check].include?(mode)
|
|
373
|
+
|
|
374
|
+
build_writer(configuration, options, dry_run: true)
|
|
375
|
+
end
|
|
376
|
+
|
|
318
377
|
def build_writer(configuration, options, dry_run: false)
|
|
319
378
|
layout_index = SigGen::LayoutIndex.new(signature_paths: configuration.signature_paths)
|
|
320
379
|
path_mapper = SigGen::PathMapper.new(configuration: configuration, layout_index: layout_index)
|
|
@@ -426,7 +485,7 @@ module Rigor
|
|
|
426
485
|
opts.on("--no-cache", "Do not read or write the analysis cache (effect collection only)") do
|
|
427
486
|
options[:no_cache] = true
|
|
428
487
|
end
|
|
429
|
-
|
|
488
|
+
Options.add_format(opts, options, "Output format: text or json")
|
|
430
489
|
opts.on("--params=POLICY", "Parameter policy: untyped (default), observed, observed-strict") do |value|
|
|
431
490
|
options[:params] = value
|
|
432
491
|
end
|
|
@@ -456,11 +515,9 @@ module Rigor
|
|
|
456
515
|
|
|
457
516
|
def validation_error(options)
|
|
458
517
|
mode = options.fetch(:mode)
|
|
459
|
-
format = options.fetch(:format)
|
|
460
518
|
params = options.fetch(:params)
|
|
461
519
|
|
|
462
520
|
return "--print, --diff, --write, and --check are mutually exclusive flags; pick one" if mode == "conflict"
|
|
463
|
-
return "unsupported --format=#{format}" unless VALID_FORMATS.include?(format)
|
|
464
521
|
return "unsupported --params=#{params}" unless VALID_PARAM_POLICIES.include?(params)
|
|
465
522
|
if params == "observed-strict"
|
|
466
523
|
return "--params=observed-strict is reserved until the capability-role catalog ships"
|