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
|
@@ -7,7 +7,7 @@ module Rigor
|
|
|
7
7
|
module Cache
|
|
8
8
|
# Cache producer that materialises the set of every RBS-declared class / module / alias name (top-level
|
|
9
9
|
# prefixed, e.g. `"::Math"`) currently loaded into the environment. Marshal-clean — the cache value is a
|
|
10
|
-
#
|
|
10
|
+
# Set of names.
|
|
11
11
|
#
|
|
12
12
|
# The set lets `RbsLoader#class_known?` answer point lookups in O(1) without re-parsing the name on each
|
|
13
13
|
# call, and lets a warm process skip the env walk entirely (the env still has to be built to enumerate
|
data/lib/rigor/cache/store.rb
CHANGED
|
@@ -135,14 +135,6 @@ module Rigor
|
|
|
135
135
|
end
|
|
136
136
|
end
|
|
137
137
|
|
|
138
|
-
# Walks the on-disk cache rooted at `root` and reports a producer-level inventory. Used by
|
|
139
|
-
# `rigor check --cache-stats` to surface cache size and per-producer entry counts without depending on
|
|
140
|
-
# in-process counters (which only reflect the current run).
|
|
141
|
-
#
|
|
142
|
-
# @return `{ root:, schema_version:, total_entries:, total_bytes:, producers: [{ id:, entries:,
|
|
143
|
-
# bytes: }, ...] }`. When the root does not exist or has no schema-version marker, `schema_version` is
|
|
144
|
-
# nil and the producer list is empty.
|
|
145
|
-
#
|
|
146
138
|
# The `schema_version.txt` marker content. Covers BOTH invalidation axes: the descriptor schema and the
|
|
147
139
|
# on-disk byte layout ({FORMAT_VERSION}, ADR-54). A format bump leaves the old entries permanently
|
|
148
140
|
# unreadable (header mismatch → miss) but, alone, would never reclaim their bytes — they can sit below
|
|
@@ -152,6 +144,13 @@ module Rigor
|
|
|
152
144
|
"#{PAYLOAD_ABI_VERSION}.#{Descriptor::SCHEMA_VERSION}.#{FORMAT_VERSION}"
|
|
153
145
|
end
|
|
154
146
|
|
|
147
|
+
# Walks the on-disk cache rooted at `root` and reports a producer-level inventory. Used by
|
|
148
|
+
# `rigor check --cache-stats` to surface cache size and per-producer entry counts without depending on
|
|
149
|
+
# in-process counters (which only reflect the current run).
|
|
150
|
+
#
|
|
151
|
+
# @return `{ root:, schema_version:, total_entries:, total_bytes:, producers: [{ id:, entries:,
|
|
152
|
+
# bytes: }, ...] }`. When the root does not exist or has no schema-version marker, `schema_version` is
|
|
153
|
+
# nil and the producer list is empty.
|
|
155
154
|
def self.disk_inventory(root:)
|
|
156
155
|
root_s = root.to_s
|
|
157
156
|
marker = File.join(root_s, "schema_version.txt")
|
|
@@ -360,7 +359,8 @@ module Rigor
|
|
|
360
359
|
# size-based LRU pass below stays gated on `max_bytes:` being configured: it walks all remaining
|
|
361
360
|
# `.entry` files, sorts by mtime ascending (oldest = least recently used), and unlinks from the oldest
|
|
362
361
|
# until the total is at or below the cap. Touch-on-disk-read ({read_entry}) is the cross-process LRU
|
|
363
|
-
# signal:
|
|
362
|
+
# signal: a disk hit (not an in-process-memo hit) updates the mtime when `max_bytes:` is set, so
|
|
363
|
+
# recently-read entries survive
|
|
364
364
|
# the eviction pass. Any FS error is swallowed — eviction must never break a run.
|
|
365
365
|
def evict!
|
|
366
366
|
return if @read_only
|
|
@@ -591,7 +591,8 @@ module Rigor
|
|
|
591
591
|
# — one check per Store is enough; a benign double-check under a thread race would just repeat
|
|
592
592
|
# idempotent work.
|
|
593
593
|
#
|
|
594
|
-
# A writable store clears the cache root and rewrites the marker on a stale
|
|
594
|
+
# A writable store clears the cache root and rewrites the marker on a stale marker (a missing or empty
|
|
595
|
+
# one is rewritten without clearing), then reports
|
|
595
596
|
# available. A read-only store never touches the root — no mkdir, no marker write, no destructive clear
|
|
596
597
|
# — so it reports available ONLY when the on-disk marker already matches current exactly; a stale or
|
|
597
598
|
# missing marker (e.g. a Rigor upgrade with no writable run yet, as in LSP / editor mode) reports
|
data/lib/rigor/ci_detector.rb
CHANGED
|
@@ -20,7 +20,7 @@ module Rigor
|
|
|
20
20
|
# non-CLI consumers exist: {Configuration#cache_validation_strict?} resolves the `auto` cache-validation default
|
|
21
21
|
# through it (a fresh CI checkout regenerates every stat tuple, so ADR-87's stat tier cannot work there).
|
|
22
22
|
# Detection is fully testable; callers pass `ENV`.
|
|
23
|
-
# `RIGOR_CI_DETECT=0` (or `false`/`no`) disables it globally — the seam the spec suite uses for determinism.
|
|
23
|
+
# `RIGOR_CI_DETECT=0` (or `false`/`no`/`off`) disables it globally — the seam the spec suite uses for determinism.
|
|
24
24
|
module CiDetector
|
|
25
25
|
Platform = Struct.new(:id, :name, :format, :tier, keyword_init: true) do
|
|
26
26
|
def native_stdout? = tier == :native_stdout
|
|
@@ -16,6 +16,7 @@ require_relative "../inference/statement_evaluator"
|
|
|
16
16
|
require_relative "prism_colorizer"
|
|
17
17
|
require_relative "command"
|
|
18
18
|
require_relative "../project_environment"
|
|
19
|
+
require_relative "json_document"
|
|
19
20
|
|
|
20
21
|
module Rigor
|
|
21
22
|
class CLI
|
|
@@ -32,10 +33,12 @@ module Rigor
|
|
|
32
33
|
class AnnotateCommand < Command
|
|
33
34
|
USAGE = "Usage: rigor annotate [options] FILE"
|
|
34
35
|
|
|
35
|
-
# Trailing `#=> …` annotation comment
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
36
|
+
# Trailing `#=> …` annotation comment, matched and stripped before re-annotating so re-running is idempotent.
|
|
37
|
+
# Follows xmpfilter's convention of owning the `#=>` marker, and also absorbs the older `#=> dump_type: <type>`
|
|
38
|
+
# spelling.
|
|
39
|
+
# Strips the first `#=>` that is preceded by whitespace and followed by whitespace or the end of the line,
|
|
40
|
+
# together with that preceding whitespace, through the end of the line. A string literal is not special-cased,
|
|
41
|
+
# since the pattern does not parse Ruby; `a#=>b` and `x #=>y` are left alone.
|
|
39
42
|
ANNOTATION_PATTERN = /\s+#=>(?:\s.*)?\z/
|
|
40
43
|
|
|
41
44
|
# Arguments for highlighting through `bat`: the annotated text arrives on stdin, so the language must be explicit;
|
|
@@ -59,19 +62,13 @@ module Rigor
|
|
|
59
62
|
execute(file, options)
|
|
60
63
|
end
|
|
61
64
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def
|
|
65
|
-
|
|
66
|
-
options = { config: nil, color: @out.tty? && !no_color_env?, bat: nil, format: :text }
|
|
67
|
-
|
|
68
|
-
parser = OptionParser.new do |opts|
|
|
65
|
+
# The parser this command parses with, built without parsing anything: the catalogue reads the
|
|
66
|
+
# flags and formats from it (ADR-126 WD3). `options` is only written by the switches' blocks.
|
|
67
|
+
def option_parser(options = {})
|
|
68
|
+
OptionParser.new do |opts|
|
|
69
69
|
opts.banner = USAGE
|
|
70
70
|
Options.add_config(opts, options)
|
|
71
|
-
|
|
72
|
-
"Output format: text (default) or json (a { line => type } map)") do |value|
|
|
73
|
-
options[:format] = value.to_sym
|
|
74
|
-
end
|
|
71
|
+
Options.add_format(opts, options, "Output format: text (default) or json (a { line => type } map)")
|
|
75
72
|
opts.on("--[no-]color",
|
|
76
73
|
"Force or disable ANSI colour (default: auto-detect a tty; honours NO_COLOR)") do |value|
|
|
77
74
|
options[:color] = value
|
|
@@ -81,7 +78,15 @@ module Rigor
|
|
|
81
78
|
options[:bat] = value
|
|
82
79
|
end
|
|
83
80
|
end
|
|
84
|
-
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def parse_options
|
|
86
|
+
# Default: colour a tty, unless `NO_COLOR` opts out. An explicit `--color` / `--no-color` overrides both.
|
|
87
|
+
options = { config: nil, color: @out.tty? && !no_color_env?, bat: nil, format: "text" }
|
|
88
|
+
|
|
89
|
+
option_parser(options).parse!(@argv)
|
|
85
90
|
|
|
86
91
|
options
|
|
87
92
|
end
|
|
@@ -118,7 +123,7 @@ module Rigor
|
|
|
118
123
|
line_types = LineTypeCollector.new(scope_index).collect(parse_result.value)
|
|
119
124
|
|
|
120
125
|
case options.fetch(:format)
|
|
121
|
-
when
|
|
126
|
+
when "json"
|
|
122
127
|
emit_json(line_types)
|
|
123
128
|
else
|
|
124
129
|
@out.puts(render(annotate(source, line_types), color: options.fetch(:color), bat: options.fetch(:bat)))
|
|
@@ -132,7 +137,7 @@ module Rigor
|
|
|
132
137
|
# object keys).
|
|
133
138
|
def emit_json(line_types)
|
|
134
139
|
annotations = line_types.keys.sort.to_h { |line| [line.to_s, line_types[line].describe(:short)] }
|
|
135
|
-
@out.puts(JSON.generate({ "annotations" => annotations }))
|
|
140
|
+
@out.puts(JSON.generate(JsonDocument.stamp({ "annotations" => annotations })))
|
|
136
141
|
end
|
|
137
142
|
|
|
138
143
|
# The plugin-aware environment for the annotated file, so a `#=> <type>` matches what `rigor check`
|
|
@@ -225,7 +230,7 @@ module Rigor
|
|
|
225
230
|
@scope_index = scope_index
|
|
226
231
|
end
|
|
227
232
|
|
|
228
|
-
# @return 1-indexed line
|
|
233
|
+
# @return map of 1-indexed line number to type.
|
|
229
234
|
def collect(program)
|
|
230
235
|
by_line = {}
|
|
231
236
|
each_statement(program) do |statement|
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require "optparse"
|
|
4
4
|
|
|
5
|
+
require "did_you_mean"
|
|
6
|
+
|
|
5
7
|
require_relative "../analysis/baseline"
|
|
6
8
|
require_relative "options"
|
|
7
9
|
require_relative "../analysis/runner"
|
|
@@ -24,7 +26,9 @@ module Rigor
|
|
|
24
26
|
# rigor baseline prune
|
|
25
27
|
class BaselineCommand < Command # rubocop:disable Metrics/ClassLength
|
|
26
28
|
EXIT_USAGE = 64
|
|
29
|
+
USAGE = "Usage: rigor baseline <subcommand> [options]"
|
|
27
30
|
DEFAULT_BASELINE_PATH = ".rigor-baseline.yml"
|
|
31
|
+
MAX_UNBASELINEABLE_FILES = 10
|
|
28
32
|
|
|
29
33
|
SUBCOMMANDS = %w[generate regenerate dump drift prune].freeze
|
|
30
34
|
|
|
@@ -41,6 +45,8 @@ module Rigor
|
|
|
41
45
|
when "prune" then run_prune
|
|
42
46
|
else
|
|
43
47
|
@err.puts("Unknown baseline subcommand: #{subcommand.inspect}")
|
|
48
|
+
nearest = ::DidYouMean::SpellChecker.new(dictionary: SUBCOMMANDS).correct(subcommand.to_s).first
|
|
49
|
+
@err.puts("Did you mean `rigor baseline #{nearest}`?") if nearest
|
|
44
50
|
@err.puts(help)
|
|
45
51
|
EXIT_USAGE
|
|
46
52
|
end
|
|
@@ -49,10 +55,78 @@ module Rigor
|
|
|
49
55
|
EXIT_USAGE
|
|
50
56
|
end
|
|
51
57
|
|
|
58
|
+
# The parser of `subcommand`, built without parsing anything (the catalogue reads it, ADR-126 WD3).
|
|
59
|
+
def option_parser(subcommand, options = {})
|
|
60
|
+
case subcommand
|
|
61
|
+
when "generate", "regenerate" then generate_parser(subcommand, options)
|
|
62
|
+
when "dump" then dump_parser(options)
|
|
63
|
+
when "drift" then drift_parser(options)
|
|
64
|
+
when "prune" then prune_parser(options)
|
|
65
|
+
else raise ArgumentError, "unknown baseline subcommand: #{subcommand.inspect}"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
52
69
|
private
|
|
53
70
|
|
|
71
|
+
def generate_parser(subcommand, options)
|
|
72
|
+
OptionParser.new do |opts|
|
|
73
|
+
opts.banner = "Usage: rigor baseline #{subcommand} [options]"
|
|
74
|
+
Options.add_config(opts, options)
|
|
75
|
+
opts.on("--output=PATH", "Write baseline to PATH (default: #{DEFAULT_BASELINE_PATH})") do |v|
|
|
76
|
+
options[:output] = v
|
|
77
|
+
end
|
|
78
|
+
opts.on("--match-mode=MODE", %i[rule message],
|
|
79
|
+
"Row form: rule (default) or message") do |v|
|
|
80
|
+
options[:match_mode] = v
|
|
81
|
+
end
|
|
82
|
+
# `regenerate` always overwrites — no `--force` to offer.
|
|
83
|
+
if subcommand == "generate"
|
|
84
|
+
opts.on("--force", "Overwrite an existing baseline file") { options[:force] = true }
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def dump_parser(options)
|
|
90
|
+
OptionParser.new do |opts|
|
|
91
|
+
opts.banner = "Usage: rigor baseline dump [options]"
|
|
92
|
+
opts.on("--baseline=PATH", "Path to the baseline file (default: #{DEFAULT_BASELINE_PATH})") do |v|
|
|
93
|
+
options[:baseline] = v
|
|
94
|
+
end
|
|
95
|
+
Options.add_format(opts, options, "Output format: text (default) or json")
|
|
96
|
+
opts.on("--rule=RULE", "Filter rows by exact rule id") { |v| options[:rule] = v }
|
|
97
|
+
opts.on("--file=GLOB", "Filter rows by File.fnmatch? glob") { |v| options[:file] = v }
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def drift_parser(options)
|
|
102
|
+
OptionParser.new do |opts|
|
|
103
|
+
opts.banner = "Usage: rigor baseline drift [options]"
|
|
104
|
+
Options.add_config(opts, options)
|
|
105
|
+
opts.on("--baseline=PATH", "Path to the baseline file (default: #{DEFAULT_BASELINE_PATH})") do |v|
|
|
106
|
+
options[:baseline] = v
|
|
107
|
+
end
|
|
108
|
+
opts.on("--only=STATUS", %i[within over cleared reducible],
|
|
109
|
+
"Show only buckets with the given status (within|over|cleared|reducible)") do |v|
|
|
110
|
+
options[:only] = v
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def prune_parser(options)
|
|
116
|
+
OptionParser.new do |opts|
|
|
117
|
+
opts.banner = "Usage: rigor baseline prune [options]"
|
|
118
|
+
Options.add_config(opts, options)
|
|
119
|
+
opts.on("--baseline=PATH", "Path to the baseline file (default: #{DEFAULT_BASELINE_PATH})") do |v|
|
|
120
|
+
options[:baseline] = v
|
|
121
|
+
end
|
|
122
|
+
opts.on("--dry-run", "Show what would be dropped without writing the file") { options[:dry_run] = true }
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
54
126
|
def run_generate
|
|
55
127
|
options = parse_generate_options
|
|
128
|
+
return positional_paths_error("generate") unless @argv.empty?
|
|
129
|
+
|
|
56
130
|
path = options.fetch(:output)
|
|
57
131
|
|
|
58
132
|
if File.exist?(path) && !options.fetch(:force)
|
|
@@ -69,9 +143,32 @@ module Rigor
|
|
|
69
143
|
# so `rigor baseline regenerate` reads cleanly as "make the baseline match reality again".
|
|
70
144
|
def run_regenerate
|
|
71
145
|
options = parse_generate_options(subcommand: "regenerate")
|
|
146
|
+
return positional_paths_error("regenerate") unless @argv.empty?
|
|
147
|
+
|
|
72
148
|
write_baseline(options, verb: "regenerated baseline")
|
|
73
149
|
end
|
|
74
150
|
|
|
151
|
+
# Issues #2466 and #2577 — the baseline covers the configured `paths:`, never the positional arguments: a
|
|
152
|
+
# baseline built over a subset would silently drop the entries of every other file. `drift` and `prune`
|
|
153
|
+
# analyse the configured `paths:` and `dump` prints the whole file, so none of the five reads a path argument.
|
|
154
|
+
# Reject them, as `rigor effects update` does (`effects_snapshot_command.rb:54`), rather than honour them.
|
|
155
|
+
def positional_paths_error(subcommand)
|
|
156
|
+
@err.puts("rigor baseline #{subcommand}: unexpected argument(s) #{@argv.map(&:inspect).join(' ')}; " \
|
|
157
|
+
"#{positional_paths_hint(subcommand)}")
|
|
158
|
+
EXIT_USAGE
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def positional_paths_hint(subcommand)
|
|
162
|
+
case subcommand
|
|
163
|
+
when "dump"
|
|
164
|
+
"dump reads the baseline file, so pass none: use --file=GLOB to filter rows or --baseline=PATH to pick a file"
|
|
165
|
+
when "drift", "prune"
|
|
166
|
+
"#{subcommand} audits the configured `paths:`, so pass none"
|
|
167
|
+
else
|
|
168
|
+
"the baseline is generated from the configured `paths:`, so pass none"
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
75
172
|
def write_baseline(options, verb:)
|
|
76
173
|
path = options.fetch(:output)
|
|
77
174
|
configuration = Configuration.load(options.fetch(:config))
|
|
@@ -81,11 +178,13 @@ module Rigor
|
|
|
81
178
|
project_root: Dir.pwd)
|
|
82
179
|
File.write(path, baseline.to_yaml)
|
|
83
180
|
|
|
181
|
+
covered = baseline.buckets.sum(&:count)
|
|
84
182
|
@err.puts(
|
|
85
183
|
"rigor: #{verb} #{path} " \
|
|
86
|
-
"(#{baseline.size} bucket(s) covering #{
|
|
184
|
+
"(#{baseline.size} bucket(s) covering #{covered} diagnostic(s); " \
|
|
87
185
|
"match-mode: #{options.fetch(:match_mode)})"
|
|
88
186
|
)
|
|
187
|
+
report_unbaselineable(Analysis::Baseline.unkeyable(diagnostics))
|
|
89
188
|
if configuration.baseline_path.nil?
|
|
90
189
|
# Name the config file that actually governs this project (`.rigor.dist.yml` when no local override
|
|
91
190
|
# exists), so the hint points at the file the user must edit.
|
|
@@ -98,6 +197,38 @@ module Rigor
|
|
|
98
197
|
0
|
|
99
198
|
end
|
|
100
199
|
|
|
200
|
+
# Diagnostics the baseline cannot key never enter the file, so the next `rigor check` surfaces them. Say so,
|
|
201
|
+
# with how many, why and where; the exit status stays 0 (the file was written as asked).
|
|
202
|
+
def report_unbaselineable(unkeyable)
|
|
203
|
+
return if unkeyable.empty?
|
|
204
|
+
|
|
205
|
+
no_rule, no_path = unkeyable.partition { |d| d.qualified_rule.nil? }
|
|
206
|
+
files = unbaselineable_files(no_rule)
|
|
207
|
+
parts = []
|
|
208
|
+
unless no_rule.empty?
|
|
209
|
+
parts << "#{no_rule.size} diagnostic(s) have no rule (parse, path or internal errors) " \
|
|
210
|
+
"in #{files.size} file(s)"
|
|
211
|
+
end
|
|
212
|
+
parts << "#{no_path.size} diagnostic(s) have no path" unless no_path.empty?
|
|
213
|
+
listing = files.empty? ? "" : ":"
|
|
214
|
+
@err.puts("rigor: #{parts.join('; ')} and cannot be baselined; " \
|
|
215
|
+
"fix them, or remove or exclude the files#{listing}")
|
|
216
|
+
files.first(MAX_UNBASELINEABLE_FILES).each { |f| @err.puts(" #{f}") }
|
|
217
|
+
extra = files.size - MAX_UNBASELINEABLE_FILES
|
|
218
|
+
@err.puts(" ... and #{extra} more file(s)") if extra.positive?
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def unbaselineable_files(diagnostics)
|
|
222
|
+
root = Pathname.new(Dir.pwd)
|
|
223
|
+
diagnostics.filter_map(&:path).map { |p| relative_to(root, p) }.uniq.sort
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def relative_to(root, path)
|
|
227
|
+
Pathname.new(path).relative_path_from(root).to_s
|
|
228
|
+
rescue ArgumentError
|
|
229
|
+
path
|
|
230
|
+
end
|
|
231
|
+
|
|
101
232
|
def parse_generate_options(subcommand: "generate")
|
|
102
233
|
options = {
|
|
103
234
|
config: nil,
|
|
@@ -105,22 +236,7 @@ module Rigor
|
|
|
105
236
|
match_mode: :rule,
|
|
106
237
|
force: false
|
|
107
238
|
}
|
|
108
|
-
|
|
109
|
-
opts.banner = "Usage: rigor baseline #{subcommand} [options]"
|
|
110
|
-
Options.add_config(opts, options)
|
|
111
|
-
opts.on("--output=PATH", "Write baseline to PATH (default: #{DEFAULT_BASELINE_PATH})") do |v|
|
|
112
|
-
options[:output] = v
|
|
113
|
-
end
|
|
114
|
-
opts.on("--match-mode=MODE", %i[rule message],
|
|
115
|
-
"Row form: rule (default) or message") do |v|
|
|
116
|
-
options[:match_mode] = v
|
|
117
|
-
end
|
|
118
|
-
# `regenerate` always overwrites — no `--force` to offer.
|
|
119
|
-
if subcommand == "generate"
|
|
120
|
-
opts.on("--force", "Overwrite an existing baseline file") { options[:force] = true }
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
parser.parse!(@argv)
|
|
239
|
+
generate_parser(subcommand, options).parse!(@argv)
|
|
124
240
|
options
|
|
125
241
|
end
|
|
126
242
|
|
|
@@ -139,8 +255,6 @@ module Rigor
|
|
|
139
255
|
runner.run(configuration_for_generation.paths).diagnostics
|
|
140
256
|
end
|
|
141
257
|
|
|
142
|
-
# The loaded configuration with only `baseline` disabled (#1777): any other key can change the
|
|
143
|
-
# diagnostic set, and the baseline must record the set `rigor check` will then report.
|
|
144
258
|
def override_configuration_baseline_off(configuration)
|
|
145
259
|
configuration.without_baseline
|
|
146
260
|
end
|
|
@@ -149,31 +263,22 @@ module Rigor
|
|
|
149
263
|
|
|
150
264
|
def run_dump
|
|
151
265
|
options = parse_dump_options
|
|
266
|
+
return positional_paths_error("dump") unless @argv.empty?
|
|
267
|
+
|
|
152
268
|
baseline = load_baseline_or_exit(options.fetch(:baseline))
|
|
153
269
|
return EXIT_USAGE if baseline == :error
|
|
154
270
|
|
|
155
271
|
rows = filter_dump_rows(baseline.buckets, options)
|
|
156
272
|
case options.fetch(:format)
|
|
157
|
-
when
|
|
273
|
+
when "json" then @out.puts(JSON.pretty_generate(dump_to_json(rows)))
|
|
158
274
|
else dump_text(rows, options)
|
|
159
275
|
end
|
|
160
276
|
0
|
|
161
277
|
end
|
|
162
278
|
|
|
163
279
|
def parse_dump_options
|
|
164
|
-
options = { baseline: DEFAULT_BASELINE_PATH, format:
|
|
165
|
-
|
|
166
|
-
opts.banner = "Usage: rigor baseline dump [options]"
|
|
167
|
-
opts.on("--baseline=PATH", "Path to the baseline file (default: #{DEFAULT_BASELINE_PATH})") do |v|
|
|
168
|
-
options[:baseline] = v
|
|
169
|
-
end
|
|
170
|
-
opts.on("--format=FORMAT", %i[text json], "Output format: text (default) or json") do |v|
|
|
171
|
-
options[:format] = v
|
|
172
|
-
end
|
|
173
|
-
opts.on("--rule=RULE", "Filter rows by exact rule id") { |v| options[:rule] = v }
|
|
174
|
-
opts.on("--file=GLOB", "Filter rows by File.fnmatch? glob") { |v| options[:file] = v }
|
|
175
|
-
end
|
|
176
|
-
parser.parse!(@argv)
|
|
280
|
+
options = { baseline: DEFAULT_BASELINE_PATH, format: "text", rule: nil, file: nil }
|
|
281
|
+
dump_parser(options).parse!(@argv)
|
|
177
282
|
options
|
|
178
283
|
end
|
|
179
284
|
|
|
@@ -226,6 +331,8 @@ module Rigor
|
|
|
226
331
|
|
|
227
332
|
def run_drift
|
|
228
333
|
options = parse_drift_options
|
|
334
|
+
return positional_paths_error("drift") unless @argv.empty?
|
|
335
|
+
|
|
229
336
|
baseline = load_baseline_or_exit(options.fetch(:baseline))
|
|
230
337
|
return EXIT_USAGE if baseline == :error
|
|
231
338
|
|
|
@@ -254,18 +361,7 @@ module Rigor
|
|
|
254
361
|
baseline: DEFAULT_BASELINE_PATH,
|
|
255
362
|
only: nil
|
|
256
363
|
}
|
|
257
|
-
|
|
258
|
-
opts.banner = "Usage: rigor baseline drift [options]"
|
|
259
|
-
Options.add_config(opts, options)
|
|
260
|
-
opts.on("--baseline=PATH", "Path to the baseline file (default: #{DEFAULT_BASELINE_PATH})") do |v|
|
|
261
|
-
options[:baseline] = v
|
|
262
|
-
end
|
|
263
|
-
opts.on("--only=STATUS", %i[within over cleared reducible],
|
|
264
|
-
"Show only buckets with the given status (within|over|cleared|reducible)") do |v|
|
|
265
|
-
options[:only] = v
|
|
266
|
-
end
|
|
267
|
-
end
|
|
268
|
-
parser.parse!(@argv)
|
|
364
|
+
drift_parser(options).parse!(@argv)
|
|
269
365
|
options
|
|
270
366
|
end
|
|
271
367
|
|
|
@@ -300,6 +396,8 @@ module Rigor
|
|
|
300
396
|
|
|
301
397
|
def run_prune
|
|
302
398
|
options = parse_prune_options
|
|
399
|
+
return positional_paths_error("prune") unless @argv.empty?
|
|
400
|
+
|
|
303
401
|
baseline = load_baseline_or_exit(options.fetch(:baseline))
|
|
304
402
|
return EXIT_USAGE if baseline == :error
|
|
305
403
|
|
|
@@ -328,15 +426,7 @@ module Rigor
|
|
|
328
426
|
baseline: DEFAULT_BASELINE_PATH,
|
|
329
427
|
dry_run: false
|
|
330
428
|
}
|
|
331
|
-
|
|
332
|
-
opts.banner = "Usage: rigor baseline prune [options]"
|
|
333
|
-
Options.add_config(opts, options)
|
|
334
|
-
opts.on("--baseline=PATH", "Path to the baseline file (default: #{DEFAULT_BASELINE_PATH})") do |v|
|
|
335
|
-
options[:baseline] = v
|
|
336
|
-
end
|
|
337
|
-
opts.on("--dry-run", "Show what would be dropped without writing the file") { options[:dry_run] = true }
|
|
338
|
-
end
|
|
339
|
-
parser.parse!(@argv)
|
|
429
|
+
prune_parser(options).parse!(@argv)
|
|
340
430
|
options
|
|
341
431
|
end
|
|
342
432
|
|
|
@@ -362,7 +452,7 @@ module Rigor
|
|
|
362
452
|
|
|
363
453
|
def help
|
|
364
454
|
<<~HELP
|
|
365
|
-
|
|
455
|
+
#{USAGE}
|
|
366
456
|
|
|
367
457
|
Subcommands:
|
|
368
458
|
generate Write a fresh baseline file from a `rigor check` run.
|