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
|
@@ -28,11 +28,6 @@ module Rigor
|
|
|
28
28
|
# The violations for a single entry call (`FactoryBot.create(...)` etc.), or `[]` when the node
|
|
29
29
|
# is not an entry call or its first argument is not a literal factory name. ADR-37: the engine
|
|
30
30
|
# owns the walk.
|
|
31
|
-
#
|
|
32
|
-
# @param call_node [Prism::Node]
|
|
33
|
-
# @param factory_index [FactoryIndex]
|
|
34
|
-
# @param model_index [Hash, nil]
|
|
35
|
-
# @return [Array<Violation>]
|
|
36
31
|
def violations_for(call_node:, factory_index:, model_index: nil)
|
|
37
32
|
return [] unless entry_call?(call_node)
|
|
38
33
|
|
|
@@ -58,6 +58,7 @@ module Rigor
|
|
|
58
58
|
class Factorybot < Rigor::Plugin::Base
|
|
59
59
|
manifest(
|
|
60
60
|
id: "factorybot",
|
|
61
|
+
target_gems: ["factory_bot"],
|
|
61
62
|
# Bumped 2026-08-16 — publishes `:reachability_references` for `rigor unused` (ADR-102 WD3 / #350).
|
|
62
63
|
version: "0.3.0",
|
|
63
64
|
description: "Validates FactoryBot.create / build / attributes_for call shapes; " \
|
|
@@ -69,7 +70,7 @@ module Rigor
|
|
|
69
70
|
consumes: [
|
|
70
71
|
{ plugin_id: "activerecord", name: :model_index, optional: true }
|
|
71
72
|
],
|
|
72
|
-
produces: [
|
|
73
|
+
produces: %i[reachability_references factory_index]
|
|
73
74
|
)
|
|
74
75
|
|
|
75
76
|
producer :factory_index, watch: -> { [[@factory_search_paths, "**/*.rb"]] } do |_params|
|
|
@@ -101,6 +102,12 @@ module Rigor
|
|
|
101
102
|
index = producer_value(:factory_index)
|
|
102
103
|
return if index.nil? || index.empty?
|
|
103
104
|
|
|
105
|
+
# The producer value and the ADR-9 fact are different channels (#921) — `producer_value` is this
|
|
106
|
+
# plugin's own cached computation, read back via `producer_value` above; `fact_store.publish` is
|
|
107
|
+
# what makes it visible to another plugin's `read_fact`, which is how rigor-rspec binds
|
|
108
|
+
# `create(:name)` to the factory's model class.
|
|
109
|
+
services.fact_store.publish(plugin_id: manifest.id, name: :factory_index, value: index)
|
|
110
|
+
|
|
104
111
|
references = index.entries.values.filter_map do |entry|
|
|
105
112
|
{ name: entry.model_class, role: :test } if entry.model_class
|
|
106
113
|
end.uniq
|
|
@@ -13,9 +13,21 @@ module Rigor
|
|
|
13
13
|
class FFI < Base
|
|
14
14
|
manifest(
|
|
15
15
|
id: "ffi",
|
|
16
|
-
|
|
16
|
+
target_gems: ["ffi"],
|
|
17
|
+
# Bumped 2026-09-10 (#918) — declares `config_schema` for the two `.rigor.yml` surfaces ADR-30
|
|
18
|
+
# WD4 (`exceptions`) and WD6 (`target`) already read off `config` but the manifest never
|
|
19
|
+
# published, so both keys were rejected by `Manifest#validate_config` as unknown.
|
|
20
|
+
version: "0.2.0",
|
|
17
21
|
description: "Models FFI library bindings, struct layouts, callbacks, carrier types, and ffx target compatibility.",
|
|
18
|
-
signature_paths: ["sig"]
|
|
22
|
+
signature_paths: ["sig"],
|
|
23
|
+
config_schema: {
|
|
24
|
+
# WD4 — typedef alias names the nominal-opaque-pointer heuristic should treat as a transparent
|
|
25
|
+
# `:pointer` alias even though they match the `_ptr$` / `_handle$` naming pattern.
|
|
26
|
+
"exceptions" => { kind: :array, default: [] },
|
|
27
|
+
# WD6 — forces ffx-target detection instead of the `extconf.rb` / `Gemfile.lock` cascade.
|
|
28
|
+
# `"auto"` (the default) keeps the cascade; `"ffi"` / `"ffx"` pin the target outright.
|
|
29
|
+
"target" => { kind: :string, default: "auto" }
|
|
30
|
+
}
|
|
19
31
|
)
|
|
20
32
|
|
|
21
33
|
producer :ffi_catalog do
|
|
@@ -25,7 +37,13 @@ module Rigor
|
|
|
25
37
|
|
|
26
38
|
# Dynamic return rule for attached FFI functions
|
|
27
39
|
# Gated on known FFI library receivers and attached function names
|
|
28
|
-
|
|
40
|
+
#
|
|
41
|
+
# Issue #701 — both receiver kinds are declared because the block below already resolved
|
|
42
|
+
# `Nominal` and `Singleton` alike: `attach_function` installs the binding on the library module
|
|
43
|
+
# itself (`MyLib.strlen`) and `include`ing that module makes the same name an instance call, so
|
|
44
|
+
# neither kind is an accident. The kind-aware gate makes that intent explicit rather than relying
|
|
45
|
+
# on a `receivers:` entry answering for both.
|
|
46
|
+
dynamic_return receivers: -> { both_receiver_kinds(producer_value(:ffi_catalog)&.libraries) },
|
|
29
47
|
methods: -> { producer_value(:ffi_catalog)&.function_method_names || [] } do |call_node, scope|
|
|
30
48
|
catalog = producer_value(:ffi_catalog)
|
|
31
49
|
next nil if catalog.nil?
|
|
@@ -51,7 +69,11 @@ module Rigor
|
|
|
51
69
|
|
|
52
70
|
# Dynamic return rule for FFI struct field accessors
|
|
53
71
|
# Gated on known FFI struct class names and field names
|
|
54
|
-
|
|
72
|
+
#
|
|
73
|
+
# Issue #701 — both kinds again, for the same reason: the block resolves the receiver itself and
|
|
74
|
+
# answers only for a name the discovered layout carries, so restricting the gate to instances
|
|
75
|
+
# would narrow a shipped rule on a shape nothing in the corpus has adjudicated.
|
|
76
|
+
dynamic_return receivers: -> { both_receiver_kinds(producer_value(:ffi_catalog)&.struct_names) },
|
|
55
77
|
methods: -> { producer_value(:ffi_catalog)&.struct_field_names || [] } do |call_node, scope|
|
|
56
78
|
catalog = producer_value(:ffi_catalog)
|
|
57
79
|
next nil if catalog.nil?
|
|
@@ -114,6 +136,16 @@ module Rigor
|
|
|
114
136
|
@target = TargetDetector.detect(root: root, config: config)
|
|
115
137
|
@exceptions = config["exceptions"] || []
|
|
116
138
|
end
|
|
139
|
+
|
|
140
|
+
private
|
|
141
|
+
|
|
142
|
+
# Every discovered name in both `dynamic_return receivers:` kinds (#701), for the two rules whose
|
|
143
|
+
# blocks accept an instance and a class receiver alike.
|
|
144
|
+
#
|
|
145
|
+
# @param names — discovered class / module names, or nil when the catalog producer did not run
|
|
146
|
+
def both_receiver_kinds(names)
|
|
147
|
+
Array(names).flat_map { |name| [name, "singleton(#{name})"] }
|
|
148
|
+
end
|
|
117
149
|
end
|
|
118
150
|
end
|
|
119
151
|
end
|
|
@@ -40,16 +40,18 @@ module Rigor
|
|
|
40
40
|
|
|
41
41
|
module_function
|
|
42
42
|
|
|
43
|
-
# @param paths
|
|
44
|
-
# @
|
|
43
|
+
# @param paths — absolute paths to `.rb` files the project's `paths:` resolves to.
|
|
44
|
+
# @param io_boundary — the plugin's {Rigor::Plugin::IoBoundary}; every source read goes
|
|
45
|
+
# through it (#630) so each scanned file is a recorded cache dependency.
|
|
46
|
+
# @return frozen 4-key result: `:types` (per-`Schema::Object` field
|
|
45
47
|
# table), `:enums` (per-`Schema::Enum` value list), `:input_objects`
|
|
46
48
|
# (per-`Schema::InputObject` argument table), `:mutations` (per-`Schema::Mutation`
|
|
47
49
|
# arguments+fields table). Any subset may be empty when no recognisable declaration of that
|
|
48
50
|
# kind is found.
|
|
49
|
-
def scan(paths:)
|
|
51
|
+
def scan(paths:, io_boundary:)
|
|
50
52
|
acc = empty_accumulator
|
|
51
53
|
paths.each do |path|
|
|
52
|
-
merge_accumulator(acc, scan_file(path))
|
|
54
|
+
merge_accumulator(acc, scan_file(path, io_boundary))
|
|
53
55
|
end
|
|
54
56
|
freeze_accumulator(acc)
|
|
55
57
|
end
|
|
@@ -75,8 +77,8 @@ module Rigor
|
|
|
75
77
|
end
|
|
76
78
|
private_class_method :freeze_accumulator
|
|
77
79
|
|
|
78
|
-
def scan_file(path)
|
|
79
|
-
source =
|
|
80
|
+
def scan_file(path, io_boundary)
|
|
81
|
+
source = io_boundary.read_file(path)
|
|
80
82
|
parse_result = Prism.parse(source, filepath: path)
|
|
81
83
|
return empty_accumulator unless parse_result.errors.empty?
|
|
82
84
|
|
|
@@ -50,6 +50,7 @@ module Rigor
|
|
|
50
50
|
class Graphql < Rigor::Plugin::Base
|
|
51
51
|
manifest(
|
|
52
52
|
id: "graphql",
|
|
53
|
+
target_gems: ["graphql"],
|
|
53
54
|
version: "0.1.0",
|
|
54
55
|
description: "Recognises `class T < GraphQL::Schema::{Object,Enum,InputObject,Mutation}` " \
|
|
55
56
|
"subclasses; publishes the per-type field-type table, the per-enum value " \
|
|
@@ -59,7 +60,7 @@ module Rigor
|
|
|
59
60
|
)
|
|
60
61
|
|
|
61
62
|
def prepare(services)
|
|
62
|
-
scanned = TypeScanner.scan(paths: scannable_paths(services))
|
|
63
|
+
scanned = TypeScanner.scan(paths: scannable_paths(services), io_boundary: io_boundary)
|
|
63
64
|
publish_if_present(services, :graphql_type_table, scanned.fetch(:types))
|
|
64
65
|
publish_if_present(services, :graphql_enum_table, scanned.fetch(:enums))
|
|
65
66
|
publish_if_present(services, :graphql_input_object_table, scanned.fetch(:input_objects))
|
|
@@ -78,11 +79,13 @@ module Rigor
|
|
|
78
79
|
services.fact_store.publish(plugin_id: manifest.id, name: name, value: value)
|
|
79
80
|
end
|
|
80
81
|
|
|
82
|
+
# ADR-45 WD1b (#613 / #630) — the classification probes go through the boundary, so an entry that
|
|
83
|
+
# is not there yet (or stops being a directory) is a recorded dependency of the scan's input set.
|
|
81
84
|
def scannable_paths(services)
|
|
82
85
|
@scannable_paths ||= services.configuration.paths.flat_map do |entry|
|
|
83
|
-
if
|
|
86
|
+
if io_boundary.directory?(entry)
|
|
84
87
|
Dir.glob(File.join(entry, "**", "*.rb"), sort: true)
|
|
85
|
-
elsif
|
|
88
|
+
elsif io_boundary.file?(entry) && entry.end_with?(".rb")
|
|
86
89
|
[entry]
|
|
87
90
|
else
|
|
88
91
|
[]
|
|
@@ -56,6 +56,7 @@ module Rigor
|
|
|
56
56
|
class Mangrove < Rigor::Plugin::Base
|
|
57
57
|
manifest(
|
|
58
58
|
id: "mangrove",
|
|
59
|
+
target_gems: ["mangrove"],
|
|
59
60
|
version: "0.2.0",
|
|
60
61
|
description: "Instantiates Mangrove Result/Option carrier generics at unwrap call sites, " \
|
|
61
62
|
"sharpening `unwrap!` / `unwrap_in` / `unwrap_or` from `untyped` to the carried type; " \
|
|
@@ -134,7 +135,7 @@ module Rigor
|
|
|
134
135
|
|
|
135
136
|
private
|
|
136
137
|
|
|
137
|
-
# @return
|
|
138
|
+
# @return the receiver's inferred type, or nil when the engine raises on a synthetic
|
|
138
139
|
# / unrecognised node (mirrors rigor-sorbet's defensive degrade).
|
|
139
140
|
def receiver_type_of(receiver, scope)
|
|
140
141
|
scope.type_of(receiver)
|
|
@@ -59,9 +59,6 @@ module Rigor
|
|
|
59
59
|
module AssertionAnalyzer
|
|
60
60
|
module_function
|
|
61
61
|
|
|
62
|
-
# @param call_node [Prism::CallNode]
|
|
63
|
-
# @param environment [Rigor::Environment, nil]
|
|
64
|
-
# @return [Rigor::FlowContribution, nil]
|
|
65
62
|
def contribution_for(call_node, environment:)
|
|
66
63
|
return nil unless call_node.is_a?(Prism::CallNode)
|
|
67
64
|
|
|
@@ -37,6 +37,7 @@ module Rigor
|
|
|
37
37
|
class Minitest < Rigor::Plugin::Base
|
|
38
38
|
manifest(
|
|
39
39
|
id: "minitest",
|
|
40
|
+
target_gems: ["minitest"],
|
|
40
41
|
version: "0.1.0",
|
|
41
42
|
description: "Narrows locals through Minitest / Test::Unit `assert_*` / `refute_*` " \
|
|
42
43
|
"and Minitest/spec `_(x).must_*` / `.wont_*` matchers.",
|
|
@@ -33,11 +33,6 @@ module Rigor
|
|
|
33
33
|
# The policy violations for a single call node (0..2 of them), or `[]` when the node is not a
|
|
34
34
|
# Pundit entry call or its record type is not statically determinable. ADR-37: the engine owns the
|
|
35
35
|
# walk, so this is per-node logic.
|
|
36
|
-
#
|
|
37
|
-
# @param call_node [Prism::Node]
|
|
38
|
-
# @param policy_index [PolicyIndex]
|
|
39
|
-
# @param scope [Rigor::Scope, nil]
|
|
40
|
-
# @return [Array<Violation>]
|
|
41
36
|
def violations_for(call_node:, policy_index:, scope:)
|
|
42
37
|
return [] unless call_node.is_a?(Prism::CallNode) && entry_call?(call_node)
|
|
43
38
|
|
|
@@ -42,7 +42,7 @@ module Rigor
|
|
|
42
42
|
@search_paths = search_paths
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
# @return
|
|
45
|
+
# @return policy class names named by an authorization call, sorted and unique. Not
|
|
46
46
|
# yet intersected with the discovered policies — the caller does that.
|
|
47
47
|
def policy_names
|
|
48
48
|
names = Set.new
|
|
@@ -93,7 +93,7 @@ module Rigor
|
|
|
93
93
|
names << name if name
|
|
94
94
|
end
|
|
95
95
|
|
|
96
|
-
# @return
|
|
96
|
+
# @return the policy class the argument names, or nil when the argument is an
|
|
97
97
|
# expression this reading cannot attribute to a record (a method call with arguments, a literal, an
|
|
98
98
|
# index read). Silence, not a guess: an unattributable call contributes nothing.
|
|
99
99
|
def policy_name_for(node)
|
|
@@ -46,6 +46,7 @@ module Rigor
|
|
|
46
46
|
class Pundit < Rigor::Plugin::Base
|
|
47
47
|
manifest(
|
|
48
48
|
id: "pundit",
|
|
49
|
+
target_gems: ["pundit"],
|
|
49
50
|
# Bumped 2026-08-16 — publishes `:reachability_roots` for `rigor unused` (ADR-102 WD3): the policy
|
|
50
51
|
# classes the project's own `authorize` / `policy` / `policy_scope` calls name by convention.
|
|
51
52
|
version: "0.2.0",
|
|
@@ -91,22 +91,10 @@ module Rigor
|
|
|
91
91
|
RAILS_SHIPPED_KEY_PREFIXES.any? { |prefix| key_str.start_with?(prefix) }
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
-
# @param path [String]
|
|
95
|
-
# @param root [Prism::Node]
|
|
96
|
-
# @param locale_index [LocaleIndex]
|
|
97
|
-
# @param configured_locales [Array<String>]
|
|
98
|
-
# @return [Array<Diagnostic>]
|
|
99
94
|
# The translation violations for a single call node, or `[]` when it is not a recognised `t` /
|
|
100
95
|
# `translate` call with a literal key. ADR-37: the engine owns the walk; `action` (the enclosing
|
|
101
96
|
# method, for lazy `t('.key')` expansion) comes from the node-rule `NodeContext`, and
|
|
102
97
|
# `controller_scope` from the file path.
|
|
103
|
-
#
|
|
104
|
-
# @param call_node [Prism::Node]
|
|
105
|
-
# @param locale_index [LocaleIndex]
|
|
106
|
-
# @param configured_locales [Array<String>]
|
|
107
|
-
# @param controller_scope [String, nil]
|
|
108
|
-
# @param action [String, nil]
|
|
109
|
-
# @return [Array<Violation>]
|
|
110
98
|
def violations_for(call_node:, locale_index:, configured_locales:, controller_scope:, action:)
|
|
111
99
|
return [] unless call_node.is_a?(Prism::CallNode) && translate_call_candidate?(call_node)
|
|
112
100
|
|
|
@@ -36,8 +36,7 @@ module Rigor
|
|
|
36
36
|
|
|
37
37
|
attr_reader :entries, :locales
|
|
38
38
|
|
|
39
|
-
# @param
|
|
40
|
-
# @param locales [Array<String>] all locale names that contributed at least one key.
|
|
39
|
+
# @param locales — all locale names that contributed at least one key.
|
|
41
40
|
def initialize(entries, locales:)
|
|
42
41
|
@entries = entries.freeze
|
|
43
42
|
@locales = locales.dup.freeze
|
|
@@ -45,7 +44,6 @@ module Rigor
|
|
|
45
44
|
freeze
|
|
46
45
|
end
|
|
47
46
|
|
|
48
|
-
# @return [Entry, nil]
|
|
49
47
|
def find(dotted_key)
|
|
50
48
|
@by_key[dotted_key.to_s]
|
|
51
49
|
end
|
|
@@ -57,6 +57,7 @@ module Rigor
|
|
|
57
57
|
class RailsI18n < Rigor::Plugin::Base
|
|
58
58
|
manifest(
|
|
59
59
|
id: "rails-i18n",
|
|
60
|
+
target_gems: ["i18n"],
|
|
60
61
|
# Bumped 2026-06-23 — view template lazy-key scanning (`t('.key')` inside ERB / Haml / Slim under
|
|
61
62
|
# `view_search_paths`).
|
|
62
63
|
version: "0.3.0",
|
|
@@ -24,8 +24,8 @@ module Rigor
|
|
|
24
24
|
module Acronyms
|
|
25
25
|
module_function
|
|
26
26
|
|
|
27
|
-
# @param contents
|
|
28
|
-
# @return
|
|
27
|
+
# @param contents — the source of `config/initializers/inflections.rb`.
|
|
28
|
+
# @return declared acronyms in declaration order. Empty for a missing / unparseable
|
|
29
29
|
# file, or one that declares none — which leaves composition exactly as it was.
|
|
30
30
|
def discover(contents)
|
|
31
31
|
return [] if contents.nil? || contents.empty?
|
|
@@ -51,9 +51,9 @@ module Rigor
|
|
|
51
51
|
# camelization of the acronym appears at a camel-word boundary — `Oauth` in `OauthMetadata` and in
|
|
52
52
|
# `WellKnown::Oauth`, but never the `Oauth` inside a hypothetical `Xoauth`.
|
|
53
53
|
#
|
|
54
|
-
# @param name
|
|
55
|
-
# @param acronyms
|
|
56
|
-
# @return
|
|
54
|
+
# @param name — e.g. `"Activitypub::CollectionsController"`.
|
|
55
|
+
# @param acronyms — as returned by {.discover}.
|
|
56
|
+
# @return e.g. `"ActivityPub::CollectionsController"`.
|
|
57
57
|
def apply(name, acronyms)
|
|
58
58
|
acronyms.reduce(name) do |current, acronym|
|
|
59
59
|
plain = Rigor::Plugin::Inflector.camelize(acronym.to_s.downcase)
|
|
@@ -61,20 +61,11 @@ module Rigor
|
|
|
61
61
|
%r{(?:\A|/)config/}
|
|
62
62
|
].freeze
|
|
63
63
|
|
|
64
|
-
# @param path
|
|
65
|
-
# @param root [Prism::Node]
|
|
66
|
-
# @param helper_table [HelperTable]
|
|
67
|
-
# @return [Array<Diagnostic>]
|
|
64
|
+
# @param path — file being analysed
|
|
68
65
|
# The route-helper violations for a single call node (0..2), or `[]` when the node is not an
|
|
69
66
|
# implicit `*_path` / `*_url` helper call, is shadowed by a same-file binding, or is in a
|
|
70
67
|
# suppressed directory. ADR-37: the engine owns the walk; the same-file `shadowing` set is built
|
|
71
68
|
# once as the node-rule file context.
|
|
72
|
-
#
|
|
73
|
-
# @param call_node [Prism::Node]
|
|
74
|
-
# @param helper_table [HelperTable]
|
|
75
|
-
# @param shadowing [Set<String>]
|
|
76
|
-
# @param path [String]
|
|
77
|
-
# @return [Array<Violation>]
|
|
78
69
|
def violations_for(call_node:, helper_table:, shadowing:, path:)
|
|
79
70
|
return [] unless call_node.is_a?(Prism::CallNode) && implicit_helper_call?(call_node)
|
|
80
71
|
|
|
@@ -70,11 +70,11 @@ module Rigor
|
|
|
70
70
|
|
|
71
71
|
module_function
|
|
72
72
|
|
|
73
|
-
# @param resource
|
|
73
|
+
# @param resource — the `devise_for` argument — typically `:users`. Used as both the
|
|
74
74
|
# helper-name segment (`new_user_session_path`) and to derive the resource path.
|
|
75
|
-
# @param skip
|
|
75
|
+
# @param skip — controllers the project disables via `devise_for :users, skip:
|
|
76
76
|
# [:registrations]`.
|
|
77
|
-
# @return
|
|
77
|
+
# @return one entry per generated `_path` helper. The caller is
|
|
78
78
|
# expected to pair `_url` variants the same way `RoutesParser` does for other entries.
|
|
79
79
|
def generate(resource:, skip: [])
|
|
80
80
|
resource_segment = singularize(resource.to_s)
|
|
@@ -66,8 +66,8 @@ module Rigor
|
|
|
66
66
|
|
|
67
67
|
module_function
|
|
68
68
|
|
|
69
|
-
# @param skip
|
|
70
|
-
# @return
|
|
69
|
+
# @param skip — controller names the project omits via `skip_controllers :name, ...`.
|
|
70
|
+
# @return flattened entries.
|
|
71
71
|
def generate(skip: [])
|
|
72
72
|
skip_set = skip.to_set(&:to_sym)
|
|
73
73
|
CONTROLLER_HELPERS.flat_map do |controller, rows|
|
|
@@ -43,9 +43,9 @@ module Rigor
|
|
|
43
43
|
|
|
44
44
|
module_function
|
|
45
45
|
|
|
46
|
-
# @param contents_per_path
|
|
46
|
+
# @param contents_per_path — file path → source text, read by the caller
|
|
47
47
|
# (through the trusted `IoBoundary`, so cache invalidation works).
|
|
48
|
-
# @return
|
|
48
|
+
# @return the recognised helper-name prefixes (`["api_v3", "api_v4"]`). Empty when
|
|
49
49
|
# the project declares no grape API — nothing changes for such a project.
|
|
50
50
|
def discover(contents_per_path)
|
|
51
51
|
declarations = {}
|
|
@@ -43,10 +43,10 @@ module Rigor
|
|
|
43
43
|
|
|
44
44
|
module_function
|
|
45
45
|
|
|
46
|
-
# @param contents_per_path
|
|
46
|
+
# @param contents_per_path — file path → source text. The caller is
|
|
47
47
|
# responsible for reading files (typically through the trusted `IoBoundary` so cache invalidation
|
|
48
48
|
# works).
|
|
49
|
-
# @return
|
|
49
|
+
# @return method names suitable for inclusion in the `HelperTable`'s custom-helper set.
|
|
50
50
|
def discover(contents_per_path)
|
|
51
51
|
names = []
|
|
52
52
|
contents_per_path.each_value do |contents|
|
|
@@ -26,21 +26,21 @@ module Rigor
|
|
|
26
26
|
|
|
27
27
|
attr_reader :entries, :custom_helpers, :devise_resources
|
|
28
28
|
|
|
29
|
-
# @param entries
|
|
30
|
-
# @param custom_helpers
|
|
29
|
+
# @param entries — freshly built; the factory below is the canonical construction path.
|
|
30
|
+
# @param custom_helpers — names of project-defined helper methods (typically
|
|
31
31
|
# pulled from `app/helpers/**/*.rb` by {HelperDiscoverer}) that the analyzer should treat as
|
|
32
32
|
# known — they do NOT correspond to a route but their presence MUST NOT fire `unknown-helper`. No
|
|
33
33
|
# arity / path metadata is recorded; the analyzer skips the route-side checks for these names.
|
|
34
|
-
# @param devise_resources
|
|
34
|
+
# @param devise_resources — resource names declared via `devise_for :resource`
|
|
35
35
|
# (already singularised, e.g. `"user"`). Used to recognise the dynamic OmniAuth helper family
|
|
36
36
|
# (`<resource>_<provider>_omniauth_(authorize|callback)_(path|url)`) whose provider segment is
|
|
37
37
|
# supplied at runtime — no per-name entry is registered for them but they MUST NOT fire
|
|
38
38
|
# `unknown-helper` either.
|
|
39
|
-
# @param grape_prefixes
|
|
39
|
+
# @param grape_prefixes — helper-name prefixes generated by `grape-path-helpers`
|
|
40
40
|
# (`"api_v4"`), composed by {GrapeApiDiscoverer} from the project's own `prefix` / `version`
|
|
41
41
|
# declarations. The names beyond the prefix come from grape's runtime route table and cannot be
|
|
42
42
|
# enumerated statically, so the namespace is open — same contract as the OmniAuth family above.
|
|
43
|
-
# @param controllers
|
|
43
|
+
# @param controllers — fully-qualified controller class names the routes file
|
|
44
44
|
# dispatches to (`"Admin::UsersController"`). Published as the plugin's `:reachability_roots` fact
|
|
45
45
|
# (ADR-102 WD3) — a controller is an entry point nothing in the project references, so without this
|
|
46
46
|
# every routed controller reads as unused. Deliberately NOT consulted by any helper-name check.
|
|
@@ -58,20 +58,19 @@ module Rigor
|
|
|
58
58
|
freeze
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
-
# @return
|
|
61
|
+
# @return the routed controller class names (see the `controllers:` parameter). Reads
|
|
62
62
|
# through a nil guard rather than `attr_reader` so a table deserialised from a cache slot written
|
|
63
63
|
# before this field existed answers `[]` instead of `nil`.
|
|
64
64
|
def controllers
|
|
65
65
|
@controllers || []
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
-
# @return
|
|
68
|
+
# @return First matching entry; for the uncountable-noun case this is the index helper
|
|
69
69
|
# (the show helper is also registered but starts second).
|
|
70
70
|
def find(helper_name)
|
|
71
71
|
@by_name[helper_name.to_s]&.first
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
-
# @return [Boolean]
|
|
75
74
|
def known?(helper_name)
|
|
76
75
|
@by_name.key?(helper_name.to_s)
|
|
77
76
|
end
|
|
@@ -118,7 +117,7 @@ module Rigor
|
|
|
118
117
|
end
|
|
119
118
|
end
|
|
120
119
|
|
|
121
|
-
# @return
|
|
120
|
+
# @return true when any entry under this helper name accepts the given positional arity.
|
|
122
121
|
#
|
|
123
122
|
# Rails helpers have the signature `helper(*segments, options = {})`, so `expected + 1` is always
|
|
124
123
|
# valid — the extra argument is treated as a query-params/options hash (e.g. `users_path(page: 2)`
|
|
@@ -127,7 +126,7 @@ module Rigor
|
|
|
127
126
|
(@by_name[helper_name.to_s] || []).any? { |entry| entry.arity == arity || entry.arity + 1 == arity }
|
|
128
127
|
end
|
|
129
128
|
|
|
130
|
-
# @return
|
|
129
|
+
# @return all accepted positional arities for a helper name. Empty when unknown.
|
|
131
130
|
def acceptable_arities(helper_name)
|
|
132
131
|
(@by_name[helper_name.to_s] || []).map(&:arity).uniq
|
|
133
132
|
end
|
|
@@ -54,11 +54,10 @@ module Rigor
|
|
|
54
54
|
|
|
55
55
|
module_function
|
|
56
56
|
|
|
57
|
-
# @param contents
|
|
58
|
-
# @param file_reader
|
|
57
|
+
# @param contents — raw `config/routes.rb` source
|
|
58
|
+
# @param file_reader — called with `"name.rb"` to load a
|
|
59
59
|
# draw partial from `config/routes/name.rb`. Returns file contents
|
|
60
60
|
# or nil when the file is absent.
|
|
61
|
-
# @return [HelperTable]
|
|
62
61
|
def parse(contents, file_reader: nil, custom_helpers: [], grape_prefixes: [], acronyms: [])
|
|
63
62
|
parse_result = Prism.parse(contents)
|
|
64
63
|
unless parse_result.errors.empty?
|
|
@@ -52,6 +52,7 @@ module Rigor
|
|
|
52
52
|
class RailsRoutes < Rigor::Plugin::Base
|
|
53
53
|
manifest(
|
|
54
54
|
id: "rails-routes",
|
|
55
|
+
target_gems: %w[actionpack railties],
|
|
55
56
|
# Bumped 2026-05-28 — GitLab FOSS sweep adds: (a) `draw_all :name` support (action_dispatch-draw_all
|
|
56
57
|
# gem; single-file load semantics matching `draw :name`); (b) keyword-style `scope(path:
|
|
57
58
|
# ':project_id', as: :project)` — path read from the `:path` keyword, not only from the positional
|
|
@@ -52,6 +52,7 @@ module Rigor
|
|
|
52
52
|
class Railties < Rigor::Plugin::Base
|
|
53
53
|
manifest(
|
|
54
54
|
id: "railties",
|
|
55
|
+
target_gems: ["railties"],
|
|
55
56
|
# Bumped 2026-09-01 (#534 item 2) — the four `Rails.` singleton readers now return lenient
|
|
56
57
|
# nominals instead of `Dynamic[top]`.
|
|
57
58
|
version: "0.2.0",
|
|
@@ -8,8 +8,18 @@ module Rigor
|
|
|
8
8
|
class RbNaCl < Base
|
|
9
9
|
manifest(
|
|
10
10
|
id: "rbnacl",
|
|
11
|
+
target_gems: ["rbnacl"],
|
|
11
12
|
version: "0.1.0",
|
|
12
|
-
description: "Rigor type support for RbNaCl libsodium bindings"
|
|
13
|
+
description: "Rigor type support for RbNaCl libsodium bindings",
|
|
14
|
+
signature_paths: ["sig"],
|
|
15
|
+
# ADR-26 — `sig/rbnacl.rbs` declares three methods of a class that has many more (`nonce_bytes`,
|
|
16
|
+
# `key_bytes`, …). Contributing it without opening the receivers would close those classes and
|
|
17
|
+
# manufacture `call.undefined-method` on correct code, which is the trade the project refuses.
|
|
18
|
+
open_receivers: [
|
|
19
|
+
"RbNaCl::SecretBox",
|
|
20
|
+
"RbNaCl::Signatures::Ed25519::SigningKey",
|
|
21
|
+
"RbNaCl::Signatures::Ed25519::VerifyKey"
|
|
22
|
+
]
|
|
13
23
|
)
|
|
14
24
|
|
|
15
25
|
# Issue 2 & 5: use robust positional binding extractor and preserve module context
|
|
@@ -28,3 +38,5 @@ module Rigor
|
|
|
28
38
|
end
|
|
29
39
|
end
|
|
30
40
|
end
|
|
41
|
+
|
|
42
|
+
Rigor::Plugin.register(Rigor::Plugin::RbNaCl)
|