rigortype 0.4.6 → 0.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +4 -2
- data/data/core_overlay/bigdecimal.rbs +33 -0
- data/data/core_overlay/resolv.rbs +30 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +45 -13
- data/data/gem_overlay/addressable/addressable.rbs +40 -0
- 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 +6 -3
- data/docs/handbook/11-sig-gen.md +85 -3
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/install.md +20 -0
- data/docs/manual/01-installation.md +67 -26
- data/docs/manual/02-cli-reference.md +455 -113
- data/docs/manual/03-configuration.md +76 -5
- data/docs/manual/04-diagnostics.md +5 -4
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/08-skills.md +11 -0
- data/docs/manual/09-editor-integration.md +21 -2
- data/docs/manual/10-mcp-server.md +38 -2
- data/docs/manual/12-caching.md +4 -3
- data/docs/manual/14-rails-quickstart.md +1 -1
- data/docs/manual/19-effect-labels.md +3 -4
- data/docs/manual/plugins/rigor-actionpack.md +32 -15
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +23 -1
- data/docs/manual/plugins/rigor-sorbet.md +17 -3
- data/lib/rigor/analysis/baseline.rb +24 -27
- data/lib/rigor/analysis/check_rules/always_raises_shield.rb +147 -0
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
- data/lib/rigor/analysis/check_rules/call_facts.rb +58 -0
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +12 -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 +16 -4
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +6 -1
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
- data/lib/rigor/analysis/check_rules/plugin_api_surface.rb +195 -0
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +41 -12
- data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +27 -1
- data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +3 -3
- data/lib/rigor/analysis/check_rules/replaced_singleton_defs.rb +96 -0
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- data/lib/rigor/analysis/check_rules/rule_ids.rb +9 -2
- 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 +2 -2
- data/lib/rigor/analysis/check_rules.rb +720 -141
- data/lib/rigor/analysis/crash_signature.rb +45 -3
- data/lib/rigor/analysis/dependency_recorder.rb +237 -80
- 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.rb +11 -1
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +4 -3
- data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
- data/lib/rigor/analysis/incremental_session.rb +306 -53
- data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -3
- data/lib/rigor/analysis/rule_catalog.rb +39 -3
- data/lib/rigor/analysis/run_cache_key.rb +27 -3
- data/lib/rigor/analysis/run_disclosure_credit.rb +26 -6
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -2
- data/lib/rigor/analysis/runner/declaration_position.rb +8 -8
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +264 -112
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +4 -9
- data/lib/rigor/analysis/runner/pool_coordinator.rb +173 -328
- data/lib/rigor/analysis/runner/project_pre_passes.rb +4 -37
- 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 +298 -109
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +62 -23
- 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 +65 -71
- data/lib/rigor/bleeding_edge.rb +22 -10
- 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/file_digest.rb +39 -31
- data/lib/rigor/cache/incremental_snapshot.rb +95 -17
- 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 +90 -4
- data/lib/rigor/cache/rbs_environment.rb +8 -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 +160 -57
- data/lib/rigor/cli/catalogue.rb +256 -0
- data/lib/rigor/cli/check_command.rb +112 -104
- 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 +26 -18
- 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 +29 -10
- 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 +92 -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 +2 -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 +214 -56
- data/lib/rigor/config_audit.rb +33 -19
- data/lib/rigor/configuration/severity_profile.rb +14 -5
- data/lib/rigor/configuration.rb +104 -35
- data/lib/rigor/effects/collector.rb +53 -21
- 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 +64 -33
- data/lib/rigor/environment/class_registry.rb +3 -3
- data/lib/rigor/environment/constant_type_cache_holder.rb +5 -0
- data/lib/rigor/environment/core_root.rb +77 -0
- data/lib/rigor/environment/default_libraries.rb +18 -1
- data/lib/rigor/environment/deferred_signature_plan.rb +381 -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/hkt_registry_holder.rb +6 -0
- 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_collection_discovery.rb +123 -8
- data/lib/rigor/environment/rbs_coverage_report.rb +12 -11
- data/lib/rigor/environment/rbs_hierarchy.rb +12 -15
- data/lib/rigor/environment/rbs_loader.rb +772 -110
- 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 +169 -42
- data/lib/rigor/flow_contribution/merger.rb +4 -4
- data/lib/rigor/inference/acceptance.rb +136 -40
- data/lib/rigor/inference/alias_mutation_floor/foreign_locals.rb +117 -0
- data/lib/rigor/inference/alias_mutation_floor/memos.rb +85 -0
- data/lib/rigor/inference/alias_mutation_floor/reach.rb +119 -0
- data/lib/rigor/inference/alias_mutation_floor/site_collector.rb +252 -0
- data/lib/rigor/inference/alias_mutation_floor.rb +339 -0
- data/lib/rigor/inference/anonymous_meta_class.rb +18 -0
- data/lib/rigor/inference/block_auto_splat.rb +1 -1
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/block_run_count.rb +185 -0
- data/lib/rigor/inference/budget_trace.rb +8 -2
- data/lib/rigor/inference/builtins/array_catalog.rb +2 -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/captured_locals.rb +2 -0
- data/lib/rigor/inference/census_foreign_block.rb +354 -0
- data/lib/rigor/inference/clause_widening.rb +1 -1
- data/lib/rigor/inference/closure_escape_analyzer.rb +11 -4
- data/lib/rigor/inference/construction_escape.rb +258 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
- data/lib/rigor/inference/declaration_walk.rb +4 -0
- 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 +33 -15
- data/lib/rigor/inference/def_return_typer.rb +47 -5
- data/lib/rigor/inference/definer_resolution.rb +19 -9
- data/lib/rigor/inference/definers_index.rb +162 -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 +16 -0
- data/lib/rigor/inference/expression_typer.rb +1559 -320
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/flow_tracer.rb +31 -14
- data/lib/rigor/inference/fork_map.rb +3 -0
- data/lib/rigor/inference/fresh_frame_blocks.rb +52 -1
- data/lib/rigor/inference/global_write_census.rb +81 -5
- 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/in_effect_refinements.rb +7 -0
- data/lib/rigor/inference/indexed_narrowing.rb +61 -15
- data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
- data/lib/rigor/inference/ivar_writer_census.rb +154 -8
- data/lib/rigor/inference/key_presence_guard.rb +51 -24
- data/lib/rigor/inference/last_line/self_evidence.rb +30 -0
- data/lib/rigor/inference/last_line.rb +5 -2
- data/lib/rigor/inference/loop_value_record.rb +66 -14
- data/lib/rigor/inference/match_rebinding/frame.rb +57 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +101 -0
- data/lib/rigor/inference/match_rebinding.rb +28 -5
- 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 +44 -30
- data/lib/rigor/inference/method_dispatcher/class_keyed_block_params.rb +65 -0
- 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/core_enumerable_receiver.rb +44 -0
- data/lib/rigor/inference/method_dispatcher/declared_constructor.rb +151 -0
- data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/interface_shape.rb +181 -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 +51 -27
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +606 -78
- 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 +3 -3
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +9 -7
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +467 -84
- 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/singleton_type_params.rb +104 -0
- 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 +123 -25
- data/lib/rigor/inference/method_parameter_binder.rb +136 -56
- data/lib/rigor/inference/module_function_state/spellings.rb +2 -2
- data/lib/rigor/inference/module_function_state.rb +24 -3
- data/lib/rigor/inference/multi_target_binder.rb +2 -2
- data/lib/rigor/inference/mutation_rejoin.rb +240 -4
- data/lib/rigor/inference/mutation_widening.rb +199 -29
- data/lib/rigor/inference/narrowing.rb +238 -50
- data/lib/rigor/inference/operand_effects.rb +43 -2
- data/lib/rigor/inference/optimistic_origin.rb +134 -9
- data/lib/rigor/inference/origin_lookup.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/precision_scanner.rb +1 -1
- data/lib/rigor/inference/project_method_ownership.rb +30 -9
- data/lib/rigor/inference/project_patched_methods.rb +65 -13
- data/lib/rigor/inference/project_patched_scanner.rb +120 -25
- data/lib/rigor/inference/project_patched_visibility.rb +39 -3
- data/lib/rigor/inference/project_yielder.rb +11 -17
- data/lib/rigor/inference/rbs_type_translator.rb +68 -15
- data/lib/rigor/inference/receiver_alias.rb +20 -0
- data/lib/rigor/inference/receiver_writer_call.rb +67 -0
- data/lib/rigor/inference/recursion_rebinding.rb +157 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +57 -17
- data/lib/rigor/inference/refined_dispatch/own_method.rb +51 -16
- data/lib/rigor/inference/refined_dispatch.rb +13 -0
- data/lib/rigor/inference/reflective_ivar_write.rb +19 -11
- data/lib/rigor/inference/remover_holder_scan.rb +287 -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/class_cvars_collector.rb +13 -6
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +70 -26
- 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 +295 -0
- data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +136 -9
- data/lib/rigor/inference/scope_indexer.rb +2213 -226
- data/lib/rigor/inference/self_call_ivar_writes.rb +103 -12
- data/lib/rigor/inference/self_preserving_block.rb +87 -0
- data/lib/rigor/inference/set_mutation.rb +189 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +42 -8
- data/lib/rigor/inference/singleton_object_constant.rb +32 -2
- data/lib/rigor/inference/statement_evaluator.rb +3268 -358
- data/lib/rigor/inference/string_mutation.rb +14 -0
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_scanner.rb +40 -17
- data/lib/rigor/inference/toplevel_def_census.rb +67 -0
- data/lib/rigor/inference/unknown_store_widening.rb +103 -4
- data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
- data/lib/rigor/inference/untyped_rejoin.rb +1 -1
- data/lib/rigor/inference/void_tail_summary.rb +29 -9
- 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/claims/comparison.rb +78 -0
- data/lib/rigor/lens/claims/request.rb +131 -0
- data/lib/rigor/lens/claims/type_reader.rb +216 -0
- data/lib/rigor/lens/claims.rb +208 -0
- data/lib/rigor/lens/contract_reader.rb +5 -0
- data/lib/rigor/lens/document.rb +1 -1
- data/lib/rigor/lens/file_reader.rb +21 -8
- data/lib/rigor/lens/plugins.rb +1 -1
- data/lib/rigor/lens/run.rb +8 -0
- data/lib/rigor/lens.rb +1 -1
- data/lib/rigor/library_probe.rb +68 -0
- data/lib/rigor/mcp/server.rb +73 -2
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/memo_audit.rb +282 -0
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- data/lib/rigor/plugin/base.rb +15 -2
- data/lib/rigor/plugin/bundled_catalog.rb +1 -12
- data/lib/rigor/plugin/io_boundary.rb +26 -4
- 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 +16 -16
- data/lib/rigor/plugin/registry.rb +16 -5
- 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 +48 -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/mutation_cache.rb +4 -1
- 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 +81 -22
- data/lib/rigor/reflection/declaration_collectors.rb +55 -0
- data/lib/rigor/reflection/declarations.rb +58 -0
- data/lib/rigor/reflection/project_method.rb +177 -0
- data/lib/rigor/reflection.rb +2 -0
- data/lib/rigor/rooted_glob.rb +32 -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 +168 -8
- data/lib/rigor/scope/resolution_chain/relevance.rb +14 -1
- data/lib/rigor/scope/resolution_chain.rb +901 -54
- data/lib/rigor/scope.rb +644 -37
- data/lib/rigor/sig_gen/bot_proposal_guard.rb +306 -0
- data/lib/rigor/sig_gen/classification.rb +18 -1
- data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
- data/lib/rigor/sig_gen/generator.rb +588 -66
- 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 +37 -4
- data/lib/rigor/sig_gen/observation_collector.rb +14 -7
- 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/platform_guarded_files.rb +184 -0
- data/lib/rigor/sig_gen/renderer.rb +15 -9
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +62 -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 +238 -37
- data/lib/rigor/sig_gen.rb +1 -0
- data/lib/rigor/source/forwardable_delegators.rb +122 -0
- data/lib/rigor/source/node_children.rb +13 -0
- 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 +173 -21
- data/lib/rigor/type/constant.rb +4 -5
- data/lib/rigor/type/hash_shape.rb +21 -40
- data/lib/rigor/type/interface.rb +60 -0
- data/lib/rigor/type/union.rb +1 -1
- data/lib/rigor/type.rb +1 -0
- 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/type_of_memo/serving.rb +99 -0
- data/lib/rigor/type_of_memo/stats.rb +43 -0
- data/lib/rigor/type_of_memo.rb +337 -0
- data/lib/rigor/typing_state.rb +1094 -0
- 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 +3 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +115 -36
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +3 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +25 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +16 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +4 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -1
- 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 +132 -33
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +20 -6
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +7 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +54 -17
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +19 -5
- 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 +11 -14
- 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 +30 -9
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +55 -13
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +15 -21
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -6
- 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 +8 -7
- 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-graphql/lib/rigor/plugin/graphql.rb +12 -2
- 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 +8 -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 +200 -4
- 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 +4 -3
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +5 -5
- 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 +210 -3
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +146 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +52 -34
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +97 -24
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +16 -16
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +4 -8
- 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/check_rules/rule_walk.rbs +6 -0
- data/sig/rigor/analysis/fact_store.rbs +2 -2
- data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
- data/sig/rigor/cache/descriptor.rbs +14 -0
- 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/environment.rbs +4 -1
- data/sig/rigor/inference.rbs +4 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +53 -18
- 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/interfaces.rbs +51 -0
- data/sig/rigor/plugin/io_boundary.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +9 -9
- data/sig/rigor/plugin/node_context.rbs +21 -0
- data/sig/rigor/plugin/protocol_contract.rbs +3 -7
- data/sig/rigor/reflection.rbs +37 -0
- data/sig/rigor/scope.rbs +43 -4
- data/sig/rigor/trinary.rbs +1 -0
- data/sig/rigor/type.rbs +9 -3
- data/sig/rigor.rbs +3 -2
- data/skills/rigor-ask/SKILL.md +1 -0
- 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/01-detect.md +1 -0
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +17 -2
- data/skills/rigor-project-init/references/06-agent-contract.md +4 -4
- data/skills/rigor-rbs-contract/SKILL.md +147 -0
- data/skills/rigor-rbs-contract/references/01-finding-candidates.md +212 -0
- data/skills/rigor-rbs-contract/references/02-contract-rules.md +157 -0
- data/skills/rigor-rbs-contract/references/03-target-own-code.md +90 -0
- data/skills/rigor-rbs-contract/references/04-target-gem-rbs-collection.md +210 -0
- data/skills/rigor-rbs-contract/references/05-declaration-mutants.md +182 -0
- data/skills/rigor-rbs-setup/SKILL.md +3 -1
- data/skills/rigor-type-oracle/SKILL.md +26 -1
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +4 -3
- metadata +82 -3
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "optparse"
|
|
4
|
+
|
|
5
|
+
require_relative "options"
|
|
6
|
+
require_relative "../version"
|
|
7
|
+
require_relative "json_document"
|
|
8
|
+
|
|
9
|
+
module Rigor
|
|
10
|
+
class CLI
|
|
11
|
+
# The machine-readable description of the CLI surface (ADR-126 WD3): one entry per command, with the one-line
|
|
12
|
+
# summary, usage, subcommands, flags, accepted formats and the MCP tool that mirrors it.
|
|
13
|
+
#
|
|
14
|
+
# Nothing here is a second hand-written list of flags. The commands that parse with an `OptionParser` expose the
|
|
15
|
+
# parser they build (`#option_parser`, built without parsing anything) and the catalogue reads its switches; the
|
|
16
|
+
# commands that read `argv` by hand declare theirs in {DECLARED}. The only prose kept here is the one-line
|
|
17
|
+
# {SUMMARIES}, which `rigor help` prints, so the text help and `rigor help --format=json` cannot disagree about
|
|
18
|
+
# which commands exist. `spec/rigor/cli/catalogue_spec.rb` gates the names against `CLI::HANDLERS`.
|
|
19
|
+
module Catalogue
|
|
20
|
+
SCHEMA_VERSION = JsonDocument::SCHEMA_VERSION
|
|
21
|
+
|
|
22
|
+
# `help` and `version` are dispatched by `CLI#run` itself, outside `CLI::HANDLERS`.
|
|
23
|
+
BUILTINS = %w[help version].freeze
|
|
24
|
+
|
|
25
|
+
# The `rigor help` listing, in print order.
|
|
26
|
+
SUMMARIES = {
|
|
27
|
+
"check" => "Analyze Ruby source files",
|
|
28
|
+
"init" => "Create a starter .rigor.yml",
|
|
29
|
+
"annotate" => "Print FILE with each line's last-expression type",
|
|
30
|
+
"type-of" => "Print inferred types at FILE:LINE[:COL] positions",
|
|
31
|
+
"lens" => "Map a file's declarations with their types and provenance (experimental)",
|
|
32
|
+
"trace" => "Replay how the engine typed FILE as a terminal animation",
|
|
33
|
+
"type-scan" => "Report Scope#type_of coverage across PATHs",
|
|
34
|
+
"effects" => "Report each method's effect labels, and the committed effect snapshot " \
|
|
35
|
+
"(opt-in; effects update/check/diff/explain)",
|
|
36
|
+
"explain" => "Print the description of one or all CheckRules",
|
|
37
|
+
"diff" => "Compare current diagnostics to a saved `rigor check --format=json` result",
|
|
38
|
+
"baseline" => "Manage the baseline file (baseline generate/regenerate/dump/drift/prune)",
|
|
39
|
+
"sig-gen" => "Emit RBS skeletons inferred from .rb sources",
|
|
40
|
+
"lsp" => "Run the Rigor Language Server (LSP) over stdio",
|
|
41
|
+
"mcp" => "Run the Rigor MCP server over stdio",
|
|
42
|
+
"triage" => "Summarise diagnostics: distribution, hotspots, hints",
|
|
43
|
+
"coverage" => "Report type-precision coverage (precise vs Dynamic ratio)",
|
|
44
|
+
"unused" => "Report unreferenced classes, modules and constants as removal candidates",
|
|
45
|
+
"plugins" => "Report activation status of every configured plugin",
|
|
46
|
+
"plugin" => "Browse bundled plugin source as worked examples (plugin <name>, plugin --list)",
|
|
47
|
+
"playground" => "Start the browser playground (requires rigor-playground gem)",
|
|
48
|
+
"describe" => "Recommend the next skill for this project (alias for `skill describe`)",
|
|
49
|
+
"skill" => "Recommend the next skill + list/print bundled Agent Skills (skill describe, skill <name>)",
|
|
50
|
+
"docs" => "Print the bundled docs offline (docs <name>, docs --list)",
|
|
51
|
+
"show-bleedingedge" => "Show the bleeding-edge overlay + what your config adopts",
|
|
52
|
+
"doctor" => "Classify setup problems vs clean run with routed next actions",
|
|
53
|
+
"upgrade" => "Migration command skeleton (queued)",
|
|
54
|
+
"version" => "Print the Rigor version",
|
|
55
|
+
"help" => "Print this help, or a command's help (help <command>, help --format=json)"
|
|
56
|
+
}.freeze
|
|
57
|
+
|
|
58
|
+
# The commands whose `argv` is read by hand, so there is no parser to read: their switches, subcommands and
|
|
59
|
+
# formats are stated here. `playground` is delegated to the optional `rigor-playground` gem and is listed without
|
|
60
|
+
# flags.
|
|
61
|
+
DECLARED = {
|
|
62
|
+
"describe" => {
|
|
63
|
+
usage: "Usage: rigor describe [--deep]",
|
|
64
|
+
flags: [
|
|
65
|
+
{ name: "--deep", arg: nil, desc: "Run `rigor check` first and route the recommendation on its result" }
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
"docs" => {
|
|
69
|
+
usage: "Usage: rigor docs [<name>] [--path <name>] [--list [<category>]]",
|
|
70
|
+
flags: [
|
|
71
|
+
{ name: "--path", arg: "NAME", desc: "Print the absolute path of a doc" },
|
|
72
|
+
{ name: "--list", arg: "CATEGORY", optional: true, desc: "List bundled docs (optionally one category)" }
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
"playground" => { usage: "Usage: rigor playground [options]" },
|
|
76
|
+
"version" => { usage: "Usage: rigor version" }
|
|
77
|
+
}.freeze
|
|
78
|
+
|
|
79
|
+
# The command files that build an `OptionParser`; the command class is the file's name in camel case.
|
|
80
|
+
PARSER_FILES = %w[
|
|
81
|
+
check annotate type_of lens trace type_scan effects explain diff sig_gen lsp mcp baseline triage coverage unused
|
|
82
|
+
plugins show_bleedingedge doctor skill plugin
|
|
83
|
+
].to_h { |file| [file.tr("_", "-"), "#{file}_command"] }.freeze
|
|
84
|
+
|
|
85
|
+
# The commands that have no parser of their own at the top, so their usage is their `USAGE` constant.
|
|
86
|
+
USAGE_FILES = { "baseline" => "baseline_command", "upgrade" => "upgrade_command" }.freeze
|
|
87
|
+
|
|
88
|
+
Ractor.make_shareable(SUMMARIES)
|
|
89
|
+
Ractor.make_shareable(USAGE_FILES)
|
|
90
|
+
Ractor.make_shareable(DECLARED)
|
|
91
|
+
Ractor.make_shareable(PARSER_FILES)
|
|
92
|
+
|
|
93
|
+
module_function
|
|
94
|
+
|
|
95
|
+
# Every command name the CLI dispatches, in `rigor help` order.
|
|
96
|
+
def names
|
|
97
|
+
SUMMARIES.keys - BUILTINS + BUILTINS
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# The `rigor help` text, rendered from {SUMMARIES} so its list is the catalogue's.
|
|
101
|
+
def help_text
|
|
102
|
+
width = names.map(&:length).max + 2
|
|
103
|
+
lines = names.map { |name| " #{name.ljust(width)}#{SUMMARIES.fetch(name)}" }
|
|
104
|
+
<<~HELP
|
|
105
|
+
Usage: rigor <command> [options]
|
|
106
|
+
|
|
107
|
+
Commands:
|
|
108
|
+
#{lines.join("\n")}
|
|
109
|
+
|
|
110
|
+
Run `rigor help <command>` (or `rigor <command> --help`) for a command's options.
|
|
111
|
+
Run `rigor describe` for the next step on this project, or `rigor help --format=json` for the full surface.
|
|
112
|
+
HELP
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# All entries, as plain hashes ready for JSON.
|
|
116
|
+
def entries
|
|
117
|
+
names.map { |name| entry(name) }
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# The document `rigor help --format=json` prints.
|
|
121
|
+
def document
|
|
122
|
+
{ "schema_version" => SCHEMA_VERSION, "version" => Rigor::VERSION, "commands" => entries }
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def entry(name)
|
|
126
|
+
declared = DECLARED.fetch(name, {})
|
|
127
|
+
parsed = parsed_entry(name)
|
|
128
|
+
{
|
|
129
|
+
"name" => name,
|
|
130
|
+
"summary" => SUMMARIES.fetch(name),
|
|
131
|
+
"usage" => declared[:usage] || parsed[:usage] || command_usage(name),
|
|
132
|
+
"subcommands" => parsed[:subcommands] || declared_subcommands(declared),
|
|
133
|
+
"flags" => parsed[:flags] || declared_flags(declared),
|
|
134
|
+
"formats" => parsed[:formats] || [],
|
|
135
|
+
"mcp_tool" => mcp_tool(name)
|
|
136
|
+
}
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# The `OptionParser` view of a command: its own flags and formats at the top, and under `subcommands` the same
|
|
140
|
+
# shape for each subcommand's parser. A command with no parser of its own (`baseline`) has empty top-level
|
|
141
|
+
# `flags` and `formats`; none of them is a union over its subcommands.
|
|
142
|
+
def parsed_entry(name)
|
|
143
|
+
pairs = parser_pairs(name)
|
|
144
|
+
return {} if pairs.nil?
|
|
145
|
+
|
|
146
|
+
own = pairs.find { |sub, _| sub.nil? }&.last
|
|
147
|
+
subs = pairs.reject { |sub, _| sub.nil? }
|
|
148
|
+
top = own ? describe_parser(own) : { flags: [], formats: [] }
|
|
149
|
+
return top if subs.empty?
|
|
150
|
+
|
|
151
|
+
top.merge(subcommands: subs.map do |sub, parser|
|
|
152
|
+
{ "name" => sub }.merge(stringify_keys(describe_parser(parser)))
|
|
153
|
+
end)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# The `[subcommand, parser]` pairs of a command (no name for its own parser), or nil when it has no parser.
|
|
157
|
+
def parser_pairs(name)
|
|
158
|
+
case name
|
|
159
|
+
when "init" then [[nil, CLI.init_option_parser]]
|
|
160
|
+
when "help" then [[nil, CLI.help_option_parser]]
|
|
161
|
+
else command_parser_pairs(name)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def command_parser_pairs(name)
|
|
166
|
+
file = PARSER_FILES[name]
|
|
167
|
+
return nil unless file
|
|
168
|
+
|
|
169
|
+
require_relative file
|
|
170
|
+
klass = CLI.const_get(file.split("_").map(&:capitalize).join)
|
|
171
|
+
case name
|
|
172
|
+
when "effects" then effects_parser_pairs(klass)
|
|
173
|
+
when "skill"
|
|
174
|
+
[[nil, klass.new(argv: []).option_parser],
|
|
175
|
+
["describe", klass.describe_option_parser("Usage: rigor skill describe [--deep]")]]
|
|
176
|
+
when "baseline"
|
|
177
|
+
command = klass.new(argv: [])
|
|
178
|
+
klass::SUBCOMMANDS.map { |sub| [sub, command.option_parser(sub)] }
|
|
179
|
+
else [[nil, klass.new(argv: []).option_parser]]
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def effects_parser_pairs(klass)
|
|
184
|
+
[[nil, klass.new(argv: []).option_parser]] +
|
|
185
|
+
EffectsSnapshotCommand::VERBS.map do |verb|
|
|
186
|
+
[verb, EffectsSnapshotCommand.new(argv: [], verb: verb).option_parser]
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# The usage line of a command with no parser to take a banner from, read from the command's own `USAGE`.
|
|
191
|
+
def command_usage(name)
|
|
192
|
+
file = USAGE_FILES[name]
|
|
193
|
+
return nil unless file
|
|
194
|
+
|
|
195
|
+
require_relative file
|
|
196
|
+
CLI.const_get(file.split("_").map(&:capitalize).join)::USAGE
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def declared_flags(declared)
|
|
200
|
+
(declared[:flags] || []).map do |flag|
|
|
201
|
+
{ "name" => flag.fetch(:name), "arg" => flag.fetch(:arg), "optional" => flag.fetch(:optional, false),
|
|
202
|
+
"desc" => flag.fetch(:desc) }
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def declared_subcommands(declared)
|
|
207
|
+
(declared[:subcommands] || []).map do |sub|
|
|
208
|
+
{ "name" => sub.fetch(:name), "usage" => sub.fetch(:usage), "flags" => [], "formats" => [] }
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Reads usage, flags and formats from the switches of `parser`.
|
|
213
|
+
def describe_parser(parser)
|
|
214
|
+
flags = parser.top.list.flat_map { |switch| flags_of(switch) }
|
|
215
|
+
format_switch = parser.top.list.any? { |switch| switch.long.first == "--format" }
|
|
216
|
+
{ usage: parser.banner.to_s.lines.first&.strip, flags: flags,
|
|
217
|
+
formats: format_switch ? Options.accepted_formats(parser) || [] : [] }
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# A switch's literal flags: `--[no-]color` is the two flags `--color` and `--no-color`.
|
|
221
|
+
def flags_of(switch)
|
|
222
|
+
long = switch.long.first
|
|
223
|
+
return [] unless long
|
|
224
|
+
|
|
225
|
+
arg, optional = arg_of(switch.arg)
|
|
226
|
+
desc = switch.desc.join(" ")
|
|
227
|
+
flag_names(long).map { |flag| { "name" => flag, "arg" => arg, "optional" => optional, "desc" => desc } }
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# `[arg, optional]`: `=[LIST]` is the argument `LIST`, optional.
|
|
231
|
+
def arg_of(raw)
|
|
232
|
+
arg = raw&.sub(/\A[= ]/, "")
|
|
233
|
+
optional = !arg.nil? && arg.start_with?("[") && arg.end_with?("]")
|
|
234
|
+
[optional ? arg[1..-2] : arg, optional]
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def flag_names(long)
|
|
238
|
+
return [long] unless long.start_with?("--[no-]")
|
|
239
|
+
|
|
240
|
+
bare = long.delete_prefix("--[no-]")
|
|
241
|
+
["--#{bare}", "--no-#{bare}"]
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# `rigor_check` mirrors `check`, `rigor_sig_gen` mirrors `sig-gen`: a tool is named for its command.
|
|
245
|
+
def mcp_tool(name)
|
|
246
|
+
require_relative "../mcp/server"
|
|
247
|
+
tool = "rigor_#{name.tr('-', '_')}"
|
|
248
|
+
tool if MCP::Server::TOOLS.any? { |candidate| candidate.fetch(:name) == tool }
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def stringify_keys(hash)
|
|
252
|
+
hash.transform_keys(&:to_s)
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
@@ -17,8 +17,9 @@ require_relative "command"
|
|
|
17
17
|
require_relative "options"
|
|
18
18
|
require_relative "diagnostic_formats"
|
|
19
19
|
require_relative "../ci_detector"
|
|
20
|
+
require_relative "json_document"
|
|
20
21
|
# ADR-87 WD4 — `coverage_scan` and `check_runner_factory` both pull the inference engine, so they are required
|
|
21
|
-
# lazily (in `
|
|
22
|
+
# lazily (in `invoke_check` / `compute_coverage`) rather than at load time: an ordinary check whose
|
|
22
23
|
# result the run-cache probe serves must reach the hit verdict without `rigor/inference` in $LOADED_FEATURES.
|
|
23
24
|
|
|
24
25
|
module Rigor
|
|
@@ -92,6 +93,97 @@ module Rigor
|
|
|
92
93
|
exit_code
|
|
93
94
|
end
|
|
94
95
|
|
|
96
|
+
# The parser this command parses with, built without parsing anything: the catalogue reads the
|
|
97
|
+
# flags and formats from it (ADR-126 WD3). `options` is only written by the switches' blocks.
|
|
98
|
+
def option_parser(options = {}) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
99
|
+
OptionParser.new do |opts| # rubocop:disable Metrics/BlockLength
|
|
100
|
+
opts.banner = "Usage: rigor check [options] [paths]"
|
|
101
|
+
Options.add_config(opts, options)
|
|
102
|
+
Options.add_format(opts, options,
|
|
103
|
+
"Output format: text, json, sarif, github, gitlab, checkstyle, junit, teamcity",
|
|
104
|
+
formats: %w[text json] + DiagnosticFormats::FORMATS)
|
|
105
|
+
opts.on("--explain", "Surface fail-soft fallback events as :info diagnostics") { options[:explain] = true }
|
|
106
|
+
opts.on("--cache-stats", "Print on-disk cache inventory at end of run") { options[:cache_stats] = true }
|
|
107
|
+
opts.on("--coverage",
|
|
108
|
+
"Add a type-precision coverage block (an extra precision pass over the analyzed files)") do
|
|
109
|
+
options[:coverage] = true
|
|
110
|
+
end
|
|
111
|
+
opts.on("--clear-cache", "Remove the .rigor/cache directory before running") { options[:clear_cache] = true }
|
|
112
|
+
opts.on("--no-cache",
|
|
113
|
+
"Disable the persistent cache for this run (with --incremental: no snapshot is read or written)") do
|
|
114
|
+
options[:no_cache] = true
|
|
115
|
+
end
|
|
116
|
+
opts.on("--[no-]stats",
|
|
117
|
+
"Print run summary (files, classes, memory, wall time) to stderr (default: on)") do |value|
|
|
118
|
+
options[:stats] = value
|
|
119
|
+
end
|
|
120
|
+
opts.on("--workers=N", Integer,
|
|
121
|
+
"Dispatch per-file analysis across N Ractor workers (default: 0; sequential)") do |value|
|
|
122
|
+
options[:workers] = value
|
|
123
|
+
end
|
|
124
|
+
Options.add_editor_mode(opts, options)
|
|
125
|
+
opts.on("--baseline=PATH",
|
|
126
|
+
"load baseline from PATH (overrides .rigor.yml `baseline:`)") do |value|
|
|
127
|
+
options[:baseline] = value
|
|
128
|
+
end
|
|
129
|
+
opts.on("--no-baseline",
|
|
130
|
+
"ignore any configured baseline for this run") do
|
|
131
|
+
options[:baseline] = false
|
|
132
|
+
end
|
|
133
|
+
opts.on("--baseline-strict",
|
|
134
|
+
"fail the run on any baseline drift (CI gate)") do
|
|
135
|
+
options[:baseline_strict] = true
|
|
136
|
+
end
|
|
137
|
+
opts.on("--fail-on=SEVERITY",
|
|
138
|
+
"exit non-zero on a diagnostic at or above SEVERITY: error (default), warning, or info") do |value|
|
|
139
|
+
# Validated post-parse (`valid_fail_on_option?`, called from `run`) rather than here — see its
|
|
140
|
+
# comment for why an unrecognised value is not raised from inside this block.
|
|
141
|
+
options[:fail_on] = value.to_s.downcase.to_sym
|
|
142
|
+
end
|
|
143
|
+
opts.on("--treat-all-as-inline-rbs",
|
|
144
|
+
"force-load rigor-rbs-inline with require_magic_comment: false") do
|
|
145
|
+
options[:treat_all_as_inline_rbs] = true
|
|
146
|
+
end
|
|
147
|
+
opts.on("--verify-incremental",
|
|
148
|
+
"assert incremental analysis matches a full run, then exit") do
|
|
149
|
+
options[:verify_incremental] = true
|
|
150
|
+
end
|
|
151
|
+
opts.on("--verify-incremental-edits=N", Integer,
|
|
152
|
+
"with --verify-incremental, also replay edits (empty, class/module skeleton, up to 2 " \
|
|
153
|
+
"one-statement drops) on N files") do |value|
|
|
154
|
+
if value.negative?
|
|
155
|
+
raise OptionParser::InvalidArgument, "--verify-incremental-edits=#{value} (must be 0 or more)"
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
options[:verify_incremental] = true
|
|
159
|
+
options[:verify_incremental_edits] = value
|
|
160
|
+
end
|
|
161
|
+
opts.on("--incremental",
|
|
162
|
+
"re-analyze only files changed since the last run (cross-process cache)") do
|
|
163
|
+
options[:incremental] = true
|
|
164
|
+
end
|
|
165
|
+
opts.on("--no-ci-detect",
|
|
166
|
+
"do not auto-emit CI-native output when a CI environment is detected") do
|
|
167
|
+
options[:ci_detect] = false
|
|
168
|
+
end
|
|
169
|
+
# ADR-50 § WD2 — `=[LIST]` (not ` [LIST]`) so a bare `--bleeding-edge` never swallows a following positional
|
|
170
|
+
# path: `rigor check --bleeding-edge lib` adopts the whole overlay and checks `lib`.
|
|
171
|
+
opts.on("--bleeding-edge=[LIST]",
|
|
172
|
+
"adopt the bleeding-edge overlay for this run " \
|
|
173
|
+
"(all features, or a comma-separated feature-id list)") do |value|
|
|
174
|
+
options[:bleeding_edge] = value.nil? || value.split(",").map(&:strip).reject(&:empty?)
|
|
175
|
+
end
|
|
176
|
+
opts.on("--no-bleeding-edge",
|
|
177
|
+
"ignore any configured bleeding_edge: selection for this run") do
|
|
178
|
+
options[:bleeding_edge] = false
|
|
179
|
+
end
|
|
180
|
+
opts.on("--no-tolerated-effects",
|
|
181
|
+
"check effect envelopes as if effects.tolerated: were empty") do
|
|
182
|
+
options[:no_tolerated_effects] = true
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
95
187
|
private
|
|
96
188
|
|
|
97
189
|
# True when `diagnostics` (already baseline-filtered and severity-resolved — the same list
|
|
@@ -219,10 +311,9 @@ module Rigor
|
|
|
219
311
|
exit_code
|
|
220
312
|
end
|
|
221
313
|
|
|
222
|
-
# ADR-46 — the two incremental-analysis check modes
|
|
223
|
-
# short-circuits
|
|
224
|
-
#
|
|
225
|
-
# also returns nil when it declines (no reusable snapshot), so the run falls back to single-file scope.
|
|
314
|
+
# ADR-46 — the two incremental-analysis check modes handle the run and return its exit code, so `run`
|
|
315
|
+
# short-circuits. Returns nil for an ordinary check, and for editor mode option B when it declines (no reusable
|
|
316
|
+
# snapshot), so the run falls back to single-file scope.
|
|
226
317
|
def dispatch_special_check_mode(configuration, options, cache_root, buffer)
|
|
227
318
|
if options.fetch(:verify_incremental)
|
|
228
319
|
# The gate compares an incremental recheck against a full-run oracle, and a buffer makes the two
|
|
@@ -283,8 +374,10 @@ module Rigor
|
|
|
283
374
|
|
|
284
375
|
# Issue #1967 — the partition check above re-analyses an unchanged tree, so every served row is still right and a
|
|
285
376
|
# missing invalidation edge goes unseen. Here each of `--verify-incremental-edits` sampled files is edited through
|
|
286
|
-
# an editor buffer (never on disk) by each of {Analysis::EditReplay.edits} — emptied,
|
|
287
|
-
# `class` / `module` skeleton
|
|
377
|
+
# an editor buffer (never on disk) by each of {Analysis::EditReplay.edits} — emptied, reduced to its
|
|
378
|
+
# `class` / `module` skeleton, and (#2277) with one body statement dropped, at most
|
|
379
|
+
# {Analysis::EditReplay::MAX_DROPS} times — and the warm recheck of the baseline
|
|
380
|
+
# ({IncrementalSession#replay_edit}) must equal
|
|
288
381
|
# a full analysis of the edited tree: a dependent the recheck failed to re-analyse is served from the baseline and
|
|
289
382
|
# differs. A run in which every replay declined compared nothing, so it fails rather than report OK.
|
|
290
383
|
def verify_edit_replays(session, configuration, paths, options)
|
|
@@ -346,8 +439,8 @@ module Rigor
|
|
|
346
439
|
return 1
|
|
347
440
|
end
|
|
348
441
|
|
|
349
|
-
line = "rigor: --verify-incremental OK — #{replayed} edits replayed (files emptied
|
|
350
|
-
"class/module skeleton), every recheck matches full"
|
|
442
|
+
line = "rigor: --verify-incremental OK — #{replayed} edits replayed (files emptied, reduced to their " \
|
|
443
|
+
"class/module skeleton, and with a body statement dropped), every recheck matches full"
|
|
351
444
|
line = "#{line}; #{declined} declined (the recheck would have been a full analysis)" if declined.positive?
|
|
352
445
|
side_output(format).puts(line)
|
|
353
446
|
0
|
|
@@ -386,9 +479,7 @@ module Rigor
|
|
|
386
479
|
return run_editor_mode_option_b(session, snapshot, fingerprint, configuration, options) if buffer
|
|
387
480
|
|
|
388
481
|
diagnostics, warm = session.run_incremental(snapshot: snapshot, fingerprint: fingerprint)
|
|
389
|
-
# The banner's file count comes from the session's analyzed set
|
|
390
|
-
# `@analyzed` advances to the current file set), so a dedicated probe Runner + `Dir.glob` is no longer
|
|
391
|
-
# built just to size the banner (recon §3 — the analysis tree was expanded three times per recheck).
|
|
482
|
+
# The banner's file count comes from the session's analyzed set, so no probe Runner is built just to size it.
|
|
392
483
|
# A warm banner reports the recheck's own sets (#1526), which only the session knows.
|
|
393
484
|
report_incremental_run(warm, session.analyzed_files.size, recheck: session.last_recheck)
|
|
394
485
|
emit_incremental_fact_surface_notes(session)
|
|
@@ -688,7 +779,7 @@ module Rigor
|
|
|
688
779
|
)
|
|
689
780
|
end
|
|
690
781
|
|
|
691
|
-
def parse_check_options
|
|
782
|
+
def parse_check_options
|
|
692
783
|
options = {
|
|
693
784
|
config: nil,
|
|
694
785
|
format: "text",
|
|
@@ -698,7 +789,7 @@ module Rigor
|
|
|
698
789
|
no_cache: false,
|
|
699
790
|
stats: true,
|
|
700
791
|
# ADR-15 Phase 4c — when nil, falls back to `RIGOR_RACTOR_WORKERS` then `.rigor.yml` `parallel.workers:` then
|
|
701
|
-
# 0 (sequential). See `resolve_workers` for the precedence chain.
|
|
792
|
+
# 0 (sequential). See `CheckRunnerFactory.resolve_workers` for the precedence chain.
|
|
702
793
|
workers: nil,
|
|
703
794
|
tmp_file: nil,
|
|
704
795
|
instead_of: nil,
|
|
@@ -747,93 +838,7 @@ module Rigor
|
|
|
747
838
|
# one-line coverage summary.
|
|
748
839
|
coverage: false
|
|
749
840
|
}
|
|
750
|
-
|
|
751
|
-
opts.banner = "Usage: rigor check [options] [paths]"
|
|
752
|
-
Options.add_config(opts, options)
|
|
753
|
-
opts.on("--format=FORMAT",
|
|
754
|
-
"Output format: text, json, sarif, github, gitlab, checkstyle, junit, teamcity") do |value|
|
|
755
|
-
options[:format] = value
|
|
756
|
-
end
|
|
757
|
-
opts.on("--explain", "Surface fail-soft fallback events as :info diagnostics") { options[:explain] = true }
|
|
758
|
-
opts.on("--cache-stats", "Print on-disk cache inventory at end of run") { options[:cache_stats] = true }
|
|
759
|
-
opts.on("--coverage",
|
|
760
|
-
"Add a type-precision coverage block (an extra precision pass over the analyzed files)") do
|
|
761
|
-
options[:coverage] = true
|
|
762
|
-
end
|
|
763
|
-
opts.on("--clear-cache", "Remove the .rigor/cache directory before running") { options[:clear_cache] = true }
|
|
764
|
-
opts.on("--no-cache",
|
|
765
|
-
"Disable the persistent cache for this run (with --incremental: no snapshot is read or written)") do
|
|
766
|
-
options[:no_cache] = true
|
|
767
|
-
end
|
|
768
|
-
opts.on("--[no-]stats",
|
|
769
|
-
"Print run summary (files, classes, memory, wall time) to stderr (default: on)") do |value|
|
|
770
|
-
options[:stats] = value
|
|
771
|
-
end
|
|
772
|
-
opts.on("--workers=N", Integer,
|
|
773
|
-
"Dispatch per-file analysis across N Ractor workers (default: 0; sequential)") do |value|
|
|
774
|
-
options[:workers] = value
|
|
775
|
-
end
|
|
776
|
-
Options.add_editor_mode(opts, options)
|
|
777
|
-
opts.on("--baseline=PATH",
|
|
778
|
-
"load baseline from PATH (overrides .rigor.yml `baseline:`)") do |value|
|
|
779
|
-
options[:baseline] = value
|
|
780
|
-
end
|
|
781
|
-
opts.on("--no-baseline",
|
|
782
|
-
"ignore any configured baseline for this run") do
|
|
783
|
-
options[:baseline] = false
|
|
784
|
-
end
|
|
785
|
-
opts.on("--baseline-strict",
|
|
786
|
-
"fail the run on any baseline drift (CI gate)") do
|
|
787
|
-
options[:baseline_strict] = true
|
|
788
|
-
end
|
|
789
|
-
opts.on("--fail-on=SEVERITY",
|
|
790
|
-
"exit non-zero on a diagnostic at or above SEVERITY: error (default), warning, or info") do |value|
|
|
791
|
-
# Validated post-parse (`valid_fail_on_option?`, called from `run`) rather than here — see its
|
|
792
|
-
# comment for why an unrecognised value is not raised from inside this block.
|
|
793
|
-
options[:fail_on] = value.to_s.downcase.to_sym
|
|
794
|
-
end
|
|
795
|
-
opts.on("--treat-all-as-inline-rbs",
|
|
796
|
-
"force-load rigor-rbs-inline with require_magic_comment: false") do
|
|
797
|
-
options[:treat_all_as_inline_rbs] = true
|
|
798
|
-
end
|
|
799
|
-
opts.on("--verify-incremental",
|
|
800
|
-
"assert incremental analysis matches a full run, then exit") do
|
|
801
|
-
options[:verify_incremental] = true
|
|
802
|
-
end
|
|
803
|
-
opts.on("--verify-incremental-edits=N", Integer,
|
|
804
|
-
"with --verify-incremental, also replay 2 edits (empty, class/module skeleton) on N files") do |value|
|
|
805
|
-
if value.negative?
|
|
806
|
-
raise OptionParser::InvalidArgument, "--verify-incremental-edits=#{value} (must be 0 or more)"
|
|
807
|
-
end
|
|
808
|
-
|
|
809
|
-
options[:verify_incremental] = true
|
|
810
|
-
options[:verify_incremental_edits] = value
|
|
811
|
-
end
|
|
812
|
-
opts.on("--incremental",
|
|
813
|
-
"re-analyze only files changed since the last run (cross-process cache)") do
|
|
814
|
-
options[:incremental] = true
|
|
815
|
-
end
|
|
816
|
-
opts.on("--no-ci-detect",
|
|
817
|
-
"do not auto-emit CI-native output when a CI environment is detected") do
|
|
818
|
-
options[:ci_detect] = false
|
|
819
|
-
end
|
|
820
|
-
# ADR-50 § WD2 — `=[LIST]` (not ` [LIST]`) so a bare `--bleeding-edge` never swallows a following positional
|
|
821
|
-
# path: `rigor check --bleeding-edge lib` adopts the whole overlay and checks `lib`.
|
|
822
|
-
opts.on("--bleeding-edge=[LIST]",
|
|
823
|
-
"adopt the bleeding-edge overlay for this run " \
|
|
824
|
-
"(all features, or a comma-separated feature-id list)") do |value|
|
|
825
|
-
options[:bleeding_edge] = value.nil? || value.split(",").map(&:strip).reject(&:empty?)
|
|
826
|
-
end
|
|
827
|
-
opts.on("--no-bleeding-edge",
|
|
828
|
-
"ignore any configured bleeding_edge: selection for this run") do
|
|
829
|
-
options[:bleeding_edge] = false
|
|
830
|
-
end
|
|
831
|
-
opts.on("--no-tolerated-effects",
|
|
832
|
-
"check effect envelopes as if effects.tolerated: were empty") do
|
|
833
|
-
options[:no_tolerated_effects] = true
|
|
834
|
-
end
|
|
835
|
-
end
|
|
836
|
-
parser.parse!(@argv)
|
|
841
|
+
option_parser(options).parse!(@argv)
|
|
837
842
|
options
|
|
838
843
|
end
|
|
839
844
|
|
|
@@ -861,7 +866,8 @@ module Rigor
|
|
|
861
866
|
return Configuration.load(options.fetch(:config)) unless options.fetch(:treat_all_as_inline_rbs)
|
|
862
867
|
|
|
863
868
|
path = options.fetch(:config) || Configuration.discover
|
|
864
|
-
|
|
869
|
+
config_file = path && File.exist?(path) ? path : nil
|
|
870
|
+
data = config_file ? Configuration.load_with_includes(config_file) : {}
|
|
865
871
|
# ADR-103 WD15 — captured before `DEFAULTS.merge` below folds "no `effects:` key" and "`effects:
|
|
866
872
|
# false`" into the same value; see `Configuration.load`'s identical capture.
|
|
867
873
|
effects_key_present = data.key?("effects")
|
|
@@ -869,7 +875,8 @@ module Rigor
|
|
|
869
875
|
data = data.dup
|
|
870
876
|
data["plugins"] = inject_treat_all_as_inline_rbs(Array(data["plugins"]))
|
|
871
877
|
Configuration.new(Configuration::DEFAULTS.merge(data), effects_key_present, target_ruby_key_present)
|
|
872
|
-
.with_exclude_base(
|
|
878
|
+
.with_exclude_base(config_file && File.dirname(config_file))
|
|
879
|
+
.with_config_path(config_file)
|
|
873
880
|
end
|
|
874
881
|
|
|
875
882
|
# ADR-50 § WD2 — applies the `--bleeding-edge[=ids]` / `--no-bleeding-edge` CLI selection over the configured
|
|
@@ -943,6 +950,7 @@ module Rigor
|
|
|
943
950
|
@err.puts(" recursion-unroll-fuel hits: #{counts[Inference::BudgetTrace::RECURSION_UNROLL_FUEL]}")
|
|
944
951
|
@err.puts(" recursion-fixpoint-cap hits: #{counts[Inference::BudgetTrace::RECURSION_FIXPOINT_CAP]}")
|
|
945
952
|
@err.puts(" block-writeback-cap hits: #{counts[Inference::BudgetTrace::BLOCK_WRITEBACK_CAP]}")
|
|
953
|
+
@err.puts(" callee-chain-cap hits: #{counts[Inference::BudgetTrace::CALLEE_CHAIN_CAP]}")
|
|
946
954
|
write_budget_distributions
|
|
947
955
|
write_memo_profile(counts)
|
|
948
956
|
end
|
|
@@ -1153,7 +1161,7 @@ module Rigor
|
|
|
1153
1161
|
payload["fail_on"] = fail_on.to_s
|
|
1154
1162
|
payload["coverage"] = coverage_payload(coverage) if coverage
|
|
1155
1163
|
payload["config_warnings"] = config_warnings.map(&:to_h) unless config_warnings.empty?
|
|
1156
|
-
@out.puts(JSON.pretty_generate(payload))
|
|
1164
|
+
@out.puts(JSON.pretty_generate(JsonDocument.stamp(payload)))
|
|
1157
1165
|
when "text"
|
|
1158
1166
|
write_text_result(result)
|
|
1159
1167
|
write_coverage_summary(coverage) if coverage
|
|
@@ -7,8 +7,8 @@ module Rigor
|
|
|
7
7
|
class CLI
|
|
8
8
|
# Shared factory for building an {Analysis::Runner} from CLI option hashes + a loaded {Configuration}.
|
|
9
9
|
#
|
|
10
|
-
# Extracted from {CheckCommand} so that `rigor doctor`
|
|
11
|
-
# runner
|
|
10
|
+
# Extracted from {CheckCommand} so that other commands (`rigor doctor`, `rigor skill describe --deep`) reuse the
|
|
11
|
+
# same runner setup (cache, workers, buffer, explain) instead of diverging from the `rigor check` path (ADR-77 WD3).
|
|
12
12
|
module CheckRunnerFactory
|
|
13
13
|
module_function
|
|
14
14
|
|
data/lib/rigor/cli/command.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../rooted_glob"
|
|
4
|
+
require_relative "../analysis/path_expansion"
|
|
4
5
|
|
|
5
6
|
module Rigor
|
|
6
7
|
class CLI
|
|
@@ -11,7 +12,7 @@ module Rigor
|
|
|
11
12
|
# `$stderr`, most not at all). Centralising it here gives one consistent shape and lets a test instantiate a command
|
|
12
13
|
# with just `argv:` (the streams default so a spec can pass a `StringIO` for one and ignore the other).
|
|
13
14
|
#
|
|
14
|
-
# Subclasses read the `@argv` / `@out` / `@err` ivars directly
|
|
15
|
+
# Subclasses read the `@argv` / `@out` / `@err` ivars directly.
|
|
15
16
|
class Command
|
|
16
17
|
def initialize(argv:, out: $stdout, err: $stderr)
|
|
17
18
|
@argv = argv
|
|
@@ -38,6 +39,19 @@ module Rigor
|
|
|
38
39
|
end
|
|
39
40
|
paths.uniq
|
|
40
41
|
end
|
|
42
|
+
|
|
43
|
+
# #2206 — the `.rb` paths `args` names, with a path that is neither a directory nor a `.rb` file reported the way
|
|
44
|
+
# `rigor check` reports it ({Analysis::PathExpansion.diagnostics}): a warning beside files that will be read, an
|
|
45
|
+
# error when nothing will be. The diagnostics go to stderr. Returns nil when one is an error, so the caller exits.
|
|
46
|
+
def collect_paths_reporting(args)
|
|
47
|
+
bad = Analysis::PathExpansion.unusable(args)
|
|
48
|
+
files = Analysis::PathExpansion.ruby_files(args, [])
|
|
49
|
+
diagnostics = Analysis::PathExpansion.diagnostics(bad, any_files: files.any?)
|
|
50
|
+
diagnostics.each { |diagnostic| @err.puts(diagnostic.to_s) }
|
|
51
|
+
return nil if diagnostics.any?(&:error?)
|
|
52
|
+
|
|
53
|
+
files.uniq
|
|
54
|
+
end
|
|
41
55
|
end
|
|
42
56
|
end
|
|
43
57
|
end
|