rigortype 0.3.8 → 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 +72 -1
- 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 +32 -4
- data/docs/manual/03-configuration.md +11 -0
- data/docs/manual/04-diagnostics.md +11 -0
- 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 +8 -24
- 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 +81 -22
- 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 +177 -26
- 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 +256 -88
- data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
- data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
- data/lib/rigor/analysis/runner.rb +133 -35
- data/lib/rigor/analysis/worker_session.rb +9 -10
- data/lib/rigor/bleeding_edge.rb +12 -24
- data/lib/rigor/builtins/hkt_builtins.rb +2 -2
- data/lib/rigor/builtins/imported_refinements.rb +155 -21
- 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 +2 -4
- 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_known_class_names.rb +0 -3
- data/lib/rigor/cache/store.rb +77 -32
- 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 +2 -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 +2 -2
- 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 +388 -59
- data/lib/rigor/environment.rb +105 -20
- 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 +4 -7
- 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 +42 -12
- 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 +4 -6
- data/lib/rigor/rbs_extended/reporter.rb +36 -10
- data/lib/rigor/rbs_extended.rb +61 -21
- 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 +115 -22
- 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 +4 -5
- 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 +5 -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 +26 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
data/lib/rigor/type_node.rb
CHANGED
|
@@ -11,6 +11,7 @@ end
|
|
|
11
11
|
|
|
12
12
|
require_relative "type_node/identifier"
|
|
13
13
|
require_relative "type_node/integer_literal"
|
|
14
|
+
require_relative "type_node/range_literal"
|
|
14
15
|
require_relative "type_node/symbol_literal"
|
|
15
16
|
require_relative "type_node/string_literal"
|
|
16
17
|
require_relative "type_node/generic"
|
data/lib/rigor/version.rb
CHANGED
|
@@ -34,10 +34,6 @@ module Rigor
|
|
|
34
34
|
# The broadcast violations for a single call node, or `[]` when the node is not a
|
|
35
35
|
# `broadcast_to` / `ActionCable.server.broadcast` call this plugin recognises. ADR-37: the
|
|
36
36
|
# engine owns the walk.
|
|
37
|
-
#
|
|
38
|
-
# @param call_node [Prism::Node]
|
|
39
|
-
# @param channel_index [ChannelIndex]
|
|
40
|
-
# @return [Array<Violation>]
|
|
41
37
|
def violations_for(call_node:, channel_index:)
|
|
42
38
|
return [] unless call_node.is_a?(Prism::CallNode)
|
|
43
39
|
|
|
@@ -39,8 +39,6 @@ module Rigor
|
|
|
39
39
|
# `"::ChatChannel"`; see {ChannelDiscoverer}), while a QUERY may legitimately arrive rooted:
|
|
40
40
|
# `::ChatChannel.broadcast_to(...)` renders its receiver as `"::ChatChannel"`. The root marker is
|
|
41
41
|
# dropped here, once, so no caller needs a `find(name) || find("::#{name}")` retry (#621).
|
|
42
|
-
#
|
|
43
|
-
# @return [Entry, nil]
|
|
44
42
|
def find(class_name)
|
|
45
43
|
@by_name[strip_leading_namespace(class_name.to_s)]
|
|
46
44
|
end
|
|
@@ -65,6 +65,7 @@ module Rigor
|
|
|
65
65
|
class Actioncable < Rigor::Plugin::Base
|
|
66
66
|
manifest(
|
|
67
67
|
id: "actioncable",
|
|
68
|
+
target_gems: ["actioncable"],
|
|
68
69
|
# Bumped 2026-09-02 (#621) — channel keys and base-class names are de-rooted at the producer and a
|
|
69
70
|
# reopened class's actions and streams are UNIONed rather than clobbered, so a cached 0.1.0 index
|
|
70
71
|
# can be missing a whole channel (`< ::ApplicationCable::Channel`) or half its stream names.
|
|
@@ -42,10 +42,6 @@ module Rigor
|
|
|
42
42
|
|
|
43
43
|
# The mailer-call violations for a single call node (0..2), or `[]` when it is not a
|
|
44
44
|
# `<Mailer>.action(...)` call on a known mailer. ADR-37: the engine owns the walk.
|
|
45
|
-
#
|
|
46
|
-
# @param call_node [Prism::Node]
|
|
47
|
-
# @param mailer_index [MailerIndex]
|
|
48
|
-
# @return [Array<Violation>]
|
|
49
45
|
def violations_for(call_node:, mailer_index:)
|
|
50
46
|
return [] unless call_node.is_a?(Prism::CallNode) && action_call_candidate?(call_node)
|
|
51
47
|
|
|
@@ -38,10 +38,9 @@ module Rigor
|
|
|
38
38
|
VIEW_FORMATS = %w[html text].freeze
|
|
39
39
|
VIEW_EXTENSIONS = %w[erb haml slim].freeze
|
|
40
40
|
|
|
41
|
-
# @param
|
|
42
|
-
# @param
|
|
43
|
-
# @param
|
|
44
|
-
# @param views_root [String] absolute or project-relative path to the views directory (typically
|
|
41
|
+
# @param search_paths — absolute or project-relative paths to scan for mailers.
|
|
42
|
+
# @param base_classes — direct superclasses that mark a class as a mailer.
|
|
43
|
+
# @param views_root — absolute or project-relative path to the views directory (typically
|
|
45
44
|
# `app/views`).
|
|
46
45
|
def initialize(io_boundary:, search_paths:, base_classes:, views_root: DEFAULT_VIEWS_ROOT)
|
|
47
46
|
@io_boundary = io_boundary
|
|
@@ -53,7 +52,6 @@ module Rigor
|
|
|
53
52
|
@views_root = views_root
|
|
54
53
|
end
|
|
55
54
|
|
|
56
|
-
# @return [MailerIndex]
|
|
57
55
|
def discover
|
|
58
56
|
# Two-pass: first collect every module's defs (for the include-following step), then build
|
|
59
57
|
# per-class entries that pull in actions from include'd modules. GitLab's `Notify` mailer
|
|
@@ -58,8 +58,6 @@ module Rigor
|
|
|
58
58
|
# `"::UserMailer"`; see {MailerDiscoverer}), while a QUERY may legitimately arrive rooted:
|
|
59
59
|
# `::UserMailer.welcome(u)` renders its receiver as `"::UserMailer"`. The root marker is dropped
|
|
60
60
|
# here, once, so no caller needs a `find(name) || find("::#{name}")` retry (#621).
|
|
61
|
-
#
|
|
62
|
-
# @return [ClassEntry, nil]
|
|
63
61
|
def find(class_name)
|
|
64
62
|
@by_name[strip_leading_namespace(class_name.to_s)]
|
|
65
63
|
end
|
|
@@ -68,9 +66,8 @@ module Rigor
|
|
|
68
66
|
@by_name.key?(strip_leading_namespace(class_name.to_s))
|
|
69
67
|
end
|
|
70
68
|
|
|
71
|
-
# @param file_path
|
|
69
|
+
# @param file_path — absolute path of a mailer file (canonicalised — see plugin entry's
|
|
72
70
|
# `harvest`)
|
|
73
|
-
# @return [ClassEntry, nil]
|
|
74
71
|
def find_by_file(file_path)
|
|
75
72
|
@entries.find { |entry| entry.file_path == file_path }
|
|
76
73
|
end
|
|
@@ -43,6 +43,7 @@ module Rigor
|
|
|
43
43
|
class Actionmailer < Rigor::Plugin::Base
|
|
44
44
|
manifest(
|
|
45
45
|
id: "actionmailer",
|
|
46
|
+
target_gems: ["actionmailer"],
|
|
46
47
|
# Bumped 2026-05-28 — extended RESERVED_CLASS_METHODS to include `respond_to?` / `public_send` /
|
|
47
48
|
# `send` / `__send__` / `method` and friends so dynamic-dispatch idioms
|
|
48
49
|
# (`Mailer.respond_to?(action)` / `Mailer.public_send(action)`) stop firing `unknown-action`
|
|
@@ -81,10 +81,8 @@ module Rigor
|
|
|
81
81
|
# duplicates on Mastodon and ~119 on Redmine, exactly stacked with the rails-routes diagnostics.
|
|
82
82
|
# Returns `[]` for unknown / arity-mismatch shapes.
|
|
83
83
|
#
|
|
84
|
-
# @param
|
|
85
|
-
# @param helper_table [Hash{String => Hash}] each entry carries `name`, `arity`,
|
|
84
|
+
# @param helper_table — each entry carries `name`, `arity`,
|
|
86
85
|
# `acceptable_arities`, `path`, `http_method`, `action`.
|
|
87
|
-
# @return [Array<Violation>]
|
|
88
86
|
def helper_violations_for(call_node:, helper_table:)
|
|
89
87
|
return [] unless implicit_helper_call?(call_node)
|
|
90
88
|
|
|
@@ -108,10 +106,7 @@ module Rigor
|
|
|
108
106
|
# each filter name reference is validated against it. Calls outside a known controller
|
|
109
107
|
# contribute nothing.
|
|
110
108
|
#
|
|
111
|
-
# @param
|
|
112
|
-
# @param ancestors [Array<Prism::Node>] the lexical ancestor chain
|
|
113
|
-
# @param controller_index [ControllerIndex]
|
|
114
|
-
# @return [Array<Violation>]
|
|
109
|
+
# @param ancestors — the lexical ancestor chain
|
|
115
110
|
def filter_violations_for(call_node:, ancestors:, controller_index:)
|
|
116
111
|
return [] unless filter_call?(call_node)
|
|
117
112
|
|
|
@@ -144,10 +139,6 @@ module Rigor
|
|
|
144
139
|
# AR model's column list (looked up via the model_index fact published by `rigor-activerecord`).
|
|
145
140
|
# Calls whose `:require` argument is a non-literal Symbol are passed through; namespaced models
|
|
146
141
|
# (`params.require(:admin_user)` → `Admin::User`) are deferred.
|
|
147
|
-
#
|
|
148
|
-
# @param call_node [Prism::Node]
|
|
149
|
-
# @param model_index [Hash{String => Hash}]
|
|
150
|
-
# @return [Array<Violation>]
|
|
151
142
|
def permit_violations_for(call_node:, model_index:)
|
|
152
143
|
return [] unless permit_chain?(call_node)
|
|
153
144
|
|
|
@@ -179,13 +170,13 @@ module Rigor
|
|
|
179
170
|
# method that doesn't call `render`) is also skipped — Phase 3 validates explicit renders only,
|
|
180
171
|
# since the implicit path would false-positive on `redirect_to` / `head` / early returns.
|
|
181
172
|
#
|
|
182
|
-
# @param
|
|
183
|
-
# @param
|
|
184
|
-
# @param
|
|
185
|
-
#
|
|
186
|
-
#
|
|
187
|
-
|
|
188
|
-
|
|
173
|
+
# @param ancestors — the lexical ancestor chain
|
|
174
|
+
# @param path — file being analysed
|
|
175
|
+
# @param io_boundary — the plugin's {Rigor::Plugin::IoBoundary}; #629 — every filesystem
|
|
176
|
+
# question this phase asks goes through it, so "no such template" is a recorded dependency and
|
|
177
|
+
# the run-result cache invalidates once the template appears
|
|
178
|
+
def render_violations_for(call_node:, ancestors:, path:, view_search_roots:, io_boundary:,
|
|
179
|
+
controller_index: nil)
|
|
189
180
|
return [] unless render_call?(call_node)
|
|
190
181
|
|
|
191
182
|
class_name = enclosing_controller_name(ancestors)
|
|
@@ -217,12 +208,12 @@ module Rigor
|
|
|
217
208
|
# `app/views/admin/settings/` holds about/, appearance/, … but no top-level templates). Skip
|
|
218
209
|
# render checks for these — the diagnostic would be a false positive against intentional Rails
|
|
219
210
|
# abstract-base layouts.
|
|
220
|
-
return [] if abstract_base_controller?(class_name, controller_path, view_search_roots)
|
|
211
|
+
return [] if abstract_base_controller?(class_name, controller_path, view_search_roots, io_boundary)
|
|
221
212
|
|
|
222
213
|
target = render_target_for(call_node, controller_path)
|
|
223
214
|
return [] if target.nil?
|
|
224
215
|
|
|
225
|
-
violation = render_violation(call_node, target, view_search_roots)
|
|
216
|
+
violation = render_violation(call_node, target, view_search_roots, io_boundary)
|
|
226
217
|
violation ? [violation] : []
|
|
227
218
|
end
|
|
228
219
|
|
|
@@ -406,15 +397,16 @@ module Rigor
|
|
|
406
397
|
# controllers.
|
|
407
398
|
# Deliberately NOT triggered by "no view directory at all" because that fires the diagnostic we
|
|
408
399
|
# DO want for genuinely-missing views (the typo / forgot-to-create case).
|
|
409
|
-
def abstract_base_controller?(class_name, controller_path, view_search_roots)
|
|
400
|
+
def abstract_base_controller?(class_name, controller_path, view_search_roots, io_boundary)
|
|
410
401
|
return true if class_name.end_with?("BaseController")
|
|
411
402
|
|
|
412
403
|
view_search_roots.any? do |root|
|
|
413
404
|
dir = File.join(root, controller_path)
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
entries
|
|
405
|
+
# #629 — the listing row this records is the same one {#locate_template} needs, and the
|
|
406
|
+
# boundary deduplicates it per directory, so the whole render phase costs one glob
|
|
407
|
+
# validation per consulted view directory on a warm run.
|
|
408
|
+
entries = io_boundary.list_directory(dir)
|
|
409
|
+
entries.any? && entries.all? { |e| File.directory?(e) }
|
|
418
410
|
end
|
|
419
411
|
end
|
|
420
412
|
|
|
@@ -473,9 +465,9 @@ module Rigor
|
|
|
473
465
|
Rigor::Plugin::Inflector.underscore(stripped)
|
|
474
466
|
end
|
|
475
467
|
|
|
476
|
-
def render_violation(call_node, target, view_search_roots)
|
|
468
|
+
def render_violation(call_node, target, view_search_roots, io_boundary)
|
|
477
469
|
kind, relative = target
|
|
478
|
-
existing = locate_template(relative, view_search_roots)
|
|
470
|
+
existing = locate_template(relative, view_search_roots, io_boundary)
|
|
479
471
|
if existing
|
|
480
472
|
render_target_violation(call_node, kind, relative, existing)
|
|
481
473
|
else
|
|
@@ -483,11 +475,22 @@ module Rigor
|
|
|
483
475
|
end
|
|
484
476
|
end
|
|
485
477
|
|
|
486
|
-
|
|
478
|
+
# #629 — resolved from the DIRECTORY LISTING, not from a probe per candidate path. The old shape
|
|
479
|
+
# asked `File.file?` up to `extensions × roots` times per `render` call and recorded nothing, so a
|
|
480
|
+
# cached run kept reporting `plugin.actionpack.missing-template` after the template was written.
|
|
481
|
+
# One {Rigor::Plugin::IoBoundary#list_directory} row per `app/views/<controller>` directory carries
|
|
482
|
+
# the dependency instead: any file added or removed there invalidates the run, and the warm-run
|
|
483
|
+
# validation count is the number of view directories the run consulted rather than the number of
|
|
484
|
+
# candidate paths it tried.
|
|
485
|
+
def locate_template(relative, view_search_roots, io_boundary)
|
|
486
|
+
basename = File.basename(relative)
|
|
487
487
|
view_search_roots.each do |root|
|
|
488
|
+
# Names, not paths: the boundary answers in absolute paths while `root` may be the relative
|
|
489
|
+
# `app/views` the user configured, and the reported candidate keeps the configured spelling.
|
|
490
|
+
listing = io_boundary.list_directory(File.join(root, File.dirname(relative)))
|
|
491
|
+
names = listing.map { |entry| File.basename(entry) }
|
|
488
492
|
RENDER_TEMPLATE_EXTENSIONS.each do |ext|
|
|
489
|
-
|
|
490
|
-
return candidate if File.file?(candidate)
|
|
493
|
+
return File.join(root, "#{relative}#{ext}") if names.include?("#{basename}#{ext}")
|
|
491
494
|
end
|
|
492
495
|
end
|
|
493
496
|
nil
|
|
@@ -42,8 +42,6 @@ module Rigor
|
|
|
42
42
|
# `"Admin::AccountsController"` — never `"::AccountsController"`; see {ControllerDiscoverer}),
|
|
43
43
|
# while a QUERY may legitimately arrive rooted. The root marker is dropped at every lookup entry
|
|
44
44
|
# point, once, so no caller needs a `find(name) || find("::#{name}")` retry (#621).
|
|
45
|
-
#
|
|
46
|
-
# @return [Entry, nil]
|
|
47
45
|
def find(class_name)
|
|
48
46
|
@entries[strip_leading_namespace(class_name)]
|
|
49
47
|
end
|
|
@@ -84,7 +82,7 @@ module Rigor
|
|
|
84
82
|
methods.uniq.freeze
|
|
85
83
|
end
|
|
86
84
|
|
|
87
|
-
# @return
|
|
85
|
+
# @return true when the class has at least one include OR parent class we couldn't
|
|
88
86
|
# resolve in the index (typically a gem-shipped concern such as Devise's
|
|
89
87
|
# `Devise::Controllers::Helpers`, or a gem-shipped parent controller such as
|
|
90
88
|
# `Devise::ConfirmationsController` or `Doorkeeper::AuthorizedApplicationsController`). Phase
|
|
@@ -55,6 +55,7 @@ module Rigor
|
|
|
55
55
|
class Actionpack < Rigor::Plugin::Base
|
|
56
56
|
manifest(
|
|
57
57
|
id: "actionpack",
|
|
58
|
+
target_gems: ["actionpack"],
|
|
58
59
|
# ADR-37: the four phases (helper / filter / render / strong-params) run per-call over the
|
|
59
60
|
# engine-owned walk; the enclosing controller is read from the node-rule `NodeContext`
|
|
60
61
|
# ancestors. Nested-module qualification is preserved — `module Admin; class
|
|
@@ -68,7 +69,11 @@ module Rigor
|
|
|
68
69
|
# Bumped 2026-09-02 (#621) — a reopened controller's `def`s are UNIONed rather than clobbered by
|
|
69
70
|
# the later file in the glob, so a cached 1.0.0 index can be missing the filter targets the merge
|
|
70
71
|
# restores.
|
|
71
|
-
|
|
72
|
+
# Bumped 2026-09-10 (#534 item 7) — the plugin gained a bundled `sig/` (ADR-25) naming the
|
|
73
|
+
# `ActionController` namespace and the errors controllers rescue, so the constants stop resolving
|
|
74
|
+
# to `untyped`. `ActionController::Parameters` and the `ActionDispatch` readers stay undeclared;
|
|
75
|
+
# `sig/action_controller.rbs` documents why that absence is load-bearing.
|
|
76
|
+
version: "1.2.0",
|
|
72
77
|
description: "Validates Action Pack route-helper calls and filter chains inside controllers, and types the request-context readers (`params` / `session` / `request` / `flash`) and their chains.",
|
|
73
78
|
config_schema: {
|
|
74
79
|
"controller_search_paths" => { kind: :array, default: ["app/controllers"] },
|
|
@@ -78,6 +83,26 @@ module Rigor
|
|
|
78
83
|
{ plugin_id: "rails-routes", name: :helper_table, optional: true },
|
|
79
84
|
{ plugin_id: "activerecord", name: :model_index, optional: true }
|
|
80
85
|
],
|
|
86
|
+
# ADR-25 (#534 item 7) — the framework-namespace signatures. See `sig/action_controller.rbs` for
|
|
87
|
+
# the admission rule and, more importantly, for what it refuses to declare.
|
|
88
|
+
signature_paths: ["sig"],
|
|
89
|
+
# ADR-26 — every class the bundled signature names is declared so the CONSTANT resolves; none of
|
|
90
|
+
# them enumerates a method surface, so `ParameterMissing#param` and its siblings must stay
|
|
91
|
+
# lenient rather than becoming `call.undefined-method` on a rescue body.
|
|
92
|
+
open_receivers: [
|
|
93
|
+
"ActionController::ActionControllerError",
|
|
94
|
+
"ActionController::BadRequest",
|
|
95
|
+
"ActionController::RoutingError",
|
|
96
|
+
"ActionController::UrlGenerationError",
|
|
97
|
+
"ActionController::MethodNotAllowed",
|
|
98
|
+
"ActionController::NotImplemented",
|
|
99
|
+
"ActionController::UnknownFormat",
|
|
100
|
+
"ActionController::InvalidAuthenticityToken",
|
|
101
|
+
"ActionController::InvalidCrossOriginRequest",
|
|
102
|
+
"ActionController::MissingExactTemplate",
|
|
103
|
+
"ActionController::ParameterMissing",
|
|
104
|
+
"ActionController::UnpermittedParameters"
|
|
105
|
+
],
|
|
81
106
|
# ADR-103 WD10 / WD14 (#387) — see {Effects} for what each row is and why.
|
|
82
107
|
effect_root: "rails",
|
|
83
108
|
effect_labels: %w[
|
|
@@ -144,7 +169,8 @@ module Rigor
|
|
|
144
169
|
diagnostics_for(
|
|
145
170
|
Analyzer.render_violations_for(
|
|
146
171
|
call_node: node, ancestors: context.ancestors, path: path,
|
|
147
|
-
view_search_roots: @view_search_paths,
|
|
172
|
+
view_search_roots: @view_search_paths, io_boundary: io_boundary,
|
|
173
|
+
controller_index: producer_value(:controller_index)
|
|
148
174
|
),
|
|
149
175
|
path: path, node: node
|
|
150
176
|
)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Action Pack's framework namespaces — issue #534 item 7.
|
|
2
|
+
#
|
|
3
|
+
# The 2026-09-01 corpus sweep counted `ActionController` 37 times on mastodon as an unresolved
|
|
4
|
+
# constant. This file names the namespace and the errors controllers rescue, and nothing else.
|
|
5
|
+
#
|
|
6
|
+
# ## What must NOT be declared here, and why the list is so short
|
|
7
|
+
#
|
|
8
|
+
# `ActionController::Parameters` is absent on purpose, and it is the most important absence in this
|
|
9
|
+
# plugin. `Rigor::Plugin::Actionpack::REQUEST_CONTEXT_READER_TYPES` types `params` as a *lenient*
|
|
10
|
+
# nominal — a real class name Rigor ships no RBS for — precisely so that `params.require(...)`,
|
|
11
|
+
# `params.to_unsafe_h` and every other member of a surface no signature can complete resolve
|
|
12
|
+
# lenient-to-`Dynamic` instead of drawing `call.undefined-method`. Declaring the class here, even
|
|
13
|
+
# empty, would make it RBS-known and undo that. The same argument covers `ActionDispatch::Request`,
|
|
14
|
+
# `Request::Session`, `Flash::FlashHash`, `Flash::FlashNow` and `Cookies::CookieJar`.
|
|
15
|
+
#
|
|
16
|
+
# `ActionController::Base` and `ActionController::API` are absent for the stronger reason: every
|
|
17
|
+
# controller in a Rails app inherits from one of them, so declaring either empty would close the
|
|
18
|
+
# whole project's controller hierarchy and turn `render`, `before_action`, `head` and the rest into
|
|
19
|
+
# `call.undefined-method` on working code.
|
|
20
|
+
#
|
|
21
|
+
# What is left is exactly the shape that is safe: bare namespace modules nobody dispatches on, and
|
|
22
|
+
# exception classes whose inherited surface is a core Ruby one Rigor already carries. Each error
|
|
23
|
+
# class is listed in the manifest's `open_receivers:` (ADR-26), so the framework-specific readers
|
|
24
|
+
# this file does not enumerate (`ParameterMissing#param`, `UrlGenerationError#routes`, …) stay
|
|
25
|
+
# lenient too.
|
|
26
|
+
#
|
|
27
|
+
# Superclasses transcribed from Rails v8.0.2 (`actionpack/lib/action_controller/metal/exceptions.rb`
|
|
28
|
+
# and `strong_parameters.rb`); `ParameterMissing < KeyError` and `UnpermittedParameters < IndexError`
|
|
29
|
+
# are the two that are NOT under `ActionControllerError`, and getting that wrong would be a wrong
|
|
30
|
+
# `rescue` reachability answer, so both are stated from the source rather than from the pattern.
|
|
31
|
+
|
|
32
|
+
module ActionController
|
|
33
|
+
class ActionControllerError < StandardError
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class BadRequest < ActionControllerError
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class RoutingError < ActionControllerError
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class UrlGenerationError < ActionControllerError
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class MethodNotAllowed < ActionControllerError
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class NotImplemented < MethodNotAllowed
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
class UnknownFormat < ActionControllerError
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class InvalidAuthenticityToken < ActionControllerError
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class InvalidCrossOriginRequest < ActionControllerError
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
class MissingExactTemplate < UnknownFormat
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# `::KeyError` is generic in core RBS (`KeyError[K, R]`, the receiver and key of the failed lookup),
|
|
64
|
+
# so the supertype has to be applied. `untyped` on both: Rails raises it from a Parameters lookup and
|
|
65
|
+
# this signature has no business claiming what `#receiver` / `#key` answer.
|
|
66
|
+
class ParameterMissing < ::KeyError[untyped, untyped]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
class UnpermittedParameters < ::IndexError
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -25,10 +25,6 @@ module Rigor
|
|
|
25
25
|
|
|
26
26
|
# The job-call violations for a single call node (0..2), or `[]` when the node is not a
|
|
27
27
|
# `<Job>.perform_*` entry call on a known job. ADR-37: the engine owns the walk.
|
|
28
|
-
#
|
|
29
|
-
# @param call_node [Prism::Node]
|
|
30
|
-
# @param job_index [JobIndex]
|
|
31
|
-
# @return [Array<Violation>]
|
|
32
28
|
def violations_for(call_node:, job_index:)
|
|
33
29
|
return [] unless call_node.is_a?(Prism::CallNode) && entry_call?(call_node)
|
|
34
30
|
|
|
@@ -61,7 +61,7 @@ module Rigor
|
|
|
61
61
|
|
|
62
62
|
module_function
|
|
63
63
|
|
|
64
|
-
# @param adapter
|
|
64
|
+
# @param adapter — the adapter the project declares, or nil when it declares none
|
|
65
65
|
# (or more than one, across environments)
|
|
66
66
|
def attributions(adapter)
|
|
67
67
|
labels = (TRANSPORTS[adapter] || ["io"]) + MEANING
|
|
@@ -42,8 +42,6 @@ module Rigor
|
|
|
42
42
|
# `"::WelcomeJob"`; see {JobDiscoverer}), while a QUERY may legitimately arrive rooted:
|
|
43
43
|
# `::WelcomeJob.perform_later(1)` renders its receiver as `"::WelcomeJob"`. The root marker is
|
|
44
44
|
# dropped here, once, so no caller needs a `find(name) || find("::#{name}")` retry (#621).
|
|
45
|
-
#
|
|
46
|
-
# @return [Entry, nil]
|
|
47
45
|
def find(class_name)
|
|
48
46
|
@by_name[strip_leading_namespace(class_name.to_s)]
|
|
49
47
|
end
|
|
@@ -51,7 +51,7 @@ module Rigor
|
|
|
51
51
|
@recurring_paths = recurring_paths
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
# @return
|
|
54
|
+
# @return the class names named by a `class:` key in a recurring-task entry, sorted
|
|
55
55
|
# and unique. NOT yet intersected with the discovered jobs — the caller does that.
|
|
56
56
|
def job_names
|
|
57
57
|
names = Set.new
|
|
@@ -37,6 +37,7 @@ module Rigor
|
|
|
37
37
|
class Activejob < Rigor::Plugin::Base
|
|
38
38
|
manifest(
|
|
39
39
|
id: "activejob",
|
|
40
|
+
target_gems: ["activejob"],
|
|
40
41
|
# Bumped — publishes `:reachability_roots` for `rigor unused` (ADR-102 WD3): the jobs Solid Queue's
|
|
41
42
|
# recurring schedule runs by name, which no `perform_later` call site writes down.
|
|
42
43
|
#
|
|
@@ -77,9 +77,8 @@ module Rigor
|
|
|
77
77
|
# `ApplicationRecord`) setting `self.table_name_prefix` for every model under it — both already
|
|
78
78
|
# guess wrong identically on unpatched code.
|
|
79
79
|
class ModelDiscoverer
|
|
80
|
-
# @param
|
|
81
|
-
# @param
|
|
82
|
-
# @param base_classes [Array<String>] superclass names that identify a class as an AR model.
|
|
80
|
+
# @param search_paths — absolute or project-relative paths.
|
|
81
|
+
# @param base_classes — superclass names that identify a class as an AR model.
|
|
83
82
|
# Declaration macros whose column's runtime value is a rich object, not the SQL scalar. Their column
|
|
84
83
|
# must NOT be narrowed to the schema type (see {ModelIndex.build}'s type-override remap).
|
|
85
84
|
TYPE_OVERRIDE_METHODS = %i[serialize mount_uploader mount_uploaders].freeze
|
|
@@ -113,7 +112,7 @@ module Rigor
|
|
|
113
112
|
|
|
114
113
|
attr_reader :type_override_columns
|
|
115
114
|
|
|
116
|
-
# @return
|
|
115
|
+
# @return rows of { class_name:, table_name_override:, sti_parent:, ... }
|
|
117
116
|
def discover
|
|
118
117
|
candidates = []
|
|
119
118
|
ruby_files_under(@search_paths).each do |path|
|
|
@@ -45,8 +45,7 @@ module Rigor
|
|
|
45
45
|
unique_constraint foreign_key primary_keys
|
|
46
46
|
].freeze
|
|
47
47
|
|
|
48
|
-
# @param source
|
|
49
|
-
# @return [SchemaTable]
|
|
48
|
+
# @param source — contents of `db/schema.rb`
|
|
50
49
|
def self.parse(source)
|
|
51
50
|
tree = Prism.parse(source).value
|
|
52
51
|
new.parse(tree)
|
|
@@ -54,8 +54,7 @@ module Rigor
|
|
|
54
54
|
# Uppercase leading keywords that mark a table-level constraint line rather than a column.
|
|
55
55
|
CONSTRAINT_KEYWORDS = %w[CONSTRAINT PRIMARY UNIQUE CHECK FOREIGN EXCLUDE LIKE PARTITION].freeze
|
|
56
56
|
|
|
57
|
-
# @param source
|
|
58
|
-
# @return [SchemaTable]
|
|
57
|
+
# @param source — contents of `db/structure.sql`
|
|
59
58
|
def self.parse(source)
|
|
60
59
|
new.parse(source)
|
|
61
60
|
end
|
|
@@ -63,6 +63,12 @@ module Rigor
|
|
|
63
63
|
class Activerecord < Rigor::Plugin::Base
|
|
64
64
|
manifest(
|
|
65
65
|
id: "activerecord",
|
|
66
|
+
target_gems: ["activerecord"],
|
|
67
|
+
# 0.9.0, 2026-09-10 (#534 item 7) — the bundled signatures gained `sig/active_record/framework.rbs`:
|
|
68
|
+
# the Active Record exception hierarchy, the `ActiveModel` namespace and `Arel`. No producer payload
|
|
69
|
+
# changed shape, but the version is the cache key a project sees, and the RBS half of a plugin's
|
|
70
|
+
# contribution rides the same bundle.
|
|
71
|
+
#
|
|
66
72
|
# 0.8.0, 2026-09-02 — a model declared `class ::User` is keyed by its de-rooted name (`"User"`) in
|
|
67
73
|
# the {ModelIndex} and the published `:model_index` fact (#583). The version is part of the
|
|
68
74
|
# producer cache KEY: a cached 0.7.0 index still keys such a model `"::User"`, and `ModelIndex#find`
|
|
@@ -87,7 +93,7 @@ module Rigor
|
|
|
87
93
|
# a scope lambda body / class-method body now contributes `Relation[Model]` via `scope.self_type`
|
|
88
94
|
# instead of falling through to `Kernel#select` (the IO multiplexer, `Array[String]` return). Plus
|
|
89
95
|
# `:select` added to the relation-entry-point list.
|
|
90
|
-
version: "0.
|
|
96
|
+
version: "0.9.0",
|
|
91
97
|
description: "Types ActiveRecord finders against the project's db/schema.rb and AR models.",
|
|
92
98
|
config_schema: {
|
|
93
99
|
"schema_file" => { kind: :string, default: "db/schema.rb" },
|
|
@@ -104,7 +110,28 @@ module Rigor
|
|
|
104
110
|
# ADR-26 — `ActiveRecord::Relation` is an "open" receiver: it delegates an unbounded set of
|
|
105
111
|
# user-defined scopes / class methods to its model, so `call.undefined-method` must not fire for
|
|
106
112
|
# it. `CheckRules` reads this manifest field and skips the rule for the class.
|
|
107
|
-
|
|
113
|
+
#
|
|
114
|
+
# The `sig/active_record/framework.rbs` names (#534 item 7) are open for the narrower reason that
|
|
115
|
+
# file documents: each is declared so the CONSTANT resolves, and none of them enumerates a method
|
|
116
|
+
# surface. `RecordInvalid#record`, `StatementInvalid#sql`, `Arel.star` and every other member left
|
|
117
|
+
# out must stay lenient, so the declaration buys constant resolution and asserts nothing else.
|
|
118
|
+
open_receivers: [
|
|
119
|
+
"ActiveRecord::Relation",
|
|
120
|
+
"ActiveRecord::ActiveRecordError",
|
|
121
|
+
"ActiveRecord::RecordNotFound",
|
|
122
|
+
"ActiveRecord::RecordInvalid",
|
|
123
|
+
"ActiveRecord::RecordNotSaved",
|
|
124
|
+
"ActiveRecord::RecordNotDestroyed",
|
|
125
|
+
"ActiveRecord::ReadOnlyRecord",
|
|
126
|
+
"ActiveRecord::Rollback",
|
|
127
|
+
"ActiveRecord::StaleObjectError",
|
|
128
|
+
"ActiveRecord::ConnectionNotEstablished",
|
|
129
|
+
"ActiveRecord::StatementInvalid",
|
|
130
|
+
"ActiveRecord::WrappedDatabaseException",
|
|
131
|
+
"ActiveRecord::RecordNotUnique",
|
|
132
|
+
"ActiveModel::ValidationError",
|
|
133
|
+
"Arel"
|
|
134
|
+
],
|
|
108
135
|
# ADR-103 WD2 / WD10 (#387) — the effect layer. rigor-activerecord models ActiveRecord, which is
|
|
109
136
|
# part of Rails, so it opens the framework's own `rails.*` root rather than one named after the
|
|
110
137
|
# plugin; `effect_root:` is granted only because the engine bundles this plugin
|
|
@@ -512,15 +539,23 @@ module Rigor
|
|
|
512
539
|
return bool_type if predicate
|
|
513
540
|
|
|
514
541
|
inner = ruby_type_to_type(column.ruby_type)
|
|
515
|
-
return
|
|
542
|
+
return Rigor::Type::Combinator.untyped if inner.nil?
|
|
516
543
|
|
|
517
544
|
column.array? ? Rigor::Type::Combinator.nominal_of("Array", type_args: [inner]) : inner
|
|
518
545
|
end
|
|
519
546
|
|
|
520
|
-
# Maps a `SchemaTable::Column#ruby_type` string to a Rigor type. `"Object"` (json / jsonb /
|
|
521
|
-
#
|
|
522
|
-
# envelope and could surface false `call.undefined-method` on a
|
|
523
|
-
# cannot model.
|
|
547
|
+
# Maps a `SchemaTable::Column#ruby_type` string to a Rigor type. `"Object"` (json / jsonb / a
|
|
548
|
+
# `serialize`d or `mount_uploader`ed column / an unrecognised SQL type) declines — `Nominal[Object]`
|
|
549
|
+
# would be NARROWER than the RBS-erased envelope and could surface false `call.undefined-method` on a
|
|
550
|
+
# value whose real shape the plugin cannot model.
|
|
551
|
+
#
|
|
552
|
+
# ADR-82 WD4 — the caller turns that decline into an explicit `Dynamic[top]` rather than passing the
|
|
553
|
+
# call on. The two are the same type at the site (dispatch widens an unresolved reader to Dynamic
|
|
554
|
+
# anyway), but only the plugin ANSWERING makes the engine record `framework_dsl_boundary`: this is a
|
|
555
|
+
# macro-generated attribute whose value crosses a DSL boundary and stays dynamic, which is exactly the
|
|
556
|
+
# cause WD4 defined and left unattributed. Answering here is honest in the way WD4 requires — the
|
|
557
|
+
# plugin knows the reader exists (the schema declares the column) and knows it cannot type it — where
|
|
558
|
+
# guessing "framework boundary" for any unresolved receiver would not be.
|
|
524
559
|
def ruby_type_to_type(ruby_type)
|
|
525
560
|
case ruby_type
|
|
526
561
|
when "bool" then bool_type
|