rigortype 0.4.6 → 0.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/data/gem_overlay/activesupport/core_ext.rbs +16 -13
- data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
- data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
- data/docs/handbook/01-getting-started.md +5 -2
- data/docs/handbook/11-sig-gen.md +50 -3
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/manual/02-cli-reference.md +152 -26
- data/docs/manual/03-configuration.md +36 -3
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/09-editor-integration.md +13 -2
- data/docs/manual/10-mcp-server.md +38 -2
- data/docs/manual/12-caching.md +4 -3
- data/docs/manual/plugins/rigor-actionpack.md +19 -11
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +20 -1
- data/lib/rigor/analysis/baseline.rb +24 -27
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +4 -4
- data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +85 -13
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +3 -2
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +0 -1
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +1 -1
- data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +3 -3
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
- data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules.rb +124 -44
- data/lib/rigor/analysis/crash_signature.rb +2 -2
- data/lib/rigor/analysis/dependency_recorder.rb +4 -4
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/edit_replay.rb +93 -6
- data/lib/rigor/analysis/fact_store.rb +3 -3
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +2 -2
- data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
- data/lib/rigor/analysis/incremental_session.rb +27 -16
- data/lib/rigor/analysis/rule_catalog.rb +2 -2
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +2 -2
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -1
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +193 -93
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +1 -1
- data/lib/rigor/analysis/runner/pool_coordinator.rb +147 -323
- data/lib/rigor/analysis/runner/project_pre_passes.rb +3 -3
- data/lib/rigor/analysis/runner/run_level_facts.rb +304 -0
- data/lib/rigor/analysis/runner/run_snapshots.rb +44 -54
- data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
- data/lib/rigor/analysis/runner.rb +77 -71
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +1 -1
- data/lib/rigor/analysis/template_unit_positions.rb +2 -3
- data/lib/rigor/analysis/template_units.rb +9 -1
- data/lib/rigor/analysis/worker_session.rb +24 -31
- data/lib/rigor/bleeding_edge.rb +3 -6
- data/lib/rigor/builtins/imported_refinements.rb +6 -8
- data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
- data/lib/rigor/builtins/regex_refinement.rb +4 -6
- data/lib/rigor/cache/descriptor.rb +6 -5
- data/lib/rigor/cache/incremental_snapshot.rb +31 -12
- data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
- data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
- data/lib/rigor/cache/rbs_constant_table.rb +1 -1
- data/lib/rigor/cache/rbs_descriptor.rb +54 -3
- data/lib/rigor/cache/rbs_environment.rb +4 -1
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
- data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
- data/lib/rigor/cache/store.rb +11 -10
- data/lib/rigor/ci_detector.rb +1 -1
- data/lib/rigor/cli/annotate_command.rb +24 -19
- data/lib/rigor/cli/baseline_command.rb +145 -55
- data/lib/rigor/cli/catalogue.rb +256 -0
- data/lib/rigor/cli/check_command.rb +108 -102
- data/lib/rigor/cli/check_runner_factory.rb +2 -2
- data/lib/rigor/cli/command.rb +15 -1
- data/lib/rigor/cli/coverage_command.rb +21 -8
- data/lib/rigor/cli/coverage_mutation.rb +5 -7
- data/lib/rigor/cli/coverage_renderer.rb +2 -1
- data/lib/rigor/cli/coverage_scan.rb +3 -5
- data/lib/rigor/cli/diff_command.rb +25 -20
- data/lib/rigor/cli/docs_command.rb +1 -2
- data/lib/rigor/cli/doctor_command.rb +12 -12
- data/lib/rigor/cli/effects_command.rb +50 -40
- data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
- data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
- data/lib/rigor/cli/effects_renderer.rb +2 -1
- data/lib/rigor/cli/effects_snapshot_command.rb +18 -17
- data/lib/rigor/cli/explain_command.rb +18 -8
- data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
- data/lib/rigor/cli/json_document.rb +41 -0
- data/lib/rigor/cli/lens_command.rb +12 -8
- data/lib/rigor/cli/lsp_command.rb +250 -23
- data/lib/rigor/cli/mcp_command.rb +12 -7
- data/lib/rigor/cli/mutation_fork_scan.rb +1 -1
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -2
- data/lib/rigor/cli/options.rb +40 -1
- data/lib/rigor/cli/plugin_command.rb +93 -46
- data/lib/rigor/cli/plugins_command.rb +17 -18
- data/lib/rigor/cli/plugins_renderer.rb +10 -6
- data/lib/rigor/cli/protection_fork_scan.rb +2 -2
- data/lib/rigor/cli/protection_renderer.rb +2 -1
- data/lib/rigor/cli/show_bleedingedge_command.rb +21 -15
- data/lib/rigor/cli/sig_gen_command.rb +84 -27
- data/lib/rigor/cli/skill_command.rb +32 -3
- data/lib/rigor/cli/skill_deep_probe.rb +1 -1
- data/lib/rigor/cli/skill_describe.rb +1 -1
- data/lib/rigor/cli/trace_command.rb +14 -9
- data/lib/rigor/cli/triage_command.rb +10 -11
- data/lib/rigor/cli/triage_renderer.rb +2 -1
- data/lib/rigor/cli/type_of_command.rb +16 -11
- data/lib/rigor/cli/type_of_project_scope.rb +1 -4
- data/lib/rigor/cli/type_of_renderer.rb +4 -1
- data/lib/rigor/cli/type_scan_command.rb +14 -9
- data/lib/rigor/cli/type_scan_renderer.rb +2 -1
- data/lib/rigor/cli/unused_command.rb +27 -17
- data/lib/rigor/cli/upgrade_command.rb +5 -0
- data/lib/rigor/cli.rb +209 -54
- data/lib/rigor/config_audit.rb +1 -1
- data/lib/rigor/configuration/severity_profile.rb +5 -5
- data/lib/rigor/configuration.rb +72 -30
- data/lib/rigor/effects/definition_context.rb +0 -2
- data/lib/rigor/effects/envelope.rb +1 -1
- data/lib/rigor/effects/framework_units.rb +1 -1
- data/lib/rigor/effects/mutation_classifier.rb +4 -7
- data/lib/rigor/effects/propagator.rb +1 -1
- data/lib/rigor/effects/registry.rb +2 -2
- data/lib/rigor/effects/snapshot.rb +1 -1
- data/lib/rigor/effects/taint_cause.rb +0 -2
- data/lib/rigor/effects/unit_scan.rb +9 -13
- data/lib/rigor/environment/bundle_sig_discovery.rb +5 -9
- data/lib/rigor/environment/class_registry.rb +3 -3
- data/lib/rigor/environment/core_root.rb +77 -0
- data/lib/rigor/environment/default_libraries.rb +1 -1
- data/lib/rigor/environment/deferred_signature_plan.rb +283 -0
- data/lib/rigor/environment/gated_signature_guard.rb +1 -1
- data/lib/rigor/environment/gated_signature_plan.rb +5 -2
- data/lib/rigor/environment/lockfile_resolver.rb +28 -12
- data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
- data/lib/rigor/environment/member_consistency.rb +56 -7
- data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
- data/lib/rigor/environment/project_path_gems.rb +124 -0
- data/lib/rigor/environment/rbs_hierarchy.rb +6 -15
- data/lib/rigor/environment/rbs_loader.rb +480 -67
- data/lib/rigor/environment/reflection.rb +7 -7
- data/lib/rigor/environment/required_features.rb +0 -3
- data/lib/rigor/environment/ruby_library_roots.rb +1 -1
- data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
- data/lib/rigor/environment.rb +41 -10
- data/lib/rigor/flow_contribution/merger.rb +4 -4
- data/lib/rigor/inference/acceptance.rb +93 -32
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/block_run_count.rb +178 -0
- data/lib/rigor/inference/budget_trace.rb +8 -2
- data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
- data/lib/rigor/inference/callee_mutation_chain.rb +88 -77
- data/lib/rigor/inference/callee_write_census.rb +815 -0
- data/lib/rigor/inference/construction_escape.rb +217 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
- data/lib/rigor/inference/deep_erasure.rb +39 -7
- data/lib/rigor/inference/def_handle.rb +1 -1
- data/lib/rigor/inference/def_node_resolver.rb +1 -1
- data/lib/rigor/inference/def_return_typer.rb +46 -4
- data/lib/rigor/inference/definer_resolution.rb +4 -2
- data/lib/rigor/inference/definers_index.rb +106 -0
- data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
- data/lib/rigor/inference/element_read_widening.rb +49 -3
- data/lib/rigor/inference/expression_typer.rb +116 -49
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/global_write_census.rb +76 -2
- data/lib/rigor/inference/global_writer_reach.rb +260 -0
- data/lib/rigor/inference/hkt_body.rb +1 -2
- data/lib/rigor/inference/hkt_body_parser.rb +3 -5
- data/lib/rigor/inference/hkt_reducer.rb +2 -2
- data/lib/rigor/inference/hkt_registry.rb +3 -5
- data/lib/rigor/inference/indexed_narrowing.rb +4 -7
- data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
- data/lib/rigor/inference/ivar_writer_census.rb +51 -0
- data/lib/rigor/inference/key_presence_guard.rb +2 -1
- data/lib/rigor/inference/loop_value_record.rb +8 -4
- data/lib/rigor/inference/match_rebinding/frame.rb +46 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +45 -0
- data/lib/rigor/inference/match_rebinding.rb +11 -1
- data/lib/rigor/inference/memo_audit.rb +55 -0
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +3 -3
- data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +39 -27
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +203 -17
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +9 -7
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +83 -20
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +64 -14
- data/lib/rigor/inference/module_function_state.rb +11 -1
- data/lib/rigor/inference/multi_target_binder.rb +1 -1
- data/lib/rigor/inference/mutation_rejoin.rb +240 -4
- data/lib/rigor/inference/mutation_widening.rb +157 -27
- data/lib/rigor/inference/narrowing.rb +47 -33
- data/lib/rigor/inference/optimistic_origin.rb +1 -1
- data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +1 -1
- data/lib/rigor/inference/project_method_ownership.rb +6 -3
- data/lib/rigor/inference/project_yielder.rb +11 -17
- data/lib/rigor/inference/receiver_alias.rb +20 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +3 -1
- data/lib/rigor/inference/refined_dispatch/own_method.rb +2 -1
- data/lib/rigor/inference/remover_holder_scan.rb +233 -0
- data/lib/rigor/inference/remover_narrowing.rb +224 -0
- data/lib/rigor/inference/rewrite_mutation.rb +53 -2
- data/lib/rigor/inference/scope_indexer/certainty.rb +0 -1
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +58 -15
- data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
- data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
- data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +135 -0
- data/lib/rigor/inference/scope_indexer.rb +137 -46
- data/lib/rigor/inference/self_call_ivar_writes.rb +84 -12
- data/lib/rigor/inference/set_mutation.rb +189 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +1 -2
- data/lib/rigor/inference/statement_evaluator.rb +2328 -138
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_scanner.rb +5 -6
- data/lib/rigor/inference/toplevel_def_census.rb +59 -0
- data/lib/rigor/inference/unknown_store_widening.rb +103 -4
- data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -1
- data/lib/rigor/language_server/buffer_table.rb +3 -3
- data/lib/rigor/language_server/completion_provider.rb +26 -20
- data/lib/rigor/language_server/config_warning_announcer.rb +28 -0
- data/lib/rigor/language_server/diagnostic_publisher.rb +8 -3
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -2
- data/lib/rigor/language_server/hover_provider.rb +1 -7
- data/lib/rigor/language_server/hover_renderer.rb +6 -8
- data/lib/rigor/language_server/project_context.rb +39 -1
- data/lib/rigor/language_server/publish_batcher.rb +3 -4
- data/lib/rigor/language_server/selection_range_provider.rb +2 -4
- data/lib/rigor/language_server/server.rb +7 -3
- data/lib/rigor/language_server/signature_help_provider.rb +4 -4
- data/lib/rigor/lens/plugins.rb +1 -1
- data/lib/rigor/mcp/server.rb +73 -2
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- data/lib/rigor/plugin/bundled_catalog.rb +1 -12
- data/lib/rigor/plugin/io_boundary.rb +1 -1
- data/lib/rigor/plugin/loader.rb +25 -8
- data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
- data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
- data/lib/rigor/plugin/manifest.rb +9 -13
- data/lib/rigor/plugin/registry.rb +3 -3
- data/lib/rigor/plugin/services.rb +2 -3
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
- data/lib/rigor/plugin/trust_policy.rb +2 -2
- data/lib/rigor/plugin/type_node_resolver.rb +2 -4
- data/lib/rigor/plugin.rb +6 -8
- data/lib/rigor/plugin_gap_advisory.rb +5 -1
- data/lib/rigor/project_environment.rb +16 -2
- data/lib/rigor/protection/analysis_guard.rb +1 -1
- data/lib/rigor/protection/discovery_seed.rb +44 -5
- data/lib/rigor/protection/mutator.rb +1 -1
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
- data/lib/rigor/rbs_extended/reporter.rb +27 -3
- data/lib/rigor/rbs_extended.rb +2 -3
- data/lib/rigor/reflection/constant_ancestors.rb +5 -3
- data/lib/rigor/rooted_glob.rb +15 -1
- data/lib/rigor/run_scoped_slots.rb +38 -0
- data/lib/rigor/runtime/jit.rb +1 -1
- data/lib/rigor/scope/discovery_index.rb +19 -6
- data/lib/rigor/scope/resolution_chain.rb +3 -1
- data/lib/rigor/scope.rb +365 -31
- data/lib/rigor/sig_gen/classification.rb +4 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
- data/lib/rigor/sig_gen/generator.rb +371 -58
- data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
- data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
- data/lib/rigor/sig_gen/layout_index.rb +3 -3
- data/lib/rigor/sig_gen/method_candidate.rb +23 -4
- data/lib/rigor/sig_gen/observation_collector.rb +3 -5
- data/lib/rigor/sig_gen/observed_call.rb +1 -1
- data/lib/rigor/sig_gen/path_mapper.rb +6 -0
- data/lib/rigor/sig_gen/renderer.rb +15 -9
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +15 -2
- data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
- data/lib/rigor/sig_gen/write_reporter.rb +63 -14
- data/lib/rigor/sig_gen/write_result.rb +23 -12
- data/lib/rigor/sig_gen/writer.rb +217 -37
- data/lib/rigor/type/acceptance_router.rb +2 -2
- data/lib/rigor/type/accepts_result.rb +1 -1
- data/lib/rigor/type/app.rb +4 -6
- data/lib/rigor/type/combinator.rb +9 -13
- data/lib/rigor/type/constant.rb +4 -5
- data/lib/rigor/type/hash_shape.rb +21 -40
- data/lib/rigor/type/union.rb +1 -1
- data/lib/rigor/type_node/identifier.rb +1 -2
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node/string_literal.rb +1 -2
- data/lib/rigor/type_node/symbol_literal.rb +1 -2
- data/lib/rigor/type_node/union.rb +3 -6
- data/lib/rigor/value_semantics.rb +1 -10
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +38 -36
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +66 -19
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +2 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +0 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +5 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +7 -12
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +19 -6
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +26 -13
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +1 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +6 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +5 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +1 -1
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +4 -5
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -2
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +1 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -2
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +3 -4
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +6 -8
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +3 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +1 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +17 -21
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +3 -2
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
- data/sig/rigor/analysis/baseline.rbs +0 -3
- data/sig/rigor/analysis/fact_store.rbs +1 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
- data/sig/rigor/cli/diff_command.rbs +1 -1
- data/sig/rigor/cli/explain_command.rbs +1 -1
- data/sig/rigor/effects/registry.rbs +2 -2
- data/sig/rigor/inference.rbs +4 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +1 -0
- data/sig/rigor/plugin/effect_attribution.rbs +3 -3
- data/sig/rigor/plugin/effect_edge.rbs +1 -1
- data/sig/rigor/plugin/effect_entry_points.rbs +3 -3
- data/sig/rigor/plugin/io_boundary.rbs +2 -1
- data/sig/rigor/plugin/manifest.rbs +9 -9
- data/sig/rigor/plugin/protocol_contract.rbs +3 -7
- data/sig/rigor/scope.rbs +23 -3
- data/skills/rigor-plugin-author/SKILL.md +4 -4
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
- data/skills/rigor-plugin-review/SKILL.md +3 -3
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +12 -1
- metadata +36 -3
|
@@ -4,6 +4,7 @@ require_relative "command"
|
|
|
4
4
|
require_relative "options"
|
|
5
5
|
require_relative "../runtime/jit"
|
|
6
6
|
|
|
7
|
+
require "json"
|
|
7
8
|
require "optionparser"
|
|
8
9
|
|
|
9
10
|
module Rigor
|
|
@@ -15,6 +16,179 @@ module Rigor
|
|
|
15
16
|
class LspCommand < Command
|
|
16
17
|
USAGE = "Usage: rigor lsp [options]"
|
|
17
18
|
|
|
19
|
+
# Records each JSON-RPC message one per line, prefixed with the direction (`<-` read from the client, `->`
|
|
20
|
+
# written to it) and a timestamp. Only the message framing is logged; the server's own debug output joins the
|
|
21
|
+
# same file through `$stderr` (see {#with_debug_output}).
|
|
22
|
+
#
|
|
23
|
+
# It never raises into the protocol path. A message the JSON encoder rejects (raw invalid UTF-8 in a string) is
|
|
24
|
+
# logged as a scrubbed rendering, so its line is still written. Any IO failure disables the log for the rest of
|
|
25
|
+
# the session: records are dropped, and debug output falls back to `fallback` (the real stderr). Nothing is
|
|
26
|
+
# `warn`ed into the failing log.
|
|
27
|
+
class WireLog
|
|
28
|
+
def initialize(io, fallback)
|
|
29
|
+
@io = io
|
|
30
|
+
@fallback = fallback
|
|
31
|
+
@broken = false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def record(direction, message)
|
|
35
|
+
deliver("#{stamp} #{direction} #{render(message)}\n")
|
|
36
|
+
nil
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Debug output written to `$stderr` while the session runs (`Kernel#warn` calls `write`). The parts are joined
|
|
40
|
+
# as binary, so an odd encoding in one part cannot raise here.
|
|
41
|
+
def write(*parts)
|
|
42
|
+
text = parts.map { |part| part.to_s.b }.join
|
|
43
|
+
@fallback.write(text) unless deliver(text)
|
|
44
|
+
text.bytesize
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def print(*parts)
|
|
48
|
+
write(*parts)
|
|
49
|
+
nil
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def puts(*args)
|
|
53
|
+
lines = if args.empty?
|
|
54
|
+
["\n"]
|
|
55
|
+
else
|
|
56
|
+
args.flatten.map { |arg| arg.to_s.end_with?("\n") ? arg.to_s : "#{arg}\n" }
|
|
57
|
+
end
|
|
58
|
+
write(*lines)
|
|
59
|
+
nil
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def printf(format_string, *)
|
|
63
|
+
write(format(format_string, *))
|
|
64
|
+
nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def putc(char)
|
|
68
|
+
write(char.is_a?(Integer) ? char.chr : char.to_s[0])
|
|
69
|
+
char
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def syswrite(text)
|
|
73
|
+
write(text)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def <<(part)
|
|
77
|
+
write(part)
|
|
78
|
+
self
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def flush
|
|
82
|
+
self
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# The exception that escaped the server loop, with its backtrace, so a crash is in the log, not only on stderr.
|
|
86
|
+
def crash(error)
|
|
87
|
+
lines = ["#{stamp} ! #{error.class}: #{error.message}\n"]
|
|
88
|
+
lines.concat((error.backtrace || []).map { |frame| " #{frame}\n" })
|
|
89
|
+
text = lines.join
|
|
90
|
+
@fallback.write(text) unless deliver(text)
|
|
91
|
+
nil
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def close
|
|
95
|
+
@io.close unless @io.closed?
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Every other IO method (`tty?`, `closed?`, `fileno`, `sync=`, `to_io` for `system(err: $stderr)`, ...) goes to
|
|
99
|
+
# the current sink: the file while the log works, the real stderr once it has failed.
|
|
100
|
+
def method_missing(name, ...)
|
|
101
|
+
return super unless sink.respond_to?(name)
|
|
102
|
+
|
|
103
|
+
sink.public_send(name, ...)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def respond_to_missing?(name, include_private = false)
|
|
107
|
+
sink.respond_to?(name) || super
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private
|
|
111
|
+
|
|
112
|
+
def sink
|
|
113
|
+
@broken ? @fallback : @io
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Writes `text` to the file. Returns false when the log is (or just became) unusable. Only a failure of the
|
|
117
|
+
# write itself disables the log.
|
|
118
|
+
def deliver(text)
|
|
119
|
+
return false if @broken
|
|
120
|
+
|
|
121
|
+
@io.write(text)
|
|
122
|
+
true
|
|
123
|
+
rescue StandardError
|
|
124
|
+
@broken = true
|
|
125
|
+
false
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def stamp
|
|
129
|
+
Time.now.strftime("%FT%T.%6N%:z")
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# The JSON line for `message`. One the encoder rejects (invalid UTF-8, nesting past its limit, a `to_json` that
|
|
133
|
+
# raises) is rendered from its scrubbed copy, or from its `inspect` when even that does not encode.
|
|
134
|
+
def render(message)
|
|
135
|
+
JSON.generate(message)
|
|
136
|
+
rescue StandardError
|
|
137
|
+
begin
|
|
138
|
+
JSON.generate(scrub(message))
|
|
139
|
+
rescue StandardError
|
|
140
|
+
JSON.generate(message.inspect)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def scrub(value)
|
|
145
|
+
case value
|
|
146
|
+
when Hash then value.to_h { |key, val| [scrub(key), scrub(val)] }
|
|
147
|
+
when Array then value.map { |item| scrub(item) }
|
|
148
|
+
when Symbol then scrub(value.to_s).to_sym
|
|
149
|
+
when String then value.dup.force_encoding(Encoding::UTF_8).scrub
|
|
150
|
+
else value
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Wraps the framed reader so every request or notification it yields is logged before the Loop dispatches it.
|
|
156
|
+
class WireLogReader
|
|
157
|
+
def initialize(inner, log)
|
|
158
|
+
@inner = inner
|
|
159
|
+
@log = log
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def read(&block)
|
|
163
|
+
@inner.read do |message|
|
|
164
|
+
@log.record("<-", message)
|
|
165
|
+
block.call(message)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def close
|
|
170
|
+
@inner.close
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Wraps the framed writer so every outgoing message is logged as it goes on the wire. It sits inside
|
|
175
|
+
# `SynchronizedWriter`, so the log line and the frame it describes are written under the same lock.
|
|
176
|
+
class WireLogWriter
|
|
177
|
+
def initialize(inner, log)
|
|
178
|
+
@inner = inner
|
|
179
|
+
@log = log
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def write(message)
|
|
183
|
+
@log.record("->", message.merge(jsonrpc: "2.0"))
|
|
184
|
+
@inner.write(message)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def close
|
|
188
|
+
@inner.close
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
18
192
|
# @return CLI exit status.
|
|
19
193
|
def run
|
|
20
194
|
options = parse_options
|
|
@@ -26,6 +200,9 @@ module Rigor
|
|
|
26
200
|
return CLI::EXIT_USAGE
|
|
27
201
|
end
|
|
28
202
|
|
|
203
|
+
log = open_wire_log(options.fetch(:log))
|
|
204
|
+
return CLI::EXIT_USAGE if log == :usage_error
|
|
205
|
+
|
|
29
206
|
# A long-lived server always outlasts the JIT compile cost, so enable
|
|
30
207
|
# YJIT at boot rather than on the analysis deadline (Runtime::Jit).
|
|
31
208
|
Runtime::Jit.enable_now
|
|
@@ -34,23 +211,83 @@ module Rigor
|
|
|
34
211
|
require_relative "../configuration"
|
|
35
212
|
require "language_server-protocol"
|
|
36
213
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
214
|
+
begin
|
|
215
|
+
serve(config_path: options.fetch(:config), log: log)
|
|
216
|
+
ensure
|
|
217
|
+
log&.close
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# The parser this command parses with, built without parsing anything: the catalogue reads the
|
|
222
|
+
# flags and formats from it (ADR-126 WD3). `options` is only written by the switches' blocks.
|
|
223
|
+
def option_parser(options = {})
|
|
224
|
+
OptionParser.new do |opts|
|
|
225
|
+
opts.banner = USAGE
|
|
226
|
+
opts.on("--transport=NAME", "Transport (default: stdio; only stdio supported in v1)") do |value|
|
|
227
|
+
options[:transport] = value
|
|
228
|
+
end
|
|
229
|
+
opts.on("--log=PATH",
|
|
230
|
+
"Append the LSP wire log and server debug output to PATH " \
|
|
231
|
+
"(default: no wire log; debug output goes to stderr)") do |value|
|
|
232
|
+
options[:log] = value
|
|
233
|
+
end
|
|
234
|
+
Options.add_config(opts, options)
|
|
235
|
+
end
|
|
47
236
|
end
|
|
48
237
|
|
|
49
238
|
private
|
|
50
239
|
|
|
240
|
+
# STDIN is read frame-by-frame via the gem's `Io::Reader`; STDOUT is wrapped in `SynchronizedWriter` so
|
|
241
|
+
# concurrent writes from the main dispatch thread + the Debouncer's async threads don't interleave frames. The
|
|
242
|
+
# Loop runs until either STDIN hits EOF or `server.exited?`; the process then exits with the server's recorded
|
|
243
|
+
# code (0 after a clean shutdown+exit, 1 otherwise). With a wire log, both directions are logged inside the lock.
|
|
244
|
+
def serve(config_path:, log:)
|
|
245
|
+
reader = ::LanguageServer::Protocol::Transport::Io::Reader.new($stdin)
|
|
246
|
+
frame_writer = ::LanguageServer::Protocol::Transport::Io::Writer.new($stdout)
|
|
247
|
+
if log
|
|
248
|
+
reader = WireLogReader.new(reader, log)
|
|
249
|
+
frame_writer = WireLogWriter.new(frame_writer, log)
|
|
250
|
+
end
|
|
251
|
+
writer = LanguageServer::SynchronizedWriter.new(frame_writer)
|
|
252
|
+
server, loop_runner = build_server(writer: writer, reader: reader, config_path: config_path)
|
|
253
|
+
with_debug_output(log) { loop_runner.run }
|
|
254
|
+
server.exit_code || 0
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Opens PATH for appending with synchronous writes, so a crash loses no logged line. Returns nil without a
|
|
258
|
+
# PATH, the open file, or `:usage_error` after reporting why it could not be opened.
|
|
259
|
+
def open_wire_log(path)
|
|
260
|
+
return nil if path.nil?
|
|
261
|
+
|
|
262
|
+
io = File.new(path, "a")
|
|
263
|
+
io.sync = true
|
|
264
|
+
WireLog.new(io, $stderr)
|
|
265
|
+
rescue SystemCallError => e
|
|
266
|
+
@err.puts("rigor lsp: cannot open --log file #{path.inspect}: #{e.message}")
|
|
267
|
+
:usage_error
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# Routes the server's debug output (`warn` and the Ruby runtime's warnings, both written to `$stderr`) into the
|
|
271
|
+
# wire log for the length of the session. An exception that escapes the loop is written to the log before
|
|
272
|
+
# `$stderr` is restored and the exception is re-raised. Without a log, `$stderr` is left alone.
|
|
273
|
+
def with_debug_output(log)
|
|
274
|
+
return yield if log.nil?
|
|
275
|
+
|
|
276
|
+
saved = $stderr
|
|
277
|
+
begin
|
|
278
|
+
$stderr = log
|
|
279
|
+
yield
|
|
280
|
+
rescue StandardError, ScriptError, SystemStackError => e
|
|
281
|
+
log.crash(e)
|
|
282
|
+
raise
|
|
283
|
+
ensure
|
|
284
|
+
$stderr = saved
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
51
288
|
# Builds the full collaborator graph from a fresh `Configuration` + `ProjectContext`. Returns `[server, loop]` so
|
|
52
289
|
# the caller drives the loop and reads `server.exit_code` for the process exit status.
|
|
53
|
-
def build_server(writer:, config_path:) # rubocop:disable Metrics/MethodLength
|
|
290
|
+
def build_server(writer:, reader:, config_path:) # rubocop:disable Metrics/MethodLength
|
|
54
291
|
configuration = Configuration.load(config_path)
|
|
55
292
|
# ProjectContext caches Environment + Cache::Store across requests so hover / publish hit the warm path.
|
|
56
293
|
# Invalidated by `workspace/didChangeWatchedFiles` and `workspace/didChangeConfiguration`; every save round
|
|
@@ -89,7 +326,7 @@ module Rigor
|
|
|
89
326
|
project_context: project_context
|
|
90
327
|
)
|
|
91
328
|
loop_runner = LanguageServer::Loop.new(
|
|
92
|
-
reader:
|
|
329
|
+
reader: reader,
|
|
93
330
|
writer: writer,
|
|
94
331
|
server: server
|
|
95
332
|
)
|
|
@@ -99,17 +336,7 @@ module Rigor
|
|
|
99
336
|
def parse_options
|
|
100
337
|
options = { transport: "stdio", log: nil, config: nil }
|
|
101
338
|
|
|
102
|
-
|
|
103
|
-
opts.banner = USAGE
|
|
104
|
-
opts.on("--transport=NAME", "Transport (default: stdio; only stdio supported in v1)") do |value|
|
|
105
|
-
options[:transport] = value
|
|
106
|
-
end
|
|
107
|
-
opts.on("--log=PATH", "Write LSP wire log + server debug to PATH (default: stderr)") do |value|
|
|
108
|
-
options[:log] = value
|
|
109
|
-
end
|
|
110
|
-
Options.add_config(opts, options)
|
|
111
|
-
end
|
|
112
|
-
parser.parse!(@argv)
|
|
339
|
+
option_parser(options).parse!(@argv)
|
|
113
340
|
options
|
|
114
341
|
rescue OptionParser::ParseError => e
|
|
115
342
|
@err.puts(e.message)
|
|
@@ -42,12 +42,10 @@ module Rigor
|
|
|
42
42
|
0
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
parser = OptionParser.new do |opts|
|
|
45
|
+
# The parser this command parses with, built without parsing anything: the catalogue reads the
|
|
46
|
+
# flags and formats from it (ADR-126 WD3). `options` is only written by the switches' blocks.
|
|
47
|
+
def option_parser(options = {})
|
|
48
|
+
OptionParser.new do |opts|
|
|
51
49
|
opts.banner = USAGE
|
|
52
50
|
opts.on("--transport=NAME",
|
|
53
51
|
"Transport (default: stdio; only stdio is supported in v1)") do |value|
|
|
@@ -58,7 +56,14 @@ module Rigor
|
|
|
58
56
|
options[:config] = Options.config_path(value)
|
|
59
57
|
end
|
|
60
58
|
end
|
|
61
|
-
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def parse_options
|
|
64
|
+
options = { transport: "stdio", config: nil }
|
|
65
|
+
|
|
66
|
+
option_parser(options).parse!(@argv)
|
|
62
67
|
options
|
|
63
68
|
rescue OptionParser::ParseError => e
|
|
64
69
|
@err.puts(e.message)
|
|
@@ -40,7 +40,7 @@ module Rigor
|
|
|
40
40
|
# @return one entry per path.
|
|
41
41
|
def run(paths:, scanner:, environment:, configuration:, workers:)
|
|
42
42
|
# Force the full RBS load on the parent so children copy-on-write inherit a warm environment rather
|
|
43
|
-
# than each rebuilding it after the fork.
|
|
43
|
+
# than each rebuilding it after the fork. Skipped when the run is sequential.
|
|
44
44
|
environment.rbs_loader&.prewarm if Inference::ForkMap.parallel?([workers, paths.size].min)
|
|
45
45
|
|
|
46
46
|
payloads = Inference::ForkMap.call(items: paths, workers: workers) do |slice|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "json"
|
|
4
|
+
require_relative "json_document"
|
|
4
5
|
|
|
5
6
|
module Rigor
|
|
6
7
|
class CLI
|
|
@@ -22,7 +23,7 @@ module Rigor
|
|
|
22
23
|
private
|
|
23
24
|
|
|
24
25
|
def render_json(report)
|
|
25
|
-
@out.puts(JSON.pretty_generate(report.to_h))
|
|
26
|
+
@out.puts(JSON.pretty_generate(JsonDocument.stamp(report.to_h)))
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
def render_text(report)
|
|
@@ -46,7 +47,7 @@ module Rigor
|
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
# Issue #686 — the count that makes the command exit non-zero, on stdout beside the ratio it qualifies
|
|
49
|
-
# (the stderr twin in
|
|
50
|
+
# (the stderr twin in {MeasurementIntegrityWarning} carries the explanation).
|
|
50
51
|
def render_unmeasured_files(report)
|
|
51
52
|
count = report.unmeasured_files
|
|
52
53
|
return if count.zero?
|
data/lib/rigor/cli/options.rb
CHANGED
|
@@ -16,6 +16,13 @@ module Rigor
|
|
|
16
16
|
module Options
|
|
17
17
|
module_function
|
|
18
18
|
|
|
19
|
+
# The formats `parser` accepts for `--format`, or nil when {.add_format} did not define the switch. A
|
|
20
|
+
# block-validated switch carries no list of its own, so {.add_format} leaves it on the parser for the CLI
|
|
21
|
+
# catalogue (ADR-126 WD3) to read.
|
|
22
|
+
def accepted_formats(parser)
|
|
23
|
+
parser.instance_variable_get(:@rigor_accepted_formats)
|
|
24
|
+
end
|
|
25
|
+
|
|
19
26
|
# Defines the standard `--config=PATH` flag on `parser`, writing the path into `options[:config]`. Used by every
|
|
20
27
|
# subcommand that loads a `.rigor.yml`; the few whose `--config` help text is intentionally bespoke (`diff`,
|
|
21
28
|
# `mcp`, `show-bleedingedge`) keep their own `parser.on` and call {.config_path} for the same check.
|
|
@@ -25,6 +32,20 @@ module Rigor
|
|
|
25
32
|
end
|
|
26
33
|
end
|
|
27
34
|
|
|
35
|
+
# Defines `--format=FORMAT` restricted to `formats`, so an unsupported value is an `OptionParser::InvalidArgument`
|
|
36
|
+
# raised while the options are parsed (#2502). A check left to the renderer ran the whole analysis first, and a
|
|
37
|
+
# renderer that read only `format == "json"` printed text for any other value. The match is exact: an
|
|
38
|
+
# OptionParser enum would also complete a prefix (`--format=j`), which then turns ambiguous the day a second
|
|
39
|
+
# format shares it. The renderers' own "unsupported format" branches remain as a guard against internal drift.
|
|
40
|
+
def add_format(parser, options, description, formats: %w[text json])
|
|
41
|
+
parser.instance_variable_set(:@rigor_accepted_formats, formats.dup.freeze)
|
|
42
|
+
parser.on("--format=FORMAT", description) do |value|
|
|
43
|
+
raise OptionParser::InvalidArgument, value unless formats.include?(value)
|
|
44
|
+
|
|
45
|
+
options[:format] = value
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
28
49
|
# The `--config` value, checked where the flag is read (#1992). A named file that is not there is an error, not
|
|
29
50
|
# a request for the defaults: the run would otherwise analyse under a configuration the caller did not ask for,
|
|
30
51
|
# and an MCP caller would get a result for it with no sign anything was missing. Auto-discovery, with no
|
|
@@ -42,22 +63,40 @@ module Rigor
|
|
|
42
63
|
def add_editor_mode(parser, options)
|
|
43
64
|
parser.on("--tmp-file=PATH",
|
|
44
65
|
"Editor mode: read source bytes from PATH instead of --instead-of (paired)") do |value|
|
|
66
|
+
note_repeated_editor_flag(options, :tmp_file, "--tmp-file")
|
|
45
67
|
options[:tmp_file] = value
|
|
46
68
|
end
|
|
47
69
|
parser.on("--instead-of=PATH",
|
|
48
70
|
"Editor mode: the logical project path the buffer represents (paired with --tmp-file)") do |value|
|
|
71
|
+
note_repeated_editor_flag(options, :instead_of, "--instead-of")
|
|
49
72
|
options[:instead_of] = value
|
|
50
73
|
end
|
|
51
74
|
end
|
|
52
75
|
|
|
76
|
+
# Issue #2233 — editor mode binds one buffer per run, so a second `--tmp-file` or `--instead-of` cannot be
|
|
77
|
+
# honoured beside the first. Overwriting it dropped the first pair with no message; the repeat is recorded here
|
|
78
|
+
# and rejected by {resolve_buffer_binding} as a usage error instead.
|
|
79
|
+
def note_repeated_editor_flag(options, key, flag)
|
|
80
|
+
return if options[key].nil?
|
|
81
|
+
|
|
82
|
+
(options[:repeated_editor_flags] ||= []) << flag
|
|
83
|
+
end
|
|
84
|
+
|
|
53
85
|
# Resolves the editor-mode buffer binding from parsed `options`: returns nil when neither editor-mode flag is set,
|
|
54
86
|
# a `Analysis::BufferBinding` when the pair is valid, or `:usage_error` (after writing the reason to `err`) when
|
|
55
|
-
# the flags are unpaired or the temp file is unreadable.
|
|
87
|
+
# a flag is repeated, the flags are unpaired, or the temp file is unreadable.
|
|
56
88
|
def resolve_buffer_binding(options, err:)
|
|
57
89
|
tmp = options[:tmp_file]
|
|
58
90
|
instead = options[:instead_of]
|
|
59
91
|
return nil if tmp.nil? && instead.nil?
|
|
60
92
|
|
|
93
|
+
repeated = Array(options[:repeated_editor_flags])
|
|
94
|
+
unless repeated.empty?
|
|
95
|
+
err.puts("#{repeated.join(' and ')} given more than once; editor mode takes one buffer per run " \
|
|
96
|
+
"(a single --tmp-file / --instead-of pair)")
|
|
97
|
+
return :usage_error
|
|
98
|
+
end
|
|
99
|
+
|
|
61
100
|
if tmp.nil? || instead.nil?
|
|
62
101
|
err.puts("--tmp-file and --instead-of must appear together")
|
|
63
102
|
return :usage_error
|