rigortype 0.3.7 → 0.3.9
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 +1 -1
- data/data/capability_roles/capability_roles.rbs +36 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +109 -13
- data/docs/handbook/02-everyday-types.md +17 -14
- data/docs/handbook/03-narrowing.md +37 -4
- data/docs/handbook/05-methods-and-blocks.md +1 -1
- data/docs/handbook/06-classes.md +2 -2
- data/docs/handbook/07-rbs-and-extended.md +14 -1
- data/docs/handbook/11-sig-gen.md +96 -15
- data/docs/handbook/README.md +1 -1
- data/docs/handbook/appendix-elixir.md +2 -2
- data/docs/handbook/appendix-go.md +2 -2
- data/docs/handbook/appendix-java-csharp.md +2 -2
- data/docs/handbook/appendix-mypy.md +2 -2
- data/docs/handbook/appendix-phpstan.md +1 -1
- data/docs/handbook/appendix-rust.md +1 -1
- data/docs/handbook/appendix-type-theory.md +4 -4
- data/docs/handbook/appendix-typescript.md +1 -1
- data/docs/llms.txt +2 -0
- data/docs/manual/02-cli-reference.md +39 -4
- data/docs/manual/03-configuration.md +11 -0
- data/docs/manual/04-diagnostics.md +12 -1
- data/docs/manual/07-plugins.md +15 -5
- data/docs/manual/08-skills.md +23 -2
- data/docs/manual/11-ci.md +9 -0
- data/docs/manual/15-type-protection-coverage.md +8 -0
- data/docs/manual/16-rbs-extended-annotations.md +6 -1
- data/docs/manual/18-removing-dead-code.md +10 -8
- data/docs/manual/plugins/rigor-actionpack.md +18 -0
- data/docs/manual/plugins/rigor-activerecord.md +28 -0
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +32 -2
- data/docs/manual/plugins/rigor-rbs-inline.md +42 -1
- data/lib/rigor/analysis/baseline.rb +2 -2
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
- data/lib/rigor/analysis/check_rules/rule_walk.rb +39 -4
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
- data/lib/rigor/analysis/check_rules.rb +200 -32
- data/lib/rigor/analysis/crash_signature.rb +69 -13
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
- data/lib/rigor/analysis/erb_template_detector.rb +1 -2
- data/lib/rigor/analysis/fact_store.rb +6 -1
- data/lib/rigor/analysis/incremental.rb +16 -0
- data/lib/rigor/analysis/incremental_session.rb +100 -29
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
- data/lib/rigor/analysis/reachability/graph.rb +3 -5
- data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -5
- data/lib/rigor/analysis/reachability/project_files.rb +2 -2
- data/lib/rigor/analysis/reachability/scan.rb +5 -5
- data/lib/rigor/analysis/reachability/scan_cache.rb +2 -2
- data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
- data/lib/rigor/analysis/result.rb +1 -3
- data/lib/rigor/analysis/rule_catalog.rb +4 -1
- data/lib/rigor/analysis/run_cache_key.rb +1 -1
- data/lib/rigor/analysis/run_cache_probe.rb +3 -5
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +316 -40
- data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -10
- data/lib/rigor/analysis/runner/pool_coordinator.rb +430 -77
- data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
- data/lib/rigor/analysis/runner/run_snapshots.rb +13 -3
- data/lib/rigor/analysis/runner.rb +200 -43
- data/lib/rigor/analysis/worker_session.rb +36 -12
- data/lib/rigor/bleeding_edge.rb +12 -24
- data/lib/rigor/builtins/hkt_builtins.rb +2 -2
- data/lib/rigor/builtins/imported_refinements.rb +166 -30
- data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
- data/lib/rigor/builtins/regex_refinement.rb +4 -4
- data/lib/rigor/builtins/static_return_refinements.rb +5 -6
- data/lib/rigor/cache/annotation_location.rb +70 -0
- data/lib/rigor/cache/descriptor.rb +58 -20
- data/lib/rigor/cache/engine_source.rb +4 -4
- data/lib/rigor/cache/file_digest.rb +9 -1
- data/lib/rigor/cache/incremental_snapshot.rb +40 -6
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
- data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
- data/lib/rigor/cache/rbs_constant_table.rb +0 -3
- data/lib/rigor/cache/rbs_descriptor.rb +79 -11
- data/lib/rigor/cache/rbs_environment.rb +8 -4
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +39 -0
- data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
- data/lib/rigor/cache/store.rb +85 -33
- data/lib/rigor/cli/annotate_command.rb +5 -6
- data/lib/rigor/cli/check_command.rb +57 -10
- data/lib/rigor/cli/check_invocation.rb +6 -11
- data/lib/rigor/cli/check_runner_factory.rb +1 -5
- data/lib/rigor/cli/coverage_command.rb +1 -1
- data/lib/rigor/cli/coverage_mutation.rb +12 -3
- data/lib/rigor/cli/coverage_scan.rb +1 -5
- data/lib/rigor/cli/diff_command.rb +1 -1
- data/lib/rigor/cli/doc_links.rb +3 -3
- data/lib/rigor/cli/docs_command.rb +2 -2
- data/lib/rigor/cli/doctor_command.rb +85 -38
- data/lib/rigor/cli/effects_command.rb +2 -2
- data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
- data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
- data/lib/rigor/cli/explain_command.rb +34 -1
- data/lib/rigor/cli/fused_protection_report.rb +7 -1
- data/lib/rigor/cli/lsp_command.rb +1 -1
- data/lib/rigor/cli/mcp_command.rb +1 -1
- data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
- data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
- data/lib/rigor/cli/mutation_protection_report.rb +7 -1
- data/lib/rigor/cli/plugin_command.rb +1 -1
- data/lib/rigor/cli/plugins_command.rb +2 -1
- data/lib/rigor/cli/prism_colorizer.rb +2 -2
- data/lib/rigor/cli/protection_fork_scan.rb +6 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
- data/lib/rigor/cli/sig_gen_command.rb +31 -2
- data/lib/rigor/cli/skill_command.rb +1 -1
- data/lib/rigor/cli/skill_deep_probe.rb +4 -4
- data/lib/rigor/cli/skill_describe.rb +36 -35
- data/lib/rigor/cli/trace_command.rb +4 -4
- data/lib/rigor/cli/trace_renderer.rb +5 -6
- data/lib/rigor/cli/triage_command.rb +1 -1
- data/lib/rigor/cli/type_of_command.rb +10 -6
- data/lib/rigor/cli/type_scan_command.rb +4 -4
- data/lib/rigor/cli/unused_command.rb +11 -3
- data/lib/rigor/cli/upgrade_command.rb +1 -1
- data/lib/rigor/cli.rb +68 -6
- data/lib/rigor/config_audit.rb +30 -5
- data/lib/rigor/configuration/severity_profile.rb +6 -6
- data/lib/rigor/configuration.rb +43 -4
- data/lib/rigor/effects/attribution.rb +1 -3
- data/lib/rigor/effects/config_envelopes.rb +6 -8
- data/lib/rigor/effects/definition_lines.rb +25 -6
- data/lib/rigor/effects/effect_table.rb +0 -1
- data/lib/rigor/effects/entry_points.rb +0 -2
- data/lib/rigor/effects/envelope_check.rb +8 -8
- data/lib/rigor/effects/envelope_index.rb +5 -8
- data/lib/rigor/effects/framework_units.rb +4 -6
- data/lib/rigor/effects/identity.rb +4 -6
- data/lib/rigor/effects/inline_anchor.rb +7 -7
- data/lib/rigor/effects/label_intent.rb +3 -4
- data/lib/rigor/effects/liskov_check.rb +8 -8
- data/lib/rigor/effects/method_key.rb +1 -1
- data/lib/rigor/effects/plugin_facts.rb +4 -6
- data/lib/rigor/effects/propagator.rb +2 -3
- data/lib/rigor/effects/scanner.rb +1 -1
- data/lib/rigor/effects/signature_sources.rb +3 -5
- data/lib/rigor/effects/snapshot.rb +10 -10
- data/lib/rigor/effects/snapshot_diff.rb +1 -1
- data/lib/rigor/effects/unit_scan.rb +10 -10
- data/lib/rigor/effects/unknown_label_check.rb +3 -8
- data/lib/rigor/effects/unknown_label_report.rb +3 -4
- data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
- data/lib/rigor/environment/class_registry.rb +3 -1
- data/lib/rigor/environment/failure_slot.rb +28 -0
- data/lib/rigor/environment/installed_gem_set.rb +85 -0
- data/lib/rigor/environment/lockfile_resolver.rb +51 -4
- data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
- data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
- data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
- data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
- data/lib/rigor/environment/rbs_loader.rb +613 -113
- data/lib/rigor/environment.rb +189 -27
- data/lib/rigor/flow_contribution/merger.rb +0 -2
- data/lib/rigor/flow_contribution.rb +11 -13
- data/lib/rigor/inference/acceptance.rb +153 -8
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/body_fixpoint.rb +5 -5
- data/lib/rigor/inference/budget_trace.rb +1 -5
- data/lib/rigor/inference/captured_locals.rb +2 -3
- data/lib/rigor/inference/closure_escape_analyzer.rb +2 -4
- data/lib/rigor/inference/coverage_scanner.rb +4 -6
- data/lib/rigor/inference/dynamic_origin.rb +1 -1
- data/lib/rigor/inference/element_read_widening.rb +168 -0
- data/lib/rigor/inference/expression_typer.rb +763 -143
- data/lib/rigor/inference/fork_map.rb +5 -7
- data/lib/rigor/inference/hkt_reducer.rb +2 -3
- data/lib/rigor/inference/hkt_registry.rb +19 -10
- data/lib/rigor/inference/hkt_sugar_translator.rb +15 -21
- data/lib/rigor/inference/index_write_widening.rb +1 -5
- data/lib/rigor/inference/macro_block_self_type.rb +1 -4
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +2 -9
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +61 -47
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +165 -31
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +4 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +12 -13
- data/lib/rigor/inference/method_parameter_binder.rb +4 -6
- data/lib/rigor/inference/multi_target_binder.rb +1 -3
- data/lib/rigor/inference/mutation_rejoin.rb +168 -0
- data/lib/rigor/inference/mutation_widening.rb +100 -51
- data/lib/rigor/inference/narrowing.rb +299 -46
- data/lib/rigor/inference/optimistic_origin.rb +1 -7
- data/lib/rigor/inference/origin_lookup.rb +2 -3
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +6 -5
- data/lib/rigor/inference/precision_scanner.rb +3 -4
- data/lib/rigor/inference/project_patched_methods.rb +2 -2
- data/lib/rigor/inference/project_patched_scanner.rb +3 -3
- data/lib/rigor/inference/protection_scanner.rb +1 -2
- data/lib/rigor/inference/range_constant.rb +57 -0
- data/lib/rigor/inference/rbs_type_translator.rb +49 -11
- data/lib/rigor/inference/receiver_alias.rb +3 -3
- data/lib/rigor/inference/refinement_mutation.rb +72 -0
- data/lib/rigor/inference/scope_indexer.rb +688 -151
- data/lib/rigor/inference/statement_evaluator.rb +221 -50
- data/lib/rigor/inference/string_mutation.rb +60 -0
- data/lib/rigor/inference/struct_fold_safety.rb +6 -7
- data/lib/rigor/inference/synthetic_method_index.rb +1 -2
- data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
- data/lib/rigor/inference/version_guard.rb +28 -21
- data/lib/rigor/inference/void_origin.rb +3 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -4
- data/lib/rigor/language_server/buffer_table.rb +4 -4
- data/lib/rigor/language_server/completion_provider.rb +1 -1
- data/lib/rigor/language_server/debouncer.rb +1 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +4 -4
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -1
- data/lib/rigor/language_server/hover_provider.rb +1 -1
- data/lib/rigor/language_server/hover_renderer.rb +2 -2
- data/lib/rigor/language_server/incremental_sync.rb +6 -6
- data/lib/rigor/language_server/project_context.rb +2 -5
- data/lib/rigor/language_server/publish_batcher.rb +2 -2
- data/lib/rigor/language_server/selection_range_provider.rb +2 -2
- data/lib/rigor/language_server/server.rb +9 -9
- data/lib/rigor/language_server/signature_help_provider.rb +1 -1
- data/lib/rigor/language_server/uri.rb +1 -1
- data/lib/rigor/plugin/base.rb +90 -21
- data/lib/rigor/plugin/bundled_catalog.rb +167 -0
- data/lib/rigor/plugin/effect_attribution.rb +7 -7
- data/lib/rigor/plugin/effect_entry_points.rb +3 -3
- data/lib/rigor/plugin/fact_store.rb +6 -6
- data/lib/rigor/plugin/io_boundary.rb +93 -8
- data/lib/rigor/plugin/isolation.rb +29 -6
- data/lib/rigor/plugin/loader.rb +31 -12
- data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
- data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
- data/lib/rigor/plugin/manifest.rb +23 -1
- data/lib/rigor/plugin/registry.rb +67 -14
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +12 -1
- data/lib/rigor/plugin/trust_policy.rb +2 -4
- data/lib/rigor/plugin/type_node_resolver.rb +3 -3
- data/lib/rigor/plugin_gap_advisory.rb +96 -0
- data/lib/rigor/project_environment.rb +138 -0
- data/lib/rigor/protection/analysis_guard.rb +93 -14
- data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
- data/lib/rigor/protection/dependency_closure.rb +5 -8
- data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
- data/lib/rigor/protection/discovery_seed.rb +11 -11
- data/lib/rigor/protection/kill_signature.rb +2 -4
- data/lib/rigor/protection/measurement_integrity.rb +1 -3
- data/lib/rigor/protection/mutation_cache.rb +6 -9
- data/lib/rigor/protection/mutation_scanner.rb +12 -15
- data/lib/rigor/protection/mutator.rb +2 -1
- data/lib/rigor/protection/test_suite_oracle.rb +5 -5
- data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
- data/lib/rigor/rbs_extended/envelope_scanner.rb +12 -13
- data/lib/rigor/rbs_extended/hkt_directives.rb +16 -1
- data/lib/rigor/rbs_extended/reporter.rb +122 -16
- data/lib/rigor/rbs_extended.rb +67 -22
- data/lib/rigor/reflection/constant_path.rb +126 -0
- data/lib/rigor/reflection.rb +26 -10
- data/lib/rigor/runtime/jit.rb +6 -8
- data/lib/rigor/scope/discovery_index.rb +30 -0
- data/lib/rigor/scope.rb +203 -4
- data/lib/rigor/sig_gen/generator.rb +181 -102
- data/lib/rigor/sig_gen/layout_index.rb +3 -4
- data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
- data/lib/rigor/sig_gen/observation_collector.rb +17 -15
- data/lib/rigor/sig_gen/path_mapper.rb +4 -6
- data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
- data/lib/rigor/sig_gen/renderer.rb +17 -11
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +111 -0
- data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
- data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
- data/lib/rigor/sig_gen/writer.rb +23 -6
- data/lib/rigor/signature_path_audit.rb +153 -6
- data/lib/rigor/source/literals.rb +0 -23
- data/lib/rigor/source/node_children.rb +0 -2
- data/lib/rigor/source/node_locator.rb +5 -11
- data/lib/rigor/source/node_walker.rb +2 -5
- data/lib/rigor/triage/catalogue.rb +1 -3
- data/lib/rigor/triage.rb +3 -5
- data/lib/rigor/type/accepts_result.rb +24 -6
- data/lib/rigor/type/combinator.rb +68 -5
- data/lib/rigor/type/data_class.rb +2 -2
- data/lib/rigor/type/data_instance.rb +4 -4
- data/lib/rigor/type/float_range.rb +128 -0
- data/lib/rigor/type/hash_shape.rb +5 -5
- data/lib/rigor/type/integer_range.rb +13 -8
- data/lib/rigor/type/nominal.rb +8 -2
- data/lib/rigor/type/refined.rb +3 -3
- data/lib/rigor/type/struct_class.rb +3 -3
- data/lib/rigor/type/struct_instance.rb +4 -4
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/generic.rb +1 -1
- data/lib/rigor/type_node/range_literal.rb +25 -0
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node.rb +1 -0
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -0
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +1 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +33 -30
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +28 -2
- data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +1 -0
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +3 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +42 -7
- data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +1 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +119 -14
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +1 -0
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
- data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +6 -3
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +0 -3
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +1 -0
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +1 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +3 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -0
- data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
- data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +128 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
- data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +1 -0
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +5 -5
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
- data/sig/rigor/cache.rbs +2 -3
- data/sig/rigor/environment.rbs +11 -0
- data/sig/rigor/inference.rbs +3 -10
- data/sig/rigor/plugin/io_boundary.rbs +4 -0
- data/sig/rigor/plugin/manifest.rbs +2 -1
- data/sig/rigor/reflection.rbs +3 -1
- data/sig/rigor/scope.rbs +24 -3
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -0
- data/sig/rigor/type.rbs +27 -1
- data/sig/rigor.rbs +1 -0
- data/skills/rigor-ask/SKILL.md +8 -6
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +13 -2
- data/skills/rigor-project-init/SKILL.md +6 -3
- data/skills/rigor-project-init/references/02-configure.md +10 -6
- data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
- data/skills/rigor-type-oracle/SKILL.md +228 -0
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +261 -0
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +128 -0
- data/skills/rigor-unused-adjudicate/SKILL.md +8 -5
- metadata +28 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require "yaml"
|
|
4
4
|
|
|
5
|
+
require_relative "../plugin_gap_advisory"
|
|
6
|
+
|
|
5
7
|
module Rigor
|
|
6
8
|
class CLI
|
|
7
9
|
# The presence-only project-state probe behind `rigor skill describe` (ADR-73 WD2). It stats files and opens only
|
|
@@ -17,19 +19,11 @@ module Rigor
|
|
|
17
19
|
# RBS into the signature paths, so its presence is the signal that the gem-RBS gap has been addressed.
|
|
18
20
|
RBS_COLLECTION_LOCKFILE = "rbs_collection.lock.yaml"
|
|
19
21
|
|
|
20
|
-
# `Gemfile.lock` substrings that mark a Rails app, and the bundled Rails-family plugin ids — used to spot a
|
|
21
|
-
# configured Rails project that has not enabled any Rails plugin (a `rigor-plugin-tune` cue).
|
|
22
|
-
RAILS_LOCK_MARKERS = %w[railties actionpack activerecord actioncable].freeze
|
|
23
|
-
RAILS_PLUGIN_MARKERS = %w[
|
|
24
|
-
rigor-activerecord rigor-actionpack rigor-actionmailer rigor-activejob
|
|
25
|
-
rigor-rails-routes rigor-rails-i18n rigor-actioncable rigor-activestorage rigor-rails
|
|
26
|
-
].freeze
|
|
27
|
-
|
|
28
22
|
def initialize(root)
|
|
29
23
|
@root = root
|
|
30
24
|
end
|
|
31
25
|
|
|
32
|
-
# @return
|
|
26
|
+
# @return the presence-only state the recommendation routes on.
|
|
33
27
|
def to_h
|
|
34
28
|
config = CONFIG_FILENAMES.find { |name| File.file?(File.join(@root, name)) }
|
|
35
29
|
{
|
|
@@ -38,7 +32,7 @@ module Rigor
|
|
|
38
32
|
sig: File.directory?(File.join(@root, "sig")),
|
|
39
33
|
gems: File.file?(File.join(@root, "Gemfile.lock")),
|
|
40
34
|
rbs_collection: File.file?(File.join(@root, RBS_COLLECTION_LOCKFILE)),
|
|
41
|
-
|
|
35
|
+
plugins_unconfigured: plugins_unconfigured?(config),
|
|
42
36
|
ci: ci_state,
|
|
43
37
|
editor: editor_state,
|
|
44
38
|
mcp: mcp_state
|
|
@@ -47,24 +41,26 @@ module Rigor
|
|
|
47
41
|
|
|
48
42
|
private
|
|
49
43
|
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
|
|
44
|
+
# ADR-96 WD2 — true when the project depends on gems that bundled plugins model and enables none of those
|
|
45
|
+
# plugins, so `rigor-plugin-tune` buys more than community RBS would (the 20260620 field trial's strap
|
|
46
|
+
# case). Reads each plugin's `target_gems:` rather than the Rails-only table it replaces, so the cue
|
|
47
|
+
# covers every framework Rigor ships a plugin for. Only fires on an already-configured project; an
|
|
48
|
+
# un-configured app routes to `rigor-project-init`, which selects the plugins itself.
|
|
49
|
+
def plugins_unconfigured?(config)
|
|
55
50
|
return false if config.nil?
|
|
56
51
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
!file_mentions_any?(File.join(@root, config), RAILS_PLUGIN_MARKERS)
|
|
52
|
+
plugins = configured_plugins(File.join(@root, config))
|
|
53
|
+
PluginGapAdvisory.unconfigured?(project_root: @root, plugins: plugins)
|
|
61
54
|
end
|
|
62
55
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
56
|
+
# The probe reads the config file itself rather than {Rigor::Configuration}: it must stay a
|
|
57
|
+
# presence-only probe, and `Configuration.load` applies defaults (including the ADR-93 auto-wired
|
|
58
|
+
# `rigor-rbs-inline` entry) that have nothing to do with the question asked here.
|
|
59
|
+
def configured_plugins(path)
|
|
60
|
+
data = YAML.safe_load_file(path, permitted_classes: [], aliases: true)
|
|
61
|
+
data.is_a?(Hash) ? Array(data["plugins"]) : []
|
|
66
62
|
rescue StandardError
|
|
67
|
-
|
|
63
|
+
[]
|
|
68
64
|
end
|
|
69
65
|
|
|
70
66
|
# `:wired` (a CI config mentions `rigor`), `:unwired` (a CI config exists but does not), or `:none`.
|
|
@@ -121,9 +117,9 @@ module Rigor
|
|
|
121
117
|
# The entry-point SKILL itself — excluded from the catalogue because it is the skill being run, not a destination.
|
|
122
118
|
ENTRY_POINT_SKILL = "rigor-next-steps"
|
|
123
119
|
|
|
124
|
-
# Adoption-journey order for the catalogue and the order the recommendation decision tree walks.
|
|
125
|
-
#
|
|
126
|
-
#
|
|
120
|
+
# Adoption-journey order for the catalogue and the order the recommendation decision tree walks. The last two are
|
|
121
|
+
# journey-agnostic companions the agent can offer at any point, never presence-recommended steps:
|
|
122
|
+
# `rigor-type-oracle` fires on an event (being about to write a type), and `rigor-ask` on a question about Rigor.
|
|
127
123
|
CATALOG_ORDER = %w[
|
|
128
124
|
rigor-project-init
|
|
129
125
|
rigor-rbs-setup
|
|
@@ -137,12 +133,13 @@ module Rigor
|
|
|
137
133
|
rigor-plugin-author
|
|
138
134
|
rigor-upgrade
|
|
139
135
|
rigor-doctor
|
|
136
|
+
rigor-type-oracle
|
|
140
137
|
rigor-ask
|
|
141
138
|
].freeze
|
|
142
139
|
|
|
143
|
-
# @param skills
|
|
144
|
-
# @param root
|
|
145
|
-
# @param deep
|
|
140
|
+
# @param skills — discovered skills, each `{name:, path:}`.
|
|
141
|
+
# @param root — project root to probe (defaults to the cwd).
|
|
142
|
+
# @param deep — opt into `--deep`: run a real `rigor check` and let its result pick the headline. Off by
|
|
146
143
|
# default, and off is ADR-73 WD2's contract — see {SkillDeepProbe} for why the whole analysis path lives
|
|
147
144
|
# behind this one flag and in its own file.
|
|
148
145
|
def initialize(skills:, root: Dir.pwd, deep: false)
|
|
@@ -151,7 +148,7 @@ module Rigor
|
|
|
151
148
|
@deep = deep
|
|
152
149
|
end
|
|
153
150
|
|
|
154
|
-
# @return
|
|
151
|
+
# @return the full describe report.
|
|
155
152
|
def render
|
|
156
153
|
catalog = catalog_skills
|
|
157
154
|
state = ProjectStateProbe.new(@root).to_h
|
|
@@ -202,14 +199,18 @@ module Rigor
|
|
|
202
199
|
def recommended_name_and_reason(state)
|
|
203
200
|
if state.fetch(:config).nil?
|
|
204
201
|
["rigor-project-init", "this project has no Rigor configuration yet — start here."]
|
|
205
|
-
elsif state.fetch(:
|
|
202
|
+
elsif state.fetch(:plugins_unconfigured)
|
|
206
203
|
["rigor-plugin-tune",
|
|
207
|
-
"
|
|
208
|
-
"
|
|
209
|
-
|
|
210
|
-
|
|
204
|
+
"your Gemfile declares gems Rigor ships plugins for and none is enabled — wire them so " \
|
|
205
|
+
"those framework calls resolve (a bigger win here than community RBS)."]
|
|
206
|
+
# ADR-73's second `rbs-setup` priority-softening case (2026-09-10): CI wiring is presence-only and
|
|
207
|
+
# side-effect-free, while community-RBS install is network-bound (`rbs collection install` hits
|
|
208
|
+
# rubygems.org). A configured project earns the cheap, local win first — checked ahead of the RBS
|
|
209
|
+
# gap rather than after it.
|
|
211
210
|
elsif state.fetch(:ci) != :wired
|
|
212
211
|
["rigor-ci-setup", "Rigor is configured but not wired into CI — lock in the regression guard."]
|
|
212
|
+
elsif state.fetch(:gems) && !state.fetch(:rbs_collection)
|
|
213
|
+
["rigor-rbs-setup", "your gems ship no community RBS yet — install it so Rigor stops typing them as Dynamic."]
|
|
213
214
|
# A present baseline is deliberately NOT a recommendation trigger. A baseline is a healthy, finished onboarding
|
|
214
215
|
# state, not a problem to work off; pushing every baselined project to "reduce it" turns a working build into a
|
|
215
216
|
# chore and tempts scattering `# rigor:disable` through the code to make a number go down — means over ends.
|
|
@@ -12,7 +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 "
|
|
15
|
+
require_relative "../project_environment"
|
|
16
16
|
|
|
17
17
|
module Rigor
|
|
18
18
|
class CLI
|
|
@@ -30,7 +30,7 @@ module Rigor
|
|
|
30
30
|
DEFAULT_KINDS = %i[bind union dispatch].freeze
|
|
31
31
|
VERBOSE_KINDS = (%i[enter result] + DEFAULT_KINDS).freeze
|
|
32
32
|
|
|
33
|
-
# @return
|
|
33
|
+
# @return CLI exit status.
|
|
34
34
|
def run
|
|
35
35
|
options = parse_options
|
|
36
36
|
file = @argv.first
|
|
@@ -92,10 +92,10 @@ module Rigor
|
|
|
92
92
|
end
|
|
93
93
|
|
|
94
94
|
# Mirrors the single-file path `rigor type-of` takes: the plugin-aware environment (so a trace replays the
|
|
95
|
-
# same dispatch `rigor check` would, including plugin-synthesized RBS — see {
|
|
95
|
+
# same dispatch `rigor check` would, including plugin-synthesized RBS — see {ProjectEnvironment}), an empty
|
|
96
96
|
# seed scope, one statement-level evaluation of the whole program — but recorded under the FlowTracer.
|
|
97
97
|
def record_events(root, file, configuration)
|
|
98
|
-
environment =
|
|
98
|
+
environment = ProjectEnvironment.build(configuration: configuration, source_files: [file])
|
|
99
99
|
scope = Scope.empty(environment: environment, source_path: file)
|
|
100
100
|
Inference::FlowTracer.record { scope.evaluate(root) }
|
|
101
101
|
end
|
|
@@ -28,9 +28,8 @@ module Rigor
|
|
|
28
28
|
BODY_HEIGHT_MIN = 3 # never shrink the source window below this
|
|
29
29
|
DEFAULT_SIZE = [24, 80].freeze
|
|
30
30
|
|
|
31
|
-
# @param
|
|
32
|
-
# @param
|
|
33
|
-
# @param file [String] display path.
|
|
31
|
+
# @param source — the traced file's source.
|
|
32
|
+
# @param file — display path.
|
|
34
33
|
def initialize(out:, source:, file:)
|
|
35
34
|
@out = out
|
|
36
35
|
@source = source
|
|
@@ -38,11 +37,11 @@ module Rigor
|
|
|
38
37
|
@lines = source.lines.map(&:chomp)
|
|
39
38
|
end
|
|
40
39
|
|
|
41
|
-
# @param events
|
|
40
|
+
# @param events — the
|
|
42
41
|
# pre-filtered frame list (the command owns kind filtering).
|
|
43
|
-
# @param delay
|
|
42
|
+
# @param delay — seconds between frames (autoplay);
|
|
44
43
|
# nil = step on key press when interactive.
|
|
45
|
-
# @param interactive
|
|
44
|
+
# @param interactive — whether to clear/redraw and wait.
|
|
46
45
|
def play(events, delay: nil, interactive: false)
|
|
47
46
|
@rows, @cols = interactive ? terminal_size : DEFAULT_SIZE
|
|
48
47
|
@interactive = interactive
|
|
@@ -22,7 +22,7 @@ module Rigor
|
|
|
22
22
|
USAGE = "Usage: rigor triage [options] [paths]"
|
|
23
23
|
DEFAULT_SECTIONS = %i[distribution selectors hotspots hints].freeze
|
|
24
24
|
|
|
25
|
-
# @return
|
|
25
|
+
# @return CLI exit status (always 0).
|
|
26
26
|
def run
|
|
27
27
|
options = parse_options
|
|
28
28
|
configuration = Configuration.load(options.fetch(:config))
|
|
@@ -15,7 +15,7 @@ require_relative "../source/node_children"
|
|
|
15
15
|
require_relative "type_of_renderer"
|
|
16
16
|
require_relative "command"
|
|
17
17
|
require_relative "options"
|
|
18
|
-
require_relative "
|
|
18
|
+
require_relative "../project_environment"
|
|
19
19
|
|
|
20
20
|
module Rigor
|
|
21
21
|
class CLI
|
|
@@ -46,7 +46,7 @@ module Rigor
|
|
|
46
46
|
LINE_ENUMERATION_CAP = 40
|
|
47
47
|
private_constant :LINE_ENUMERATION_CAP
|
|
48
48
|
|
|
49
|
-
# @return
|
|
49
|
+
# @return CLI exit status.
|
|
50
50
|
def run
|
|
51
51
|
options = parse_options
|
|
52
52
|
buffer = Options.resolve_buffer_binding(options, err: @err)
|
|
@@ -111,8 +111,12 @@ module Rigor
|
|
|
111
111
|
return 1 if parse_errors?(parse_result, file)
|
|
112
112
|
|
|
113
113
|
# Built with no tracer attached — it would otherwise double-record fallback events with the per-node
|
|
114
|
-
# `type_of` calls below.
|
|
115
|
-
|
|
114
|
+
# `type_of` calls below. `with_source_path(file)` (like `annotate` and `trace` already do) matters
|
|
115
|
+
# beyond diagnostics attribution: a plugin `dynamic_return file_methods:` rule (ADR-52 slice 5a,
|
|
116
|
+
# e.g. rigor-rspec's `let`-binding resolver, #921) gates on `scope.source_path` and fails closed
|
|
117
|
+
# when it is nil — an unset source_path silently declined every such rule under this command.
|
|
118
|
+
scope_index = Inference::ScopeIndexer.index(parse_result.value,
|
|
119
|
+
default_scope: base_scope.with_source_path(file))
|
|
116
120
|
locator = Source::NodeLocator.new(source: source, root: parse_result.value)
|
|
117
121
|
lines = indexed_targets.filter_map { |target, _index| target.line if target.column.nil? }
|
|
118
122
|
line_nodes, line_totals = collect_line_nodes(parse_result.value, lines)
|
|
@@ -196,12 +200,12 @@ module Rigor
|
|
|
196
200
|
|
|
197
201
|
# Builds the plugin-aware environment relative to the probed file, so the reported type matches what `rigor
|
|
198
202
|
# check` computes for the same position — including types synthesized from inline RBS annotations by the
|
|
199
|
-
# ADR-93 auto-wired `rigor-rbs-inline` plugin (see {
|
|
203
|
+
# ADR-93 auto-wired `rigor-rbs-inline` plugin (see {ProjectEnvironment} for the #162 misattribution this
|
|
200
204
|
# closes). The probed file is threaded as the synthesizer's `source_files:`. Project-RBS auto-detection
|
|
201
205
|
# roots at CWD today; future work will walk parent directories to find the enclosing `Gemfile`/`*.gemspec`
|
|
202
206
|
# so probes against files outside the current process's CWD still see the right `sig/` tree.
|
|
203
207
|
def project_environment(files, configuration)
|
|
204
|
-
|
|
208
|
+
ProjectEnvironment.build(configuration: configuration, source_files: files)
|
|
205
209
|
end
|
|
206
210
|
|
|
207
211
|
def file_exists?(file)
|
|
@@ -11,7 +11,7 @@ require_relative "../scope"
|
|
|
11
11
|
require_relative "type_scan_renderer"
|
|
12
12
|
require_relative "type_scan_report"
|
|
13
13
|
require_relative "command"
|
|
14
|
-
require_relative "
|
|
14
|
+
require_relative "../project_environment"
|
|
15
15
|
require_relative "coverage_scan"
|
|
16
16
|
|
|
17
17
|
module Rigor
|
|
@@ -26,7 +26,7 @@ module Rigor
|
|
|
26
26
|
|
|
27
27
|
LocatedEvent = Data.define(:file, :event)
|
|
28
28
|
|
|
29
|
-
# @return
|
|
29
|
+
# @return CLI exit status.
|
|
30
30
|
def run
|
|
31
31
|
options = parse_options
|
|
32
32
|
paths = collect_paths(@argv, command_name: "type-scan")
|
|
@@ -89,9 +89,9 @@ module Rigor
|
|
|
89
89
|
# Builds the plugin-aware environment that auto-detects `<cwd>/sig` by default and honours the
|
|
90
90
|
# configuration's `libraries:` / `signature_paths:` keys when present. The scanned `paths` are threaded as
|
|
91
91
|
# the plugin `source_rbs_synthesizer` inputs so coverage reflects the same synthesized RBS `rigor check`
|
|
92
|
-
# sees (see {
|
|
92
|
+
# sees (see {ProjectEnvironment}).
|
|
93
93
|
def project_environment(configuration, source_files)
|
|
94
|
-
|
|
94
|
+
ProjectEnvironment.build(configuration: configuration, source_files: source_files)
|
|
95
95
|
end
|
|
96
96
|
|
|
97
97
|
def scan_one(path, scanner, accumulator, configuration)
|
|
@@ -14,7 +14,7 @@ require_relative "../analysis/reachability/project_files"
|
|
|
14
14
|
require_relative "../cache/store"
|
|
15
15
|
require_relative "options"
|
|
16
16
|
require_relative "command"
|
|
17
|
-
require_relative "
|
|
17
|
+
require_relative "../project_environment"
|
|
18
18
|
|
|
19
19
|
module Rigor
|
|
20
20
|
class CLI
|
|
@@ -219,15 +219,23 @@ module Rigor
|
|
|
219
219
|
# declaration, which produced three of redmine's artifacts from a single initializer. A name the bundled
|
|
220
220
|
# (non-project) environment already knows is not ours to call unused. The project's own `sig/` is
|
|
221
221
|
# deliberately excluded from this environment, so a project class that ships a signature stays owned.
|
|
222
|
+
# That exclusion is the ONLY thing withheld: the dependency-discovery axes (issue #882, the shape of
|
|
223
|
+
# #821) must be passed, because "outside the project" is exactly what they describe. Without the
|
|
224
|
+
# bundle's per-gem `sig/` and an installed `rbs collection`, a gem-declared class the project reopens
|
|
225
|
+
# is unknown to this environment, reads as project-owned, and lands in the candidate list — a false
|
|
226
|
+
# positive in a report whose measured precision cannot afford one.
|
|
222
227
|
#
|
|
223
228
|
# The cache store makes the environment a Marshal restore instead of a cold RBS build on every
|
|
224
229
|
# invocation — the same ADR-54 slot `rigor check` reads, keyed apart by this environment's own
|
|
225
|
-
# (sig-less) descriptor. The
|
|
230
|
+
# (sig-less) descriptor. The discovered bundle and collection directories ride in the loader's
|
|
231
|
+
# signature paths, so the descriptor digests them and this slot still cannot be served a run whose
|
|
232
|
+
# dependency sources differ. The report only ever asks "is this name known", which the cached
|
|
226
233
|
# environment answers identically: the one thing the cache degrades is `RBS::Location`, and no
|
|
227
234
|
# location is read here.
|
|
228
235
|
def foreign_predicate(configuration)
|
|
229
236
|
env = Environment.for_project(libraries: configuration.libraries, signature_paths: [],
|
|
230
|
-
cache_store: cache_store(configuration)
|
|
237
|
+
cache_store: cache_store(configuration),
|
|
238
|
+
**ProjectEnvironment.dependency_discovery_options(configuration))
|
|
231
239
|
->(fqn) { !env.singleton_for_name(fqn).nil? }
|
|
232
240
|
rescue StandardError
|
|
233
241
|
->(_fqn) { false }
|
|
@@ -13,7 +13,7 @@ module Rigor
|
|
|
13
13
|
class UpgradeCommand < Command
|
|
14
14
|
USAGE = "Usage: rigor upgrade [options]"
|
|
15
15
|
|
|
16
|
-
# @return
|
|
16
|
+
# @return CLI exit status.
|
|
17
17
|
def run
|
|
18
18
|
@out.puts("rigor upgrade: No migration target available yet (ADR-50 WD7, queued).")
|
|
19
19
|
@out.puts("Current version: #{Rigor::VERSION}")
|
data/lib/rigor/cli.rb
CHANGED
|
@@ -12,6 +12,7 @@ require_relative "analysis/result"
|
|
|
12
12
|
require_relative "cli/options"
|
|
13
13
|
require_relative "cli/diagnostic_formats"
|
|
14
14
|
require_relative "ci_detector"
|
|
15
|
+
require_relative "analysis/check_rules/rule_ids"
|
|
15
16
|
|
|
16
17
|
module Rigor
|
|
17
18
|
# The CLI class is a dispatcher: each `run_*` method delegates to a command-specific class once the command grows
|
|
@@ -20,6 +21,21 @@ module Rigor
|
|
|
20
21
|
class CLI # rubocop:disable Metrics/ClassLength
|
|
21
22
|
EXIT_USAGE = 64
|
|
22
23
|
|
|
24
|
+
# Issue [#609](https://github.com/rigortype/rigor/issues/609) — a run the analyzer could not finish.
|
|
25
|
+
# Distinct from `1` (the analysis ran and reported diagnostics) so a CI step can tell "your code has
|
|
26
|
+
# findings" from "Rigor died and this report is not an account of anything".
|
|
27
|
+
EXIT_INTERNAL_ERROR = 70
|
|
28
|
+
|
|
29
|
+
# The exceptions that mean "the process is out of a resource, and the backtrace names nothing
|
|
30
|
+
# actionable": `SystemStackError` from an ancestry walk that never terminates, `NoMemoryError` from a
|
|
31
|
+
# runaway allocation. Neither is a `StandardError`, so every rescue between here and the raise site
|
|
32
|
+
# declines them by design — the whole point of those rescues is to convert a per-file or per-plugin
|
|
33
|
+
# failure into a diagnostic, and a process running out of stack is not per-file. Left uncaught they
|
|
34
|
+
# abort with Ruby's own ~40-frame dump, which is non-zero (so a CI gate does catch it) but says
|
|
35
|
+
# neither that Rigor is at fault nor that the report on disk is empty. `Interrupt`, `SignalException`
|
|
36
|
+
# and `SystemExit` stay out: those are the user's answer, not a failure.
|
|
37
|
+
FATAL_ERRORS = [SystemStackError, NoMemoryError].freeze
|
|
38
|
+
|
|
23
39
|
# The published location of `schemas/rigor-config.schema.json`, written into the `.rigor.yml` that
|
|
24
40
|
# `rigor init` generates so an editor validates the file as the user types.
|
|
25
41
|
#
|
|
@@ -95,10 +111,28 @@ module Rigor
|
|
|
95
111
|
# register presets have loaded) would each need their own rescue at their own point.
|
|
96
112
|
@err.puts("rigor: #{e.message}")
|
|
97
113
|
EXIT_USAGE
|
|
114
|
+
rescue *FATAL_ERRORS => e
|
|
115
|
+
report_fatal_error(e)
|
|
116
|
+
EXIT_INTERNAL_ERROR
|
|
98
117
|
end
|
|
99
118
|
|
|
100
119
|
private
|
|
101
120
|
|
|
121
|
+
# One `rigor:` line naming the failure, then the innermost frames — enough to route a bug report,
|
|
122
|
+
# without the full dump the raise would otherwise print. The "no report" sentence is the part a user
|
|
123
|
+
# acts on: the crash usually happens with a redirect in place, so the artifact a CI step goes on to
|
|
124
|
+
# read is an empty file rather than a missing one.
|
|
125
|
+
def report_fatal_error(error)
|
|
126
|
+
@err.puts("rigor: analysis aborted: #{error.class}: #{error.message}")
|
|
127
|
+
Array(error.backtrace).first(FATAL_BACKTRACE_FRAMES).each { |frame| @err.puts(" from #{frame}") }
|
|
128
|
+
@err.puts(" Rigor could not finish this run, so its report is EMPTY rather than clean — do not")
|
|
129
|
+
@err.puts(" read this exit status as a passing check.")
|
|
130
|
+
@err.puts(" This is a defect in Rigor; please report it at https://github.com/rigortype/rigor/issues.")
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
FATAL_BACKTRACE_FRAMES = 5
|
|
134
|
+
private_constant :FATAL_BACKTRACE_FRAMES
|
|
135
|
+
|
|
102
136
|
def dispatch(command)
|
|
103
137
|
handler = HANDLERS[command]
|
|
104
138
|
return arm_jit_deadline { send(handler) } if handler
|
|
@@ -169,6 +203,30 @@ module Rigor
|
|
|
169
203
|
@out.puts " 3. Run `rigor check` to analyse your code."
|
|
170
204
|
end
|
|
171
205
|
|
|
206
|
+
# Word-wraps `CheckRules::ALL_RULES` into a comma-separated, `#`-commented block for `init_template`, so the
|
|
207
|
+
# listed rule ids cannot drift from the catalogue the way a hand-picked subset did (#920) — every rule and only
|
|
208
|
+
# a rule actually in `ALL_RULES` gets named here.
|
|
209
|
+
def rule_ids_comment
|
|
210
|
+
prefix = "# "
|
|
211
|
+
max_width = 78 - prefix.length
|
|
212
|
+
ids = Analysis::CheckRules::ALL_RULES
|
|
213
|
+
lines = []
|
|
214
|
+
current = +""
|
|
215
|
+
ids.each_with_index do |id, index|
|
|
216
|
+
token = "#{id}#{index == ids.length - 1 ? '.' : ','}"
|
|
217
|
+
if current.empty?
|
|
218
|
+
current = token
|
|
219
|
+
elsif current.length + 1 + token.length <= max_width
|
|
220
|
+
current << " " << token
|
|
221
|
+
else
|
|
222
|
+
lines << current
|
|
223
|
+
current = token
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
lines << current unless current.empty?
|
|
227
|
+
lines.map { |line| "#{prefix}#{line}" }.join("\n")
|
|
228
|
+
end
|
|
229
|
+
|
|
172
230
|
# Renders the starter `.rigor.yml` body. The template serialises `Configuration::DEFAULTS` (so the on-disk file
|
|
173
231
|
# round-trips through `Configuration.load`) and prepends a short header that points the user at the keys they are
|
|
174
232
|
# most likely to want to edit.
|
|
@@ -187,11 +245,10 @@ module Rigor
|
|
|
187
245
|
# See https://github.com/rigortype/rigor/tree/master/plugins
|
|
188
246
|
# for production plugins (rigor-activerecord, rigor-sorbet, …).
|
|
189
247
|
# - disable: list of `rigor check` rule identifiers to
|
|
190
|
-
# silence project-wide.
|
|
191
|
-
#
|
|
192
|
-
#
|
|
193
|
-
#
|
|
194
|
-
# assert.type-mismatch, flow.always-raises.
|
|
248
|
+
# silence project-wide. Every rule id Rigor
|
|
249
|
+
# ships (run `rigor explain <rule>` for what
|
|
250
|
+
# each one catches):
|
|
251
|
+
#{rule_ids_comment}
|
|
195
252
|
# A bare family token (`call`, `flow`,
|
|
196
253
|
# `assert`, `dump`, `def`) wildcards every
|
|
197
254
|
# rule under that prefix. Legacy unprefixed
|
|
@@ -320,7 +377,10 @@ module Rigor
|
|
|
320
377
|
@err.puts "Install it with: gem install rigor-playground"
|
|
321
378
|
return EXIT_USAGE
|
|
322
379
|
end
|
|
323
|
-
|
|
380
|
+
# `run` shifts the verb off `@argv` before dispatching, so the handler receives the arguments
|
|
381
|
+
# already. Slicing here dropped the first one — `rigor playground --port=4000` served the
|
|
382
|
+
# default port, and a bare `rigor playground` passed `nil` into a command that iterates it.
|
|
383
|
+
Rigor::CLI::PlaygroundCommand.new(@argv, @out, @err).run
|
|
324
384
|
end
|
|
325
385
|
|
|
326
386
|
def run_skill
|
|
@@ -383,11 +443,13 @@ module Rigor
|
|
|
383
443
|
(opt-in; effects update/check/diff/explain)
|
|
384
444
|
explain Print the description of one or all CheckRules
|
|
385
445
|
diff Compare current diagnostics to a saved baseline JSON
|
|
446
|
+
baseline Manage the baseline file (baseline generate/regenerate/dump/drift/prune)
|
|
386
447
|
sig-gen Emit RBS skeletons inferred from .rb sources
|
|
387
448
|
lsp Run the Rigor Language Server (LSP) over stdio
|
|
388
449
|
mcp Run the Rigor MCP server over stdio
|
|
389
450
|
triage Summarise diagnostics: distribution, hotspots, hints
|
|
390
451
|
coverage Report type-precision coverage (precise vs Dynamic ratio)
|
|
452
|
+
unused Report unreferenced classes, modules and constants as removal candidates
|
|
391
453
|
plugins Report activation status of every configured plugin
|
|
392
454
|
plugin Browse bundled plugin source as worked examples (list/path/print/root)
|
|
393
455
|
playground Start the browser playground (requires rigor-playground gem)
|
data/lib/rigor/config_audit.rb
CHANGED
|
@@ -18,6 +18,11 @@ module Rigor
|
|
|
18
18
|
# never written. This module surfaces each such entry up front so the cause is visible
|
|
19
19
|
# instead of inferred.
|
|
20
20
|
#
|
|
21
|
+
# {.bundled_plugin_signature_path_warnings} widens that by one step, to a value that
|
|
22
|
+
# resolves to *part* of what it looks like. It belongs here because the symptom is the
|
|
23
|
+
# same one — a cluster of `call.undefined-method` on working code, traceable to a config
|
|
24
|
+
# line nothing in the output names.
|
|
25
|
+
#
|
|
21
26
|
# Every check is held to the same bar that {SignaturePathAudit} set: it mirrors the
|
|
22
27
|
# loader's own acceptance test, so a warning means the loader really did load nothing, and
|
|
23
28
|
# it never fires on a setup that works. In particular the rule-token check only flags a
|
|
@@ -27,8 +32,8 @@ module Rigor
|
|
|
27
32
|
# a typo.
|
|
28
33
|
module ConfigAudit
|
|
29
34
|
# One config-level finding. `kind` discriminates the source key (`:signature_path`,
|
|
30
|
-
# `:library`, `:disabled_rule`, `:severity_override`,
|
|
31
|
-
# `:bundler_lockfile`, `:rbs_collection_lockfile`); `fields` carries the kind-specific
|
|
35
|
+
# `:bundled_plugin_signature_path`, `:library`, `:disabled_rule`, `:severity_override`,
|
|
36
|
+
# `:bundler_bundle_path`, `:bundler_lockfile`, `:rbs_collection_lockfile`); `fields` carries the kind-specific
|
|
32
37
|
# structured data merged into {#to_h} for JSON consumers.
|
|
33
38
|
Warning = Data.define(:kind, :message, :fields) do
|
|
34
39
|
def to_h
|
|
@@ -36,13 +41,12 @@ module Rigor
|
|
|
36
41
|
end
|
|
37
42
|
end
|
|
38
43
|
|
|
39
|
-
# @param
|
|
40
|
-
# @param project_root [String] the directory the run's relative bundler / collection
|
|
44
|
+
# @param project_root — the directory the run's relative bundler / collection
|
|
41
45
|
# paths resolve against (the CLI's CWD), used only by the explicit-path checks.
|
|
42
|
-
# @return [Array<Warning>]
|
|
43
46
|
def self.warnings(configuration, project_root: Dir.pwd)
|
|
44
47
|
unknown_key_warnings(configuration) +
|
|
45
48
|
signature_path_warnings(configuration) +
|
|
49
|
+
bundled_plugin_signature_path_warnings(configuration) +
|
|
46
50
|
library_warnings(configuration) +
|
|
47
51
|
rule_token_warnings(configuration) +
|
|
48
52
|
explicit_path_warnings(configuration, project_root)
|
|
@@ -100,6 +104,27 @@ module Rigor
|
|
|
100
104
|
end
|
|
101
105
|
end
|
|
102
106
|
|
|
107
|
+
# The one member of this module whose subject is a configured value that resolves to
|
|
108
|
+
# *something*: a `signature_paths:` entry loading a bundled plugin's own `sig/` while
|
|
109
|
+
# `plugins:` never names that plugin (issue #697). The RBS arrives, its manifest does not,
|
|
110
|
+
# and the declarations that manifest marks deliberately partial (ADR-26 `open_receivers:`)
|
|
111
|
+
# are then read as complete — so the symptom is this module's usual one, a cluster of
|
|
112
|
+
# high-confidence `call.undefined-method` firings on code that runs, with nothing pointing
|
|
113
|
+
# at the config line that caused them.
|
|
114
|
+
#
|
|
115
|
+
# It is a warning and not a fix on purpose. Teaching the check rules to recognise this
|
|
116
|
+
# route would be a fourth way for a class to be open-receiver protected, and settling
|
|
117
|
+
# where that membership belongs is [#660](https://github.com/rigortype/rigor/issues/660)'s
|
|
118
|
+
# question; until it is answered, the honest move is to make the misconfiguration loud
|
|
119
|
+
# rather than to grow the mechanism. The false positive itself still fires.
|
|
120
|
+
def self.bundled_plugin_signature_path_warnings(configuration)
|
|
121
|
+
routes = SignaturePathAudit.bundled_plugin_routes(configuration.signature_paths, configuration.plugins)
|
|
122
|
+
routes.map do |route|
|
|
123
|
+
Warning.new(kind: :bundled_plugin_signature_path, message: route.message,
|
|
124
|
+
fields: { "path" => route.path, "gem" => route.gem })
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
103
128
|
# `libraries:` entries RBS does not recognise. Uses the same
|
|
104
129
|
# `RBS::EnvironmentLoader#has_library?` guard the loader filters through
|
|
105
130
|
# ({Environment::RbsLoader} `build_env_for`), so a flagged name is exactly one the loader
|
|
@@ -167,20 +167,20 @@ module Rigor
|
|
|
167
167
|
# Resolves the configured severity for a diagnostic given the active profile and any per-rule
|
|
168
168
|
# overrides.
|
|
169
169
|
#
|
|
170
|
-
# @param rule
|
|
171
|
-
# @param authored_severity
|
|
170
|
+
# @param rule — canonical rule id (`call.undefined-method`).
|
|
171
|
+
# @param authored_severity — severity the rule emitted the diagnostic with (`:error`, `:warning`,
|
|
172
172
|
# `:info`).
|
|
173
|
-
# @param profile
|
|
174
|
-
# @param overrides
|
|
173
|
+
# @param profile — one of {VALID_PROFILES}; falls back to {DEFAULT_PROFILE} for unknown values.
|
|
174
|
+
# @param overrides — per-rule severity overrides from `.rigor.yml`'s
|
|
175
175
|
# `severity_overrides:` map. Keys are canonical rule ids; values are {VALID_SEVERITIES} symbols.
|
|
176
176
|
# Family-wildcard keys (`call`) match every rule under that prefix.
|
|
177
|
-
# @param bleeding_edge_overrides
|
|
177
|
+
# @param bleeding_edge_overrides — the severity map imposed by the active ADR-50
|
|
178
178
|
# § WD2 bleeding-edge features ({Rigor::BleedingEdge.severity_overrides_for}). Consulted *below* the
|
|
179
179
|
# user's own `overrides` (so an explicit `severity_overrides:` entry, exact or family wildcard,
|
|
180
180
|
# always wins) and *above* the profile table. Exact rule ids only — the overlay never carries family
|
|
181
181
|
# wildcards. Empty while the overlay is unpopulated, so the default leaves resolution bit-for-bit
|
|
182
182
|
# unchanged.
|
|
183
|
-
# @return
|
|
183
|
+
# @return the resolved severity. Returns `:off` to mean "drop the diagnostic entirely".
|
|
184
184
|
def resolve(rule:, authored_severity:, profile: DEFAULT_PROFILE, overrides: {}, bleeding_edge_overrides: {})
|
|
185
185
|
return authored_severity if rule.nil?
|
|
186
186
|
|