rigortype 0.4.5 → 0.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/data/gem_overlay/activesupport/core_ext.rbs +16 -13
- data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
- data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
- data/docs/handbook/01-getting-started.md +5 -2
- data/docs/handbook/11-sig-gen.md +61 -5
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/manual/02-cli-reference.md +192 -27
- data/docs/manual/03-configuration.md +38 -4
- data/docs/manual/04-diagnostics.md +1 -1
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/09-editor-integration.md +13 -2
- data/docs/manual/10-mcp-server.md +47 -3
- data/docs/manual/12-caching.md +9 -1
- data/docs/manual/plugins/rigor-actionmailer.md +30 -0
- data/docs/manual/plugins/rigor-actionpack.md +49 -11
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +20 -1
- data/docs/manual/plugins/rigor-devise.md +15 -0
- data/docs/manual/plugins/rigor-rspec.md +2 -1
- data/lib/rigor/analysis/baseline.rb +24 -27
- data/lib/rigor/analysis/buffer_binding.rb +14 -0
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +4 -4
- data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +105 -12
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +143 -15
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +4 -1
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +1 -1
- data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +66 -3
- data/lib/rigor/analysis/check_rules/rebinding_block_callees/dispatch.rb +112 -0
- data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +394 -0
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- data/lib/rigor/analysis/check_rules/ruby_deprecations.rb +7 -0
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
- data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +53 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules.rb +217 -52
- data/lib/rigor/analysis/crash_signature.rb +2 -2
- data/lib/rigor/analysis/dependency_recorder.rb +15 -4
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/edit_replay.rb +154 -0
- data/lib/rigor/analysis/fact_store.rb +3 -3
- data/lib/rigor/analysis/incremental.rb +37 -0
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +2 -2
- data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
- data/lib/rigor/analysis/incremental_session.rb +202 -43
- data/lib/rigor/analysis/path_expansion.rb +41 -0
- data/lib/rigor/analysis/require_relative_closure.rb +100 -0
- data/lib/rigor/analysis/rule_catalog.rb +7 -3
- data/lib/rigor/analysis/run_disclosure_credit.rb +80 -0
- data/lib/rigor/analysis/run_disclosure_ledger.rb +155 -0
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +8 -4
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -1
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +204 -96
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +1 -1
- data/lib/rigor/analysis/runner/pool_coordinator.rb +157 -320
- data/lib/rigor/analysis/runner/project_pre_passes.rb +48 -6
- data/lib/rigor/analysis/runner/run_level_facts.rb +304 -0
- data/lib/rigor/analysis/runner/run_snapshots.rb +44 -54
- data/lib/rigor/analysis/runner/scan_environment.rb +51 -0
- data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
- data/lib/rigor/analysis/runner.rb +233 -120
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +1 -1
- data/lib/rigor/analysis/template_unit_positions.rb +2 -3
- data/lib/rigor/analysis/template_units.rb +9 -1
- data/lib/rigor/analysis/worker_session.rb +38 -33
- data/lib/rigor/bleeding_edge.rb +3 -6
- data/lib/rigor/builtins/imported_refinements.rb +6 -8
- data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
- data/lib/rigor/builtins/regex_refinement.rb +4 -6
- data/lib/rigor/cache/descriptor.rb +6 -5
- data/lib/rigor/cache/incremental_snapshot.rb +60 -18
- data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
- data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
- data/lib/rigor/cache/rbs_constant_table.rb +1 -1
- data/lib/rigor/cache/rbs_descriptor.rb +54 -3
- data/lib/rigor/cache/rbs_environment.rb +4 -1
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
- data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
- data/lib/rigor/cache/store.rb +11 -10
- data/lib/rigor/ci_detector.rb +1 -1
- data/lib/rigor/cli/annotate_command.rb +24 -19
- data/lib/rigor/cli/baseline_command.rb +145 -55
- data/lib/rigor/cli/catalogue.rb +256 -0
- data/lib/rigor/cli/check_command.rb +188 -93
- data/lib/rigor/cli/check_runner_factory.rb +2 -2
- data/lib/rigor/cli/command.rb +15 -1
- data/lib/rigor/cli/coverage_command.rb +21 -8
- data/lib/rigor/cli/coverage_mutation.rb +5 -7
- data/lib/rigor/cli/coverage_renderer.rb +2 -1
- data/lib/rigor/cli/coverage_scan.rb +3 -5
- data/lib/rigor/cli/diff_command.rb +27 -21
- data/lib/rigor/cli/docs_command.rb +1 -2
- data/lib/rigor/cli/doctor_command.rb +12 -12
- data/lib/rigor/cli/effects_command.rb +50 -40
- data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
- data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
- data/lib/rigor/cli/effects_renderer.rb +2 -1
- data/lib/rigor/cli/effects_snapshot_command.rb +18 -17
- data/lib/rigor/cli/explain_command.rb +18 -8
- data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
- data/lib/rigor/cli/json_document.rb +41 -0
- data/lib/rigor/cli/lens_command.rb +17 -13
- data/lib/rigor/cli/lsp_command.rb +250 -23
- data/lib/rigor/cli/mcp_command.rb +14 -8
- data/lib/rigor/cli/mutation_fork_scan.rb +1 -1
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -2
- data/lib/rigor/cli/options.rb +60 -4
- data/lib/rigor/cli/plugin_command.rb +93 -46
- data/lib/rigor/cli/plugins_command.rb +22 -18
- data/lib/rigor/cli/plugins_renderer.rb +34 -8
- data/lib/rigor/cli/protection_fork_scan.rb +2 -2
- data/lib/rigor/cli/protection_renderer.rb +2 -1
- data/lib/rigor/cli/show_bleedingedge_command.rb +23 -16
- data/lib/rigor/cli/sig_gen_command.rb +130 -34
- data/lib/rigor/cli/skill_command.rb +32 -3
- data/lib/rigor/cli/skill_deep_probe.rb +1 -1
- data/lib/rigor/cli/skill_describe.rb +1 -1
- data/lib/rigor/cli/trace_command.rb +14 -9
- data/lib/rigor/cli/triage_command.rb +10 -11
- data/lib/rigor/cli/triage_renderer.rb +2 -1
- data/lib/rigor/cli/type_of_command.rb +18 -12
- data/lib/rigor/cli/type_of_project_scope.rb +24 -0
- data/lib/rigor/cli/type_of_renderer.rb +4 -1
- data/lib/rigor/cli/type_scan_command.rb +14 -9
- data/lib/rigor/cli/type_scan_renderer.rb +2 -1
- data/lib/rigor/cli/unused_command.rb +27 -17
- data/lib/rigor/cli/upgrade_command.rb +5 -0
- data/lib/rigor/cli.rb +209 -54
- data/lib/rigor/config_audit.rb +17 -6
- data/lib/rigor/configuration/severity_profile.rb +5 -5
- data/lib/rigor/configuration.rb +86 -34
- data/lib/rigor/effects/definition_context.rb +0 -2
- data/lib/rigor/effects/envelope.rb +1 -1
- data/lib/rigor/effects/framework_units.rb +1 -1
- data/lib/rigor/effects/mutation_classifier.rb +4 -7
- data/lib/rigor/effects/propagator.rb +1 -1
- data/lib/rigor/effects/registry.rb +2 -2
- data/lib/rigor/effects/snapshot.rb +1 -1
- data/lib/rigor/effects/taint_cause.rb +0 -2
- data/lib/rigor/effects/unit_scan.rb +9 -13
- data/lib/rigor/environment/bundle_sig_discovery.rb +5 -9
- data/lib/rigor/environment/class_registry.rb +3 -3
- data/lib/rigor/environment/core_root.rb +77 -0
- data/lib/rigor/environment/default_libraries.rb +1 -1
- data/lib/rigor/environment/deferred_signature_plan.rb +283 -0
- data/lib/rigor/environment/gated_signature_guard.rb +1 -1
- data/lib/rigor/environment/gated_signature_plan.rb +5 -2
- data/lib/rigor/environment/lockfile_resolver.rb +28 -12
- data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
- data/lib/rigor/environment/member_consistency.rb +56 -7
- data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
- data/lib/rigor/environment/project_path_gems.rb +124 -0
- data/lib/rigor/environment/rbs_hierarchy.rb +6 -15
- data/lib/rigor/environment/rbs_loader.rb +592 -70
- data/lib/rigor/environment/reflection.rb +7 -7
- data/lib/rigor/environment/required_features.rb +136 -32
- data/lib/rigor/environment/ruby_library_roots.rb +122 -0
- data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
- data/lib/rigor/environment.rb +44 -11
- data/lib/rigor/flow_contribution/merger.rb +4 -4
- data/lib/rigor/inference/acceptance.rb +93 -32
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/block_run_count.rb +178 -0
- data/lib/rigor/inference/budget_trace.rb +8 -2
- data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
- data/lib/rigor/inference/callee_mutation_chain.rb +126 -0
- data/lib/rigor/inference/callee_write_census.rb +815 -0
- data/lib/rigor/inference/construction_escape.rb +217 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
- data/lib/rigor/inference/deep_erasure.rb +170 -0
- data/lib/rigor/inference/def_handle.rb +1 -1
- data/lib/rigor/inference/def_node_resolver.rb +1 -1
- data/lib/rigor/inference/def_return_typer.rb +204 -12
- data/lib/rigor/inference/definer_resolution.rb +21 -5
- data/lib/rigor/inference/definers_index.rb +106 -0
- data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
- data/lib/rigor/inference/element_read_widening.rb +49 -3
- data/lib/rigor/inference/error_info.rb +17 -3
- data/lib/rigor/inference/expression_typer.rb +173 -57
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/global_write_census.rb +110 -1
- data/lib/rigor/inference/global_writer_reach.rb +260 -0
- data/lib/rigor/inference/hkt_body.rb +1 -2
- data/lib/rigor/inference/hkt_body_parser.rb +3 -5
- data/lib/rigor/inference/hkt_reducer.rb +2 -2
- data/lib/rigor/inference/hkt_registry.rb +3 -5
- data/lib/rigor/inference/indexed_narrowing.rb +4 -7
- data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
- data/lib/rigor/inference/ivar_writer_census.rb +246 -0
- data/lib/rigor/inference/key_presence_guard.rb +2 -1
- data/lib/rigor/inference/loop_value_record.rb +57 -0
- data/lib/rigor/inference/match_rebinding/frame.rb +65 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +45 -0
- data/lib/rigor/inference/match_rebinding.rb +11 -1
- data/lib/rigor/inference/memo_audit.rb +55 -0
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +113 -12
- data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -3
- data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +39 -27
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +203 -17
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +59 -24
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +83 -20
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +109 -29
- data/lib/rigor/inference/module_function_state/spellings.rb +64 -0
- data/lib/rigor/inference/module_function_state.rb +23 -1
- data/lib/rigor/inference/multi_target_binder.rb +1 -1
- data/lib/rigor/inference/mutation_rejoin.rb +240 -4
- data/lib/rigor/inference/mutation_widening.rb +162 -29
- data/lib/rigor/inference/narrowing.rb +113 -38
- data/lib/rigor/inference/object_mixins.rb +13 -1
- data/lib/rigor/inference/optimistic_origin.rb +1 -1
- data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +1 -1
- data/lib/rigor/inference/project_method_ownership.rb +6 -3
- data/lib/rigor/inference/project_seed_tables.rb +2 -1
- data/lib/rigor/inference/project_yielder.rb +122 -0
- data/lib/rigor/inference/receiver_alias.rb +20 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +3 -1
- data/lib/rigor/inference/refined_dispatch/own_method.rb +2 -1
- data/lib/rigor/inference/remover_holder_scan.rb +233 -0
- data/lib/rigor/inference/remover_narrowing.rb +224 -0
- data/lib/rigor/inference/rewrite_mutation.rb +53 -2
- data/lib/rigor/inference/scope_indexer/certainty.rb +9 -1
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_ivar_types.rb +64 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +205 -0
- data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
- data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
- data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +135 -0
- data/lib/rigor/inference/scope_indexer/unseen_ivar_writers.rb +133 -0
- data/lib/rigor/inference/scope_indexer.rb +614 -146
- data/lib/rigor/inference/self_call_ivar_writes.rb +101 -17
- data/lib/rigor/inference/set_mutation.rb +189 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +1 -2
- data/lib/rigor/inference/statement_evaluator.rb +3036 -278
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_index.rb +23 -0
- data/lib/rigor/inference/synthetic_method_scanner/project_members.rb +130 -0
- data/lib/rigor/inference/synthetic_method_scanner.rb +73 -14
- data/lib/rigor/inference/toplevel_def_census.rb +59 -0
- data/lib/rigor/inference/unknown_store_widening.rb +103 -4
- data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -1
- data/lib/rigor/language_server/buffer_table.rb +9 -3
- data/lib/rigor/language_server/completion_provider.rb +26 -20
- data/lib/rigor/language_server/config_warning_announcer.rb +28 -0
- data/lib/rigor/language_server/diagnostic_publisher.rb +28 -9
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -2
- data/lib/rigor/language_server/hover_provider.rb +1 -7
- data/lib/rigor/language_server/hover_renderer.rb +6 -8
- data/lib/rigor/language_server/project_context.rb +98 -4
- data/lib/rigor/language_server/publish_batcher.rb +3 -4
- data/lib/rigor/language_server/selection_range_provider.rb +2 -4
- data/lib/rigor/language_server/server.rb +12 -6
- data/lib/rigor/language_server/signature_help_provider.rb +4 -4
- data/lib/rigor/lens/plugins.rb +1 -1
- data/lib/rigor/mcp/server.rb +82 -3
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- data/lib/rigor/plugin/base.rb +26 -11
- data/lib/rigor/plugin/bundled_catalog.rb +1 -12
- data/lib/rigor/plugin/io_boundary.rb +1 -1
- data/lib/rigor/plugin/loader.rb +25 -8
- data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
- data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
- data/lib/rigor/plugin/manifest.rb +38 -16
- data/lib/rigor/plugin/registry.rb +41 -3
- data/lib/rigor/plugin/services.rb +2 -3
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
- data/lib/rigor/plugin/trust_policy.rb +2 -2
- data/lib/rigor/plugin/type_node_resolver.rb +2 -4
- data/lib/rigor/plugin.rb +6 -8
- data/lib/rigor/plugin_gap_advisory.rb +5 -1
- data/lib/rigor/project_environment.rb +16 -2
- data/lib/rigor/protection/analysis_guard.rb +1 -1
- data/lib/rigor/protection/discovery_seed.rb +96 -2
- data/lib/rigor/protection/mutator.rb +1 -1
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
- data/lib/rigor/rbs_extended/reporter.rb +27 -3
- data/lib/rigor/rbs_extended.rb +2 -3
- data/lib/rigor/reflection/constant_ancestors.rb +5 -3
- data/lib/rigor/rooted_glob.rb +25 -2
- data/lib/rigor/run_scoped_slots.rb +38 -0
- data/lib/rigor/runtime/jit.rb +1 -1
- data/lib/rigor/scope/discovery_index.rb +19 -6
- data/lib/rigor/scope/resolution_chain.rb +3 -1
- data/lib/rigor/scope.rb +379 -36
- data/lib/rigor/sig_gen/classification.rb +4 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
- data/lib/rigor/sig_gen/generator.rb +668 -84
- data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
- data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
- data/lib/rigor/sig_gen/layout_index.rb +3 -3
- data/lib/rigor/sig_gen/method_candidate.rb +23 -4
- data/lib/rigor/sig_gen/observation_collector.rb +7 -6
- data/lib/rigor/sig_gen/observed_call.rb +1 -1
- data/lib/rigor/sig_gen/path_mapper.rb +6 -0
- data/lib/rigor/sig_gen/rbs_validity.rb +1 -1
- data/lib/rigor/sig_gen/renderer.rb +21 -12
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +15 -2
- data/lib/rigor/sig_gen/type_elaborator.rb +21 -5
- data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
- data/lib/rigor/sig_gen/write_reporter.rb +63 -14
- data/lib/rigor/sig_gen/write_result.rb +23 -12
- data/lib/rigor/sig_gen/writer.rb +217 -37
- data/lib/rigor/type/acceptance_router.rb +2 -2
- data/lib/rigor/type/accepts_result.rb +1 -1
- data/lib/rigor/type/app.rb +4 -6
- data/lib/rigor/type/combinator.rb +13 -16
- data/lib/rigor/type/constant.rb +48 -7
- data/lib/rigor/type/hash_shape.rb +26 -40
- data/lib/rigor/type/struct_instance.rb +3 -2
- data/lib/rigor/type/union.rb +1 -1
- data/lib/rigor/type_node/identifier.rb +1 -2
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node/string_literal.rb +1 -2
- data/lib/rigor/type_node/symbol_literal.rb +1 -2
- data/lib/rigor/type_node/union.rb +3 -6
- data/lib/rigor/value_semantics.rb +1 -10
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +89 -70
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +25 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +79 -19
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +6 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +2 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +0 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +5 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +7 -12
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +19 -6
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +26 -13
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +5 -3
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +1 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +6 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +5 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +7 -6
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +4 -5
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -2
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +102 -11
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -2
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +3 -4
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +6 -8
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +5 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +1 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +17 -21
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +3 -2
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer.rb +1 -1
- data/sig/rigor/analysis/baseline.rbs +0 -2
- data/sig/rigor/analysis/fact_store.rbs +1 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
- data/sig/rigor/cli/diff_command.rbs +1 -1
- data/sig/rigor/cli/explain_command.rbs +1 -1
- data/sig/rigor/effects/file_collection.rbs +5 -6
- data/sig/rigor/effects/registry.rbs +8 -6
- data/sig/rigor/effects/summary.rbs +6 -5
- data/sig/rigor/environment.rbs +5 -3
- data/sig/rigor/inference/optimistic_origin.rbs +11 -0
- data/sig/rigor/inference.rbs +10 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +1 -0
- data/sig/rigor/plugin/effect_ancestry.rbs +6 -4
- data/sig/rigor/plugin/effect_attribution.rbs +25 -10
- data/sig/rigor/plugin/effect_edge.rbs +12 -5
- data/sig/rigor/plugin/effect_entry_points.rbs +8 -3
- data/sig/rigor/plugin/io_boundary.rbs +6 -5
- data/sig/rigor/plugin/manifest.rbs +16 -18
- data/sig/rigor/plugin/protocol_contract.rbs +6 -9
- data/sig/rigor/plugin/registry.rbs +1 -1
- data/sig/rigor/scope.rbs +51 -50
- data/sig/rigor/type.rbs +0 -6
- data/skills/rigor-plugin-author/SKILL.md +4 -4
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
- data/skills/rigor-plugin-review/SKILL.md +3 -3
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +12 -1
- metadata +55 -3
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "optionparser"
|
|
4
4
|
|
|
5
5
|
require_relative "../configuration"
|
|
6
|
+
require_relative "../analysis/path_expansion"
|
|
6
7
|
require_relative "../effects/config_envelopes"
|
|
7
8
|
require_relative "../effects/registry"
|
|
8
9
|
require_relative "options"
|
|
@@ -37,7 +38,11 @@ module Rigor
|
|
|
37
38
|
|
|
38
39
|
VALID_MODES = %w[print diff write check].freeze
|
|
39
40
|
VALID_PARAM_POLICIES = %w[untyped observed observed-strict].freeze
|
|
40
|
-
|
|
41
|
+
|
|
42
|
+
# The per-target outcomes `--write` refuses: the file is left untouched and the run exits 1. `--check` counts
|
|
43
|
+
# them apart from the targets it would update, since `--write` does not fix a refused file.
|
|
44
|
+
TARGET_REFUSALS = %i[skipped_invalid_rbs skipped_invalid_encoding skipped_unparseable].freeze
|
|
45
|
+
private_constant :TARGET_REFUSALS
|
|
41
46
|
|
|
42
47
|
# The skip reasons {#report_skipped} counts. The four left out each have a report of their own
|
|
43
48
|
# ({#report_unrenderable}, {#report_unresolvable_superclasses}, {#report_inline_declared}, and the refusal
|
|
@@ -55,29 +60,43 @@ module Rigor
|
|
|
55
60
|
configuration = Configuration.load(options.fetch(:config))
|
|
56
61
|
paths = @argv.empty? ? configuration.paths : @argv
|
|
57
62
|
|
|
63
|
+
path_errors = path_diagnostics(paths, configuration)
|
|
64
|
+
report_path_errors(path_errors, options)
|
|
65
|
+
|
|
58
66
|
observation_roots = observation_roots(configuration, options)
|
|
59
|
-
observations = collect_observations(configuration, options, observation_roots)
|
|
67
|
+
observations = collect_observations(configuration, options, observation_roots, paths)
|
|
68
|
+
mode = options.fetch(:mode).to_sym
|
|
60
69
|
generator = SigGen::Generator.new(configuration: configuration, paths: paths,
|
|
61
70
|
observations: observations,
|
|
62
71
|
include_private: options.fetch(:include_private),
|
|
63
72
|
effect_annotator: effect_annotator(configuration, paths, options),
|
|
64
|
-
overwrite: options.fetch(:overwrite)
|
|
73
|
+
overwrite: options.fetch(:overwrite),
|
|
74
|
+
target_writer: target_writer(configuration, options, mode))
|
|
65
75
|
candidates = generator.run
|
|
66
|
-
mode = options.fetch(:mode).to_sym
|
|
67
76
|
|
|
68
77
|
status = case mode
|
|
69
|
-
when :write then dispatch_write(candidates, configuration, options)
|
|
70
|
-
when :check then dispatch_check(candidates, configuration, options)
|
|
78
|
+
when :write then dispatch_write(candidates, configuration, options, generator)
|
|
79
|
+
when :check then dispatch_check(candidates, configuration, options, generator)
|
|
71
80
|
else
|
|
72
|
-
dispatch_print_or_diff(candidates, mode, options, observation_roots)
|
|
81
|
+
dispatch_print_or_diff(candidates, mode, options, observation_roots, path_errors)
|
|
73
82
|
0
|
|
74
83
|
end
|
|
84
|
+
status = 1 if path_errors.any?(&:error?)
|
|
85
|
+
report_run_notes(candidates, generator, options)
|
|
86
|
+
status
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def report_run_notes(candidates, generator, options)
|
|
75
90
|
report_skipped(candidates, options)
|
|
76
91
|
report_inline_declared(candidates, options)
|
|
77
92
|
report_withheld_annotations(candidates, options)
|
|
78
93
|
report_unrenderable(generator.unrenderable)
|
|
79
|
-
report_unresolvable_superclasses(generator.unresolvable_superclasses)
|
|
80
|
-
|
|
94
|
+
report_unresolvable_superclasses(generator.unresolvable_superclasses, generator)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# The parser `run` parses with, built without parsing anything (the catalogue reads it, ADR-126 WD3).
|
|
98
|
+
def option_parser(options = {})
|
|
99
|
+
build_option_parser(options)
|
|
81
100
|
end
|
|
82
101
|
|
|
83
102
|
private
|
|
@@ -208,41 +227,87 @@ module Rigor
|
|
|
208
227
|
# declaring it would fail to build and take the class's type coverage with it. Naming the unresolved
|
|
209
228
|
# superclass is the actionable part — it is usually one gem's missing RBS standing between the user
|
|
210
229
|
# and signatures for a whole layer of their app.
|
|
211
|
-
|
|
212
|
-
|
|
230
|
+
#
|
|
231
|
+
# Issue #2437 — a superclass the project declares in source but this run wrote nothing for gets different
|
|
232
|
+
# advice: no gem RBS is missing; the superclass's own methods could not be typed, or were not written.
|
|
233
|
+
def report_unresolvable_superclasses(unresolvable, generator)
|
|
234
|
+
reports = unresolvable.map { |name, superclass| [name, *generator.unresolvable_superclass_report(superclass)] }
|
|
235
|
+
project, external = reports.partition { |_name, kind, _root| kind == :project }
|
|
236
|
+
.map { |rows| rows.to_h { |name, _kind, root| [name, root] } }
|
|
237
|
+
report_unwritten_project_superclasses(project)
|
|
238
|
+
return if external.empty?
|
|
213
239
|
|
|
214
240
|
@err.puts(
|
|
215
|
-
"rigor sig-gen: skipped #{
|
|
241
|
+
"rigor sig-gen: skipped #{external.size} class(es) whose superclass no loaded RBS declares. " \
|
|
216
242
|
"A signature for them would fail to build (`RBS::NoSuperclassFoundError`) and would leave the " \
|
|
217
243
|
"class LESS typed than it is now. Provide RBS for the superclass — `rbs collection install`, the " \
|
|
218
244
|
"gem's own `sig/`, or a `signature_paths:` entry — and re-run."
|
|
219
245
|
)
|
|
220
|
-
|
|
246
|
+
external.sort.each { |class_name, superclass| @err.puts(" #{class_name} < #{superclass}") }
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def report_unwritten_project_superclasses(project)
|
|
250
|
+
return if project.empty?
|
|
251
|
+
|
|
252
|
+
@err.puts(
|
|
253
|
+
"rigor sig-gen: skipped #{project.size} class(es) whose superclass this run wrote no signature for. " \
|
|
254
|
+
"The superclass is declared in the project's source, but its own methods could not be typed, or its " \
|
|
255
|
+
"`sig/` file could not be written, so a signature for these subclasses would fail to build " \
|
|
256
|
+
"(`RBS::NoSuperclassFoundError`). Annotate or type the superclass's methods, fix its `sig/` file, or " \
|
|
257
|
+
"hand-write its declaration, and re-run."
|
|
258
|
+
)
|
|
259
|
+
project.sort.each { |class_name, superclass| @err.puts(" #{class_name} < #{superclass}") }
|
|
221
260
|
end
|
|
222
261
|
|
|
223
|
-
|
|
262
|
+
# #1991 — the path errors `rigor check` reports for the arguments: a path that is neither a directory nor a
|
|
263
|
+
# `.rb` file. Warned about beside files that will be read, an error when nothing will be, as `check` grades it.
|
|
264
|
+
def path_diagnostics(paths, configuration)
|
|
265
|
+
bad = Analysis::PathExpansion.unusable(paths)
|
|
266
|
+
return [] if bad.empty?
|
|
267
|
+
|
|
268
|
+
any_files = !Analysis::PathExpansion.ruby_files(paths, configuration.exclude_patterns,
|
|
269
|
+
base: configuration.exclude_base).empty?
|
|
270
|
+
Analysis::PathExpansion.diagnostics(bad, any_files: any_files)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Without this a missing path was read as empty, so the run said "No candidates" and exited 0 (#1991). JSON
|
|
274
|
+
# print/diff carries the errors in its payload (see {#path_errors_in_payload?}); every other form says them on
|
|
275
|
+
# stderr, as `rigor check` does.
|
|
276
|
+
def report_path_errors(path_errors, options)
|
|
277
|
+
return if path_errors_in_payload?(options)
|
|
278
|
+
|
|
279
|
+
path_errors.each { |diagnostic| @err.puts(diagnostic.to_s) }
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def path_errors_in_payload?(options)
|
|
283
|
+
options.fetch(:format) == "json" && %w[print diff].include?(options.fetch(:mode))
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def dispatch_print_or_diff(candidates, mode, options, observation_roots, path_errors)
|
|
224
287
|
SigGen::Renderer.new(out: @out).render(
|
|
225
288
|
candidates: candidates,
|
|
226
289
|
mode: mode,
|
|
227
290
|
format: options.fetch(:format),
|
|
228
291
|
selection: options.fetch(:selection),
|
|
229
|
-
observation: observation_report(observation_roots, options)
|
|
292
|
+
observation: observation_report(observation_roots, options),
|
|
293
|
+
diagnostics: path_errors_in_payload?(options) ? path_errors : []
|
|
230
294
|
)
|
|
231
295
|
end
|
|
232
296
|
|
|
233
297
|
# @return exit status — non-zero when a file the user asked to write could not be written.
|
|
234
|
-
def dispatch_write(candidates, configuration, options)
|
|
235
|
-
|
|
298
|
+
def dispatch_write(candidates, configuration, options, generator)
|
|
299
|
+
writer = build_writer(configuration, options)
|
|
300
|
+
results = writer.write_all(candidates, header_spelling: generator.method(:generic_header_spelling))
|
|
236
301
|
|
|
237
302
|
refused = refused_candidates(candidates)
|
|
238
|
-
SigGen::Renderer.new(out: @out).render_write(results: results, format: options.fetch(:format), refused: refused
|
|
303
|
+
SigGen::Renderer.new(out: @out).render_write(results: results, format: options.fetch(:format), refused: refused,
|
|
304
|
+
left_bare: writer.left_bare)
|
|
239
305
|
SigGen::Renderer.refusal_lines(refused).each { |line| @err.puts(line) }
|
|
240
|
-
# A refused write (an assembled file that does not parse, an existing target that is not valid UTF-8
|
|
241
|
-
# or a method whose inline and `sig/` declarations disagree without `--overwrite`) means the
|
|
242
|
-
# write and did not get one, so the command must not report success — a green
|
|
243
|
-
# would otherwise mean nothing.
|
|
244
|
-
|
|
245
|
-
results.any? { |result| refusals.include?(result.action) } || !refused.empty? ? 1 : 0
|
|
306
|
+
# A refused write (an assembled file that does not parse, an existing target that is not valid UTF-8 or
|
|
307
|
+
# does not parse, or a method whose inline and `sig/` declarations disagree without `--overwrite`) means the
|
|
308
|
+
# user asked for a write and did not get one, so the command must not report success — a green
|
|
309
|
+
# `sig-gen --write` in CI would otherwise mean nothing.
|
|
310
|
+
results.any? { |result| TARGET_REFUSALS.include?(result.action) } || !refused.empty? ? 1 : 0
|
|
246
311
|
end
|
|
247
312
|
|
|
248
313
|
# ADR-112 WD4 — the methods whose inline and `sig/` declarations disagree, refused because the run did not
|
|
@@ -258,11 +323,15 @@ module Rigor
|
|
|
258
323
|
# --overwrite` would apply it.
|
|
259
324
|
#
|
|
260
325
|
# @return 1 when `sig/` is out of date (or a write would be refused), 0 when it is current.
|
|
261
|
-
|
|
262
|
-
|
|
326
|
+
#
|
|
327
|
+
# A generic header held back from the repair (#2605) is reported, but does not fail the check: `--write`
|
|
328
|
+
# cannot change it, and it was failing the project's env build before the run as well.
|
|
329
|
+
def dispatch_check(candidates, configuration, options, generator)
|
|
330
|
+
writer = build_writer(configuration, options, dry_run: true)
|
|
331
|
+
results = writer.write_all(candidates, header_spelling: generator.method(:generic_header_spelling))
|
|
263
332
|
refused = refused_candidates(candidates)
|
|
264
333
|
SigGen::Renderer.new(out: @out).render_check(results: results, format: options.fetch(:format),
|
|
265
|
-
refused: refused)
|
|
334
|
+
refused: refused, left_bare: writer.left_bare)
|
|
266
335
|
stale = SigGen::Renderer.out_of_date(results)
|
|
267
336
|
return 0 if stale.empty? && refused.empty?
|
|
268
337
|
|
|
@@ -271,16 +340,40 @@ module Rigor
|
|
|
271
340
|
end
|
|
272
341
|
|
|
273
342
|
def report_check_failure(stale, refused)
|
|
274
|
-
|
|
275
|
-
|
|
343
|
+
pending, refusals = stale.partition { |result| !TARGET_REFUSALS.include?(result.action) }
|
|
344
|
+
unless pending.empty?
|
|
345
|
+
@err.puts("rigor sig-gen --check: #{pending.size} signature file(s) out of date; " \
|
|
276
346
|
"run `rigor sig-gen --write` with the same options to update them.")
|
|
277
347
|
end
|
|
348
|
+
@err.puts(refused_targets_line(refusals)) unless refusals.empty?
|
|
278
349
|
return if refused.empty?
|
|
279
350
|
|
|
280
351
|
@err.puts("rigor sig-gen --check: #{refused.size} method(s) whose inline declaration and sig/ copy " \
|
|
281
352
|
"disagree; make them agree, or pass --overwrite to replace the sig/ member.")
|
|
282
353
|
end
|
|
283
354
|
|
|
355
|
+
# A target `--write` refuses is not one it would update: it is counted apart from the out-of-date targets. Where
|
|
356
|
+
# the existing file is the user's to repair (encoding, parse), it is named for a hand fix. An invalid generated
|
|
357
|
+
# file is a Rigor rendering bug (its REFUSED line says so), so it is counted but never listed as a hand fix.
|
|
358
|
+
def refused_targets_line(results)
|
|
359
|
+
by_hand = results.reject { |result| result.action == :skipped_invalid_rbs }
|
|
360
|
+
.map { |result| result.target_path.to_s }
|
|
361
|
+
head = "rigor sig-gen --check: #{results.size} signature file(s) refused; `--write` does not update them"
|
|
362
|
+
if by_hand.empty?
|
|
363
|
+
return "#{head}. The generated RBS did not parse: a Rigor bug, see its REFUSED line on stdout."
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
"#{head}. Repair by hand: #{by_hand.join(', ')}."
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
# #2461 — only a run that writes or compares files asks whether the writer will accept each target: where a
|
|
370
|
+
# target lands depends on the working directory, and print, diff and JSON output must not.
|
|
371
|
+
def target_writer(configuration, options, mode)
|
|
372
|
+
return nil unless %i[write check].include?(mode)
|
|
373
|
+
|
|
374
|
+
build_writer(configuration, options, dry_run: true)
|
|
375
|
+
end
|
|
376
|
+
|
|
284
377
|
def build_writer(configuration, options, dry_run: false)
|
|
285
378
|
layout_index = SigGen::LayoutIndex.new(signature_paths: configuration.signature_paths)
|
|
286
379
|
path_mapper = SigGen::PathMapper.new(configuration: configuration, layout_index: layout_index)
|
|
@@ -291,7 +384,11 @@ module Rigor
|
|
|
291
384
|
# specified, observe the project's test roots: `test_paths:`, or whichever of `spec/` and `test/` exist. With no
|
|
292
385
|
# root at all the run still succeeds, but says so — otherwise every parameter stays `untyped` with nothing
|
|
293
386
|
# naming the reason.
|
|
294
|
-
|
|
387
|
+
#
|
|
388
|
+
# The collector pre-indexes the classes of the paths the run generates for as well as the configured ones, so
|
|
389
|
+
# a call site on a class named only on the command line (`sig-gen ../lib` from a subdirectory, where no
|
|
390
|
+
# config file is discovered and the default `paths:` resolve under the subdirectory) attaches (#2078).
|
|
391
|
+
def collect_observations(configuration, options, observe_paths, paths)
|
|
295
392
|
return {} if options.fetch(:params) != "observed"
|
|
296
393
|
|
|
297
394
|
if observe_paths.empty?
|
|
@@ -299,7 +396,8 @@ module Rigor
|
|
|
299
396
|
else
|
|
300
397
|
warn_missing_test_roots(observe_paths)
|
|
301
398
|
end
|
|
302
|
-
SigGen::ObservationCollector.new(configuration: configuration, paths: observe_paths
|
|
399
|
+
SigGen::ObservationCollector.new(configuration: configuration, paths: observe_paths,
|
|
400
|
+
source_paths: (configuration.paths + paths).uniq).collect
|
|
303
401
|
end
|
|
304
402
|
|
|
305
403
|
# The roots `--params=observed` reads: `--observe=PATH...` when given, else the project's test roots.
|
|
@@ -387,7 +485,7 @@ module Rigor
|
|
|
387
485
|
opts.on("--no-cache", "Do not read or write the analysis cache (effect collection only)") do
|
|
388
486
|
options[:no_cache] = true
|
|
389
487
|
end
|
|
390
|
-
|
|
488
|
+
Options.add_format(opts, options, "Output format: text or json")
|
|
391
489
|
opts.on("--params=POLICY", "Parameter policy: untyped (default), observed, observed-strict") do |value|
|
|
392
490
|
options[:params] = value
|
|
393
491
|
end
|
|
@@ -417,11 +515,9 @@ module Rigor
|
|
|
417
515
|
|
|
418
516
|
def validation_error(options)
|
|
419
517
|
mode = options.fetch(:mode)
|
|
420
|
-
format = options.fetch(:format)
|
|
421
518
|
params = options.fetch(:params)
|
|
422
519
|
|
|
423
520
|
return "--print, --diff, --write, and --check are mutually exclusive flags; pick one" if mode == "conflict"
|
|
424
|
-
return "unsupported --format=#{format}" unless VALID_FORMATS.include?(format)
|
|
425
521
|
return "unsupported --params=#{params}" unless VALID_PARAM_POLICIES.include?(params)
|
|
426
522
|
if params == "observed-strict"
|
|
427
523
|
return "--params=observed-strict is reserved until the capability-role catalog ships"
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "optparse"
|
|
4
|
+
|
|
3
5
|
require_relative "command"
|
|
4
6
|
require_relative "../rooted_glob"
|
|
5
7
|
|
|
@@ -77,6 +79,28 @@ module Rigor
|
|
|
77
79
|
# up.
|
|
78
80
|
SKILLS_ROOT = File.expand_path("../../../skills", __dir__).freeze
|
|
79
81
|
|
|
82
|
+
# The parser of `rigor skill`, built without parsing anything: the catalogue reads its flags from it (ADR-126
|
|
83
|
+
# WD3). The positional grammar is read by hand in {#run}, so this describes the switches and parses nothing.
|
|
84
|
+
def option_parser(options = {})
|
|
85
|
+
OptionParser.new do |opts|
|
|
86
|
+
opts.banner = USAGE.lines.first.chomp
|
|
87
|
+
opts.on("--full NAME", "Print the SKILL.md body and its references/ inline") { |v| options[:full] = v }
|
|
88
|
+
opts.on("--path NAME", "Print the absolute path of the SKILL.md file") { |v| options[:path] = v }
|
|
89
|
+
opts.on("--list", "List bundled skills (name + absolute path)") { options[:list] = true }
|
|
90
|
+
opts.on("--describe", "Report project state and recommend the next skill") { options[:describe] = true }
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# The parser of `rigor skill describe` and `rigor describe`; `banner` is the usage line of the spelling used.
|
|
95
|
+
def self.describe_option_parser(banner = "Usage: rigor skill describe [--deep]", options = {})
|
|
96
|
+
OptionParser.new do |opts|
|
|
97
|
+
opts.banner = banner
|
|
98
|
+
opts.on("--deep", "Run `rigor check` first and route the recommendation on its result") do
|
|
99
|
+
options[:deep] = true
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
80
104
|
# @return CLI exit status.
|
|
81
105
|
def run
|
|
82
106
|
case @argv.first
|
|
@@ -87,7 +111,7 @@ module Rigor
|
|
|
87
111
|
0
|
|
88
112
|
when "describe", "--describe"
|
|
89
113
|
@argv.shift
|
|
90
|
-
run_describe
|
|
114
|
+
run_describe("Usage: rigor skill describe [--deep]")
|
|
91
115
|
when "--list"
|
|
92
116
|
@argv.shift
|
|
93
117
|
run_list
|
|
@@ -176,7 +200,12 @@ module Rigor
|
|
|
176
200
|
# `--deep` is the one opt-in that breaks the "never runs `rigor check`" guarantee — and only for the invocation
|
|
177
201
|
# that asks for it (issue #148 / ADR-73 § "Field-trial follow-ups"). It runs a real check and lets the result
|
|
178
202
|
# pick the headline; the un-flagged command is unchanged, down to loading no analysis code at all.
|
|
179
|
-
def run_describe
|
|
203
|
+
def run_describe(usage)
|
|
204
|
+
if @argv.intersect?(%w[-h --help])
|
|
205
|
+
@out.puts(usage)
|
|
206
|
+
return 0
|
|
207
|
+
end
|
|
208
|
+
|
|
180
209
|
deep = !@argv.delete("--deep").nil?
|
|
181
210
|
unknown = @argv.first
|
|
182
211
|
return usage_error("unknown option for `describe`: #{unknown}") unless unknown.nil?
|
|
@@ -222,7 +251,7 @@ module Rigor
|
|
|
222
251
|
discover_skills.find { |s| s.fetch(:name) == name }
|
|
223
252
|
end
|
|
224
253
|
|
|
225
|
-
# The `references/*.md` files bundled alongside a skill
|
|
254
|
+
# The `references/*.md` files bundled alongside a skill.
|
|
226
255
|
def reference_files(skill)
|
|
227
256
|
dir = File.join(File.dirname(skill.fetch(:path)), "references")
|
|
228
257
|
return [] unless File.directory?(dir)
|
|
@@ -96,7 +96,7 @@ module Rigor
|
|
|
96
96
|
end
|
|
97
97
|
end
|
|
98
98
|
|
|
99
|
-
# The `rigor-doctor` signal
|
|
99
|
+
# The `rigor-doctor` signal: an RBS environment that built to
|
|
100
100
|
# zero classes (analysis is hollow, so every other count is meaningless), or a `configuration-error` the run
|
|
101
101
|
# already emitted. Checked first — with a broken setup the remaining diagnostics are not evidence of anything.
|
|
102
102
|
def broken_environment?(result)
|
|
@@ -361,7 +361,7 @@ module Rigor
|
|
|
361
361
|
end
|
|
362
362
|
|
|
363
363
|
# The one-line essence of a skill's frontmatter `description`, read live from the SKILL.md so the catalogue can
|
|
364
|
-
# never go stale relative to the shipped skill. Drops the `Triggers:`
|
|
364
|
+
# never go stale relative to the shipped skill. Drops the `Triggers:` tail and caps the length.
|
|
365
365
|
def catalog_blurb(path)
|
|
366
366
|
text = frontmatter(path).fetch("description", "").to_s.strip.tr("\n", " ").squeeze(" ")
|
|
367
367
|
head = text.split(/\s*Triggers?:/, 2).first.to_s.strip
|
|
@@ -12,6 +12,7 @@ require_relative "../inference/flow_tracer"
|
|
|
12
12
|
require_relative "../inference/scope_indexer"
|
|
13
13
|
require_relative "command"
|
|
14
14
|
require_relative "trace_renderer"
|
|
15
|
+
require_relative "json_document"
|
|
15
16
|
require_relative "../project_environment"
|
|
16
17
|
|
|
17
18
|
module Rigor
|
|
@@ -43,15 +44,12 @@ module Rigor
|
|
|
43
44
|
execute(file: file, options: options)
|
|
44
45
|
end
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def
|
|
49
|
-
|
|
50
|
-
parser = OptionParser.new do |opts|
|
|
47
|
+
# The parser this command parses with, built without parsing anything: the catalogue reads the
|
|
48
|
+
# flags and formats from it (ADR-126 WD3). `options` is only written by the switches' blocks.
|
|
49
|
+
def option_parser(options = {})
|
|
50
|
+
OptionParser.new do |opts|
|
|
51
51
|
opts.banner = USAGE
|
|
52
|
-
|
|
53
|
-
options[:format] = value
|
|
54
|
-
end
|
|
52
|
+
Options.add_format(opts, options, "Output format: text (animation) or json (raw event stream)")
|
|
55
53
|
opts.on("--delay=SECONDS", Float,
|
|
56
54
|
"Autoplay with SECONDS between frames (default: step on key press)") do |value|
|
|
57
55
|
options[:delay] = value
|
|
@@ -62,7 +60,13 @@ module Rigor
|
|
|
62
60
|
opts.on("--verbose", "Include every expression enter/result frame") { options[:verbose] = true }
|
|
63
61
|
Options.add_config(opts, options)
|
|
64
62
|
end
|
|
65
|
-
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def parse_options
|
|
68
|
+
options = { format: "text", delay: nil, verbose: false, line: nil, config: nil }
|
|
69
|
+
option_parser(options).parse!(@argv)
|
|
66
70
|
options
|
|
67
71
|
end
|
|
68
72
|
|
|
@@ -82,6 +86,7 @@ module Rigor
|
|
|
82
86
|
|
|
83
87
|
if options.fetch(:format) == "json"
|
|
84
88
|
@out.puts(JSON.pretty_generate(frames.map { |event| event_to_h(event) }))
|
|
89
|
+
@err.puts(JsonDocument::SHAPE_NOTICES.fetch(:trace))
|
|
85
90
|
return 0
|
|
86
91
|
end
|
|
87
92
|
|
|
@@ -36,14 +36,13 @@ module Rigor
|
|
|
36
36
|
0
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def
|
|
42
|
-
options = { config: nil, format: "text", top: 10, sections: DEFAULT_SECTIONS, include_info: false }
|
|
39
|
+
# The parser this command parses with, built without parsing anything: the catalogue reads the
|
|
40
|
+
# flags and formats from it (ADR-126 WD3). `options` is only written by the switches' blocks.
|
|
41
|
+
def option_parser(options = {})
|
|
43
42
|
OptionParser.new do |opts|
|
|
44
43
|
opts.banner = USAGE
|
|
45
44
|
Options.add_config(opts, options)
|
|
46
|
-
|
|
45
|
+
Options.add_format(opts, options, "Output format: text (default) or json")
|
|
47
46
|
opts.on("--top=N", Integer, "Hotspot-file count (default 10)") { |v| options[:top] = v }
|
|
48
47
|
opts.on("--include-info",
|
|
49
48
|
"Route info diagnostics into distribution / selectors / hotspots " \
|
|
@@ -57,15 +56,15 @@ module Rigor
|
|
|
57
56
|
opts.on("--selectors-only", "Print only the class/method selectors section") do
|
|
58
57
|
options[:sections] = %i[selectors]
|
|
59
58
|
end
|
|
60
|
-
end
|
|
61
|
-
validate!(options)
|
|
62
|
-
options
|
|
59
|
+
end
|
|
63
60
|
end
|
|
64
61
|
|
|
65
|
-
|
|
66
|
-
return if %w[text json].include?(options.fetch(:format))
|
|
62
|
+
private
|
|
67
63
|
|
|
68
|
-
|
|
64
|
+
def parse_options
|
|
65
|
+
options = { config: nil, format: "text", top: 10, sections: DEFAULT_SECTIONS, include_info: false }
|
|
66
|
+
option_parser(options).parse!(@argv)
|
|
67
|
+
options
|
|
69
68
|
end
|
|
70
69
|
|
|
71
70
|
# Sequential, cache-backed, no run-stats: triage only needs the diagnostic stream, and sequential keeps the rule
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "json"
|
|
4
4
|
|
|
5
5
|
require_relative "../triage"
|
|
6
|
+
require_relative "json_document"
|
|
6
7
|
|
|
7
8
|
module Rigor
|
|
8
9
|
class CLI
|
|
@@ -17,7 +18,7 @@ module Rigor
|
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def json
|
|
20
|
-
JSON.pretty_generate(Triage.report_to_h(@report))
|
|
21
|
+
JSON.pretty_generate(JsonDocument.stamp(Triage.report_to_h(@report)))
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
def text
|
|
@@ -13,10 +13,12 @@ require_relative "../inference/scope_indexer"
|
|
|
13
13
|
require_relative "../inference/precision_scanner"
|
|
14
14
|
require_relative "../source/node_children"
|
|
15
15
|
require_relative "type_of_renderer"
|
|
16
|
+
require_relative "json_document"
|
|
16
17
|
require_relative "type_of_template_probe"
|
|
17
18
|
require_relative "command"
|
|
18
19
|
require_relative "options"
|
|
19
20
|
require_relative "../project_environment"
|
|
21
|
+
require_relative "type_of_project_scope"
|
|
20
22
|
|
|
21
23
|
module Rigor
|
|
22
24
|
class CLI
|
|
@@ -28,7 +30,7 @@ module Rigor
|
|
|
28
30
|
# Encapsulating the command in its own class keeps `Rigor::CLI` focused on dispatching and lets us evolve the
|
|
29
31
|
# type-of UX (extra flags, watch mode, streaming output) without bloating the CLI shell. Output formatting is
|
|
30
32
|
# delegated to {TypeOfRenderer}.
|
|
31
|
-
class TypeOfCommand < Command
|
|
33
|
+
class TypeOfCommand < Command # rubocop:disable Metrics/ClassLength
|
|
32
34
|
include TypeOfTemplateProbe
|
|
33
35
|
|
|
34
36
|
USAGE = "Usage: rigor type-of [options] FILE:LINE[:COL] [FILE:LINE[:COL] ...]"
|
|
@@ -64,19 +66,24 @@ module Rigor
|
|
|
64
66
|
execute(targets: targets, options: options, buffer: buffer)
|
|
65
67
|
end
|
|
66
68
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
parser = OptionParser.new do |opts|
|
|
69
|
+
# The parser this command parses with, built without parsing anything: the catalogue reads the
|
|
70
|
+
# flags and formats from it (ADR-126 WD3). `options` is only written by the switches' blocks.
|
|
71
|
+
def option_parser(options = {})
|
|
72
|
+
OptionParser.new do |opts|
|
|
73
73
|
opts.banner = USAGE
|
|
74
|
-
|
|
74
|
+
Options.add_format(opts, options, "Output format: text or json")
|
|
75
75
|
opts.on("--trace", "Record fail-soft fallbacks via FallbackTracer") { options[:trace] = true }
|
|
76
76
|
Options.add_config(opts, options)
|
|
77
77
|
Options.add_editor_mode(opts, options)
|
|
78
78
|
end
|
|
79
|
-
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def parse_options
|
|
84
|
+
options = { format: "text", trace: false, config: nil, tmp_file: nil, instead_of: nil }
|
|
85
|
+
|
|
86
|
+
option_parser(options).parse!(@argv)
|
|
80
87
|
|
|
81
88
|
options
|
|
82
89
|
end
|
|
@@ -88,7 +95,7 @@ module Rigor
|
|
|
88
95
|
def execute(targets:, options:, buffer: nil)
|
|
89
96
|
configuration = Configuration.load(options.fetch(:config))
|
|
90
97
|
environment = project_environment(targets.map(&:file).uniq, configuration, buffer)
|
|
91
|
-
base_scope =
|
|
98
|
+
base_scope = TypeOfProjectScope.build(environment: environment, configuration: configuration, buffer: buffer)
|
|
92
99
|
|
|
93
100
|
results_by_target = Array.new(targets.length)
|
|
94
101
|
targets.each_with_index.group_by { |target, _index| target.file }.each do |file, indexed_targets|
|
|
@@ -100,7 +107,7 @@ module Rigor
|
|
|
100
107
|
results = results_by_target.compact.flatten(1)
|
|
101
108
|
return 1 if results.empty?
|
|
102
109
|
|
|
103
|
-
TypeOfRenderer.new(out: @out).render(results, format: options.fetch(:format))
|
|
110
|
+
TypeOfRenderer.new(out: @out, err: @err).render(results, format: options.fetch(:format))
|
|
104
111
|
0
|
|
105
112
|
end
|
|
106
113
|
|
|
@@ -205,7 +212,6 @@ module Rigor
|
|
|
205
212
|
end
|
|
206
213
|
end
|
|
207
214
|
|
|
208
|
-
# `presentation` is the Result's optional `enumeration:` / `location_mapper:`.
|
|
209
215
|
def type_result(file, line, column, node, scope_index, options, **presentation)
|
|
210
216
|
tracer = options[:trace] ? Inference::FallbackTracer.new : nil
|
|
211
217
|
type = scope_index[node].type_of(node, tracer: tracer)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../scope"
|
|
4
|
+
require_relative "../protection/discovery_seed"
|
|
5
|
+
|
|
6
|
+
module Rigor
|
|
7
|
+
class CLI
|
|
8
|
+
# Issue #512 — the scope `rigor type-of` probes with: single-file, plus the cross-file tables a prior
|
|
9
|
+
# `rigor check --incremental` left in the snapshot ({Protection::DiscoverySeed.from_snapshot}) when there are
|
|
10
|
+
# any. A probe never re-parses the project for its seed, so without a reusable snapshot this is the bare
|
|
11
|
+
# `Scope.empty` it always was.
|
|
12
|
+
#
|
|
13
|
+
# The seed is the full {Inference::ProjectSeedTables} fold, so a `contested_*` sibling travels with the member
|
|
14
|
+
# it qualifies (ADR-119): this is a recorded whole-index consumer in `admission_census.yml`.
|
|
15
|
+
module TypeOfProjectScope
|
|
16
|
+
module_function
|
|
17
|
+
|
|
18
|
+
def build(environment:, configuration:, buffer: nil)
|
|
19
|
+
seed = Protection::DiscoverySeed.from_snapshot(configuration: configuration, buffer: buffer)
|
|
20
|
+
Protection::DiscoverySeed.seeded_scope(environment: environment, seed: seed)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -4,6 +4,7 @@ require "json"
|
|
|
4
4
|
require "optionparser"
|
|
5
5
|
|
|
6
6
|
require_relative "renderable"
|
|
7
|
+
require_relative "json_document"
|
|
7
8
|
|
|
8
9
|
module Rigor
|
|
9
10
|
class CLI
|
|
@@ -14,8 +15,9 @@ module Rigor
|
|
|
14
15
|
class TypeOfRenderer
|
|
15
16
|
include Renderable
|
|
16
17
|
|
|
17
|
-
def initialize(out:)
|
|
18
|
+
def initialize(out:, err: nil)
|
|
18
19
|
@out = out
|
|
20
|
+
@err = err
|
|
19
21
|
end
|
|
20
22
|
|
|
21
23
|
private
|
|
@@ -96,6 +98,7 @@ module Rigor
|
|
|
96
98
|
enumerations = line_enumerations(list)
|
|
97
99
|
payload[:line_enumerations] = enumerations unless enumerations.empty?
|
|
98
100
|
@out.puts(JSON.pretty_generate(payload))
|
|
101
|
+
@err&.puts(JsonDocument::SHAPE_NOTICES.fetch(:type_of))
|
|
99
102
|
end
|
|
100
103
|
|
|
101
104
|
def line_enumerations(results)
|
|
@@ -38,15 +38,12 @@ module Rigor
|
|
|
38
38
|
determine_exit(report, options)
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def
|
|
44
|
-
|
|
45
|
-
config: nil }
|
|
46
|
-
|
|
47
|
-
parser = OptionParser.new do |opts|
|
|
41
|
+
# The parser this command parses with, built without parsing anything: the catalogue reads the
|
|
42
|
+
# flags and formats from it (ADR-126 WD3). `options` is only written by the switches' blocks.
|
|
43
|
+
def option_parser(options = {})
|
|
44
|
+
OptionParser.new do |opts|
|
|
48
45
|
opts.banner = USAGE
|
|
49
|
-
|
|
46
|
+
Options.add_format(opts, options, "Output format: text or json")
|
|
50
47
|
Options.add_config(opts, options)
|
|
51
48
|
opts.on("--limit=N", Integer, "Max example events to print (text only)") do |value|
|
|
52
49
|
options[:limit] = value
|
|
@@ -58,7 +55,15 @@ module Rigor
|
|
|
58
55
|
options[:threshold] = value
|
|
59
56
|
end
|
|
60
57
|
end
|
|
61
|
-
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def parse_options
|
|
63
|
+
options = { format: "text", limit: 10, show_recognized: false, threshold: nil,
|
|
64
|
+
config: nil }
|
|
65
|
+
|
|
66
|
+
option_parser(options).parse!(@argv)
|
|
62
67
|
|
|
63
68
|
options
|
|
64
69
|
end
|