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
|
@@ -61,9 +61,33 @@ module Rigor
|
|
|
61
61
|
|
|
62
62
|
COMMENT_LINE = /\A\s*#/
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
# The RBS annotation the synthesizer writes on every member whose type upstream DEFAULTED rather
|
|
65
|
+
# than read off an annotation (issue #823). Its meaning is normative in
|
|
66
|
+
# `docs/type-specification/rbs-extended.md`: the declaration states the member's presence and its
|
|
67
|
+
# parameters, and says nothing about what it returns, so the engine infers the return from the body
|
|
68
|
+
# instead of adopting the placeholder. `Rigor::RbsExtended.inferred_return?` is the reader.
|
|
69
|
+
INFERRED_RETURN_ANNOTATION = "rigor:v1:inferred-return"
|
|
65
70
|
|
|
66
|
-
#
|
|
71
|
+
# The stand-in type handed to upstream's `Writer#default_type`, and the whole reason this plugin can
|
|
72
|
+
# tell a defaulted type from an authored one. `RBS::Inline::Writer` substitutes `default_type`
|
|
73
|
+
# wherever the author wrote nothing — `return_type || default_type` in `RubyDef#method_overloads`,
|
|
74
|
+
# `attribute_type || default_type` in `RubyAttr#rbs` — and leaves an authored type alone. Rendering
|
|
75
|
+
# with a distinctive stand-in therefore makes "the author did not write this" a fact in the output
|
|
76
|
+
# rather than a guess about it: a member whose return renders as this name was defaulted, and one
|
|
77
|
+
# that renders `untyped` was WRITTEN `untyped` by its author (`#: (String) -> untyped`), which is a
|
|
78
|
+
# real contract and stays one.
|
|
79
|
+
#
|
|
80
|
+
# `default_type` is upstream's own public accessor for exactly this substitution, and `Writer.write`
|
|
81
|
+
# yields the writer to configure it, so nothing here reaches into upstream internals. The stand-in
|
|
82
|
+
# never survives into the contributed RBS — {#mark_inferred_returns} rewrites every occurrence back
|
|
83
|
+
# to `untyped` — which is not cosmetic: an undeclared type alias makes `RBS::DefinitionBuilder` raise
|
|
84
|
+
# `NoTypeFoundError` for the whole class, the same failure ADR-93 WD4's Finding 5 measured on
|
|
85
|
+
# `#:nodoc:`.
|
|
86
|
+
DEFAULTED_TYPE_NAME = :rigor__inline_defaulted
|
|
87
|
+
|
|
88
|
+
private_constant :ECHOED_ANNOTATION, :DECLARATION_LINE, :COMMENT_LINE, :DEFAULTED_TYPE_NAME
|
|
89
|
+
|
|
90
|
+
# @param require_magic_comment — when `false` (the default since ADR-93 WD1), the magic
|
|
67
91
|
# comment is not required and the file is processed only if it actually carries an annotation — see
|
|
68
92
|
# {#annotated?}. When `true` (the old ADR-32 WD2 gate), only files opening with
|
|
69
93
|
# `# rbs_inline: enabled` are processed, and upstream's opt-in semantics apply verbatim.
|
|
@@ -95,7 +119,8 @@ module Rigor
|
|
|
95
119
|
return nil if parsed.nil?
|
|
96
120
|
|
|
97
121
|
uses, decls, rbs_decls = parsed
|
|
98
|
-
rendered =
|
|
122
|
+
rendered = mark_inferred_returns(render_with_defaulted_marker(uses, decls, rbs_decls))
|
|
123
|
+
rendered = reattach_declaration_annotations(rendered)
|
|
99
124
|
return nil if rendered.nil? || rendered.strip.empty?
|
|
100
125
|
|
|
101
126
|
notices = unhonoured_annotations(result)
|
|
@@ -109,6 +134,106 @@ module Rigor
|
|
|
109
134
|
|
|
110
135
|
private
|
|
111
136
|
|
|
137
|
+
# Upstream's writer, told to substitute {DEFAULTED_TYPE_NAME} instead of `untyped` wherever the
|
|
138
|
+
# author wrote no type. See that constant for why the distinction is worth a stand-in.
|
|
139
|
+
def render_with_defaulted_marker(uses, decls, rbs_decls)
|
|
140
|
+
marker = ::RBS::Types::Alias.new(
|
|
141
|
+
name: ::RBS::TypeName.new(namespace: ::RBS::Namespace.empty, name: DEFAULTED_TYPE_NAME),
|
|
142
|
+
args: [],
|
|
143
|
+
location: nil
|
|
144
|
+
)
|
|
145
|
+
::RBS::Inline::Writer.write(uses, decls, rbs_decls) { |writer| writer.default_type = marker }
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Issue #823 — the sibling rule. ADR-93 WD1 gates synthesis on a file that carries an annotation;
|
|
149
|
+
# inside such a file upstream emits a full `def f: (untyped x) -> untyped` skeleton for every
|
|
150
|
+
# unannotated `def`, and Rigor trusts an accepted signature over body inference. So one `# @rbs`
|
|
151
|
+
# anywhere in a file used to retype every OTHER method in it to `untyped` — the same mechanism
|
|
152
|
+
# ADR-93 WD1 measured project-wide before the file gate landed (mail 26 → 42 diagnostics), just
|
|
153
|
+
# scoped to the annotated file. An annotation on one method must not change the typing of its
|
|
154
|
+
# siblings in either direction.
|
|
155
|
+
#
|
|
156
|
+
# The skeleton is kept — that is what PR #779's member-level drop got wrong, because a partially
|
|
157
|
+
# declared class reads to RBS as a fully declared one (32 `call.undefined-method` and 12
|
|
158
|
+
# `call.wrong-arity` on `new`, and 44 classes to `Dynamic[top]` behind cross-file references that
|
|
159
|
+
# stopped resolving). What changes is what the skeleton CLAIMS: a defaulted type slot is rewritten
|
|
160
|
+
# back to `untyped` and its member is annotated {INFERRED_RETURN_ANNOTATION}, so the declaration
|
|
161
|
+
# keeps the class's full method surface, `new`'s arity and every cross-file name, and states nothing
|
|
162
|
+
# about the return. The engine reads the annotation in `RbsDispatch` and falls through to the same
|
|
163
|
+
# body-inference tier an undeclared method takes.
|
|
164
|
+
#
|
|
165
|
+
# Two properties make the rewrite safe rather than clever:
|
|
166
|
+
#
|
|
167
|
+
# - the stand-in is a token this plugin injected, so replacing it is exact — it can only appear
|
|
168
|
+
# where upstream substituted it (a comment echoing the same word would merely read `untyped`);
|
|
169
|
+
# - the lines to annotate come from `RBS::Parser` reading the writer's own output, not from a
|
|
170
|
+
# pattern over it, and the replacement is length-preserving per line, so the parser's line numbers
|
|
171
|
+
# still address the same members after it.
|
|
172
|
+
#
|
|
173
|
+
# Marking is best-effort and the rewrite is not: if the probe parse is skipped or fails, the file
|
|
174
|
+
# still ships with today's `untyped` skeleton rather than an unresolvable alias (see
|
|
175
|
+
# {DEFAULTED_TYPE_NAME}). That asymmetry is why the encoding guard is shaped the way it is. Invalid
|
|
176
|
+
# UTF-8 does not reach here today — the RDoc-directive scan above raises on it first, and {#call}'s
|
|
177
|
+
# rescue routes the file to WD6 — but the guarded parse is the one `RbsLoader.add_virtual_rbs`
|
|
178
|
+
# documents as HANG-prone on pre-4.1 rbs lexers, and a hang escapes every rescue there is. So the
|
|
179
|
+
# probe is skipped for a byte sequence it could hang on, while the substitution runs over bytes and
|
|
180
|
+
# therefore always completes.
|
|
181
|
+
def mark_inferred_returns(rendered)
|
|
182
|
+
return rendered if rendered.nil? || !rendered.b.include?(DEFAULTED_TYPE_NAME.to_s)
|
|
183
|
+
|
|
184
|
+
marked_lines = rendered.valid_encoding? ? defaulted_member_lines(rendered) : Set.new
|
|
185
|
+
scrubbed = rendered.b.gsub(DEFAULTED_TYPE_NAME.to_s, "untyped").force_encoding(rendered.encoding)
|
|
186
|
+
return scrubbed if marked_lines.empty?
|
|
187
|
+
|
|
188
|
+
scrubbed.lines.each_with_index.flat_map do |line, index|
|
|
189
|
+
next line unless marked_lines.include?(index + 1)
|
|
190
|
+
|
|
191
|
+
["#{line[/\A[ \t]*/]}%a{#{INFERRED_RETURN_ANNOTATION}}\n", line]
|
|
192
|
+
end.join
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# The 1-based line of every member in `rendered` whose return type (or, for an attribute, whose
|
|
196
|
+
# type) is the defaulted stand-in. A member's `location.start_line` is the line its `def` /
|
|
197
|
+
# `attr_*` keyword sits on, which is where the annotation has to go.
|
|
198
|
+
def defaulted_member_lines(rendered)
|
|
199
|
+
buffer = ::RBS::Buffer.new(name: "(rigor: inline defaulted-type probe)", content: rendered)
|
|
200
|
+
_, _directives, decls = ::RBS::Parser.parse_signature(buffer)
|
|
201
|
+
collect_defaulted_member_lines(decls, Set.new)
|
|
202
|
+
rescue ::StandardError
|
|
203
|
+
# The writer's own output failing to parse is a condition the loader already handles (ADR-32 WD6
|
|
204
|
+
# fail-soft drops the entry). Declining to mark here keeps that the only failure.
|
|
205
|
+
Set.new
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def collect_defaulted_member_lines(nodes, lines)
|
|
209
|
+
nodes.each do |node|
|
|
210
|
+
next unless node.respond_to?(:members)
|
|
211
|
+
|
|
212
|
+
node.members.each do |member|
|
|
213
|
+
line = defaulted_member_line(member)
|
|
214
|
+
lines << line if line
|
|
215
|
+
end
|
|
216
|
+
collect_defaulted_member_lines(node.members.grep(::RBS::AST::Declarations::Base), lines)
|
|
217
|
+
end
|
|
218
|
+
lines
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def defaulted_member_line(member)
|
|
222
|
+
case member
|
|
223
|
+
when ::RBS::AST::Members::MethodDefinition
|
|
224
|
+
return nil unless member.overloads.any? { |o| defaulted_type?(o.method_type.type.return_type) }
|
|
225
|
+
when ::RBS::AST::Members::Attribute
|
|
226
|
+
return nil unless defaulted_type?(member.type)
|
|
227
|
+
else
|
|
228
|
+
return nil
|
|
229
|
+
end
|
|
230
|
+
member.location&.start_line
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def defaulted_type?(type)
|
|
234
|
+
type.is_a?(::RBS::Types::Alias) && type.name.name == DEFAULTED_TYPE_NAME
|
|
235
|
+
end
|
|
236
|
+
|
|
112
237
|
# Re-attaches a class- or module-level `%a{…}` that upstream's writer dropped (#452).
|
|
113
238
|
#
|
|
114
239
|
# `RBS::Inline::Writer` emits a member's annotations as real annotation lines and a *declaration's*
|
|
@@ -45,13 +45,11 @@ module Rigor
|
|
|
45
45
|
# ADR-52 slice 5a — every `let` / `subject` name declared anywhere in the file, across all describe
|
|
46
46
|
# scopes. Feeds the plugin's `dynamic_return file_methods:` gate: the engine only consults the rule
|
|
47
47
|
# for a call whose name appears here; the precise line-scoped resolution stays in `let_block_at`.
|
|
48
|
-
# @return [Array<Symbol>]
|
|
49
48
|
def let_names
|
|
50
49
|
@records.flat_map { |rec| rec.lets.keys }.uniq
|
|
51
50
|
end
|
|
52
51
|
|
|
53
52
|
# Resolves a `let` name at the given line by walking records innermost to outermost.
|
|
54
|
-
# @return [Prism::BlockNode, nil]
|
|
55
53
|
def let_block_at(line, name)
|
|
56
54
|
name_sym = name.to_sym
|
|
57
55
|
records_at(line).reverse.each do |rec|
|
|
@@ -62,7 +60,6 @@ module Rigor
|
|
|
62
60
|
|
|
63
61
|
# Resolves the `describe`-anchor constant name at the given line. The innermost describe with a
|
|
64
62
|
# constant anchor wins; describe-with-String anchors are skipped.
|
|
65
|
-
# @return [String, nil]
|
|
66
63
|
def describe_const_at(line)
|
|
67
64
|
records_at(line).reverse.each do |rec|
|
|
68
65
|
return rec.describe_const if rec.describe_const
|
|
@@ -30,12 +30,8 @@ module Rigor
|
|
|
30
30
|
|
|
31
31
|
module_function
|
|
32
32
|
|
|
33
|
-
# @param
|
|
34
|
-
# @param describe_const [String, nil]
|
|
35
|
-
# @param factory_index [Object, nil] something responding to `find(factory_name)` and returning an
|
|
33
|
+
# @param factory_index — something responding to `find(factory_name)` and returning an
|
|
36
34
|
# entry that responds to `model_class`.
|
|
37
|
-
# @param environment [Rigor::Environment, nil]
|
|
38
|
-
# @return [Rigor::Type, nil]
|
|
39
35
|
def resolve(block_node, describe_const:, factory_index:, environment:)
|
|
40
36
|
tail = body_tail(block_node)
|
|
41
37
|
return nil if tail.nil?
|
|
@@ -38,13 +38,12 @@ module Rigor
|
|
|
38
38
|
module MatcherAnalyzer
|
|
39
39
|
module_function
|
|
40
40
|
|
|
41
|
-
# @param call_node
|
|
41
|
+
# @param call_node — the call whose contribution we're computing. Returns nil when
|
|
42
42
|
# the call shape does not match `expect(local).to matcher`.
|
|
43
|
-
# @param environment
|
|
43
|
+
# @param environment — the surrounding environment used to resolve a
|
|
44
44
|
# matcher's class-name argument to a `Type::Nominal`. When nil, class-name resolution falls back
|
|
45
45
|
# to a bare `Nominal[<name>]` carrier (sound — the receiver constant may be a user class not in
|
|
46
46
|
# RBS).
|
|
47
|
-
# @return [Rigor::FlowContribution, nil]
|
|
48
47
|
def contribution_for(call_node, environment:)
|
|
49
48
|
verb = assertion_verb(call_node)
|
|
50
49
|
return nil if verb.nil?
|
|
@@ -48,6 +48,7 @@ module Rigor
|
|
|
48
48
|
class Rspec < Rigor::Plugin::Base
|
|
49
49
|
manifest(
|
|
50
50
|
id: "rspec",
|
|
51
|
+
target_gems: ["rspec-core"],
|
|
51
52
|
version: "0.3.0",
|
|
52
53
|
description: "Validates RSpec `let` / `subject` declarations within each scope; " \
|
|
53
54
|
"narrows expect(x).to <matcher> assertions downstream in `it` bodies; " \
|
|
@@ -40,9 +40,6 @@ module Rigor
|
|
|
40
40
|
# The matcher violation for a single CallNode, or nil when the node is not a
|
|
41
41
|
# `have_http_status(<int|symbol>)` matcher or its argument is statically valid. ADR-37: the engine
|
|
42
42
|
# owns the walk, so this is per-node logic — no traversal here.
|
|
43
|
-
#
|
|
44
|
-
# @param call_node [Prism::Node]
|
|
45
|
-
# @return [Violation, nil]
|
|
46
43
|
def violation_for(call_node)
|
|
47
44
|
return nil unless call_to_matcher?(call_node)
|
|
48
45
|
|
|
@@ -72,23 +72,15 @@ module Rigor
|
|
|
72
72
|
|
|
73
73
|
module_function
|
|
74
74
|
|
|
75
|
-
# @param
|
|
76
|
-
# @param root [Prism::Node]
|
|
77
|
-
# @param model_index [Hash{String => Hash}, nil] the `:model_index` fact value — the flat
|
|
75
|
+
# @param model_index — the `:model_index` fact value — the flat
|
|
78
76
|
# `class_name => { table:, columns:, associations:, ... }` Hash `rigor-activerecord` publishes
|
|
79
77
|
# (ADR-9's "the value is data, not objects" contract; see `Activerecord#index_to_published_hash`).
|
|
80
78
|
# When nil the analyzer falls silent.
|
|
81
|
-
# @return [Array<Diagnostic>]
|
|
82
79
|
# The matcher violations for a single call node (0..1), or `[]` when it is not a shoulda matcher,
|
|
83
80
|
# sits outside any `describe <ModelConst>` block, or the model is unknown. ADR-37: the engine owns
|
|
84
81
|
# the walk; the model anchor comes from the node-rule `NodeContext` ancestors (the innermost
|
|
85
82
|
# enclosing `describe`-with-constant — nested const describes override, `describe ".method"`
|
|
86
83
|
# string blocks inherit), exactly as the former recursive anchor propagation did.
|
|
87
|
-
#
|
|
88
|
-
# @param matcher_call [Prism::Node]
|
|
89
|
-
# @param ancestors [Array<Prism::Node>]
|
|
90
|
-
# @param model_index [Hash{String => Hash}, nil]
|
|
91
|
-
# @return [Array<Violation>]
|
|
92
84
|
def violations_for(matcher_call:, ancestors:, model_index:)
|
|
93
85
|
return [] if model_index.nil?
|
|
94
86
|
return [] unless matcher_invocation?(matcher_call)
|
|
@@ -47,6 +47,7 @@ module Rigor
|
|
|
47
47
|
class ShouldaMatchers < Rigor::Plugin::Base
|
|
48
48
|
manifest(
|
|
49
49
|
id: "shoulda-matchers",
|
|
50
|
+
target_gems: ["shoulda-matchers"],
|
|
50
51
|
version: "0.1.0",
|
|
51
52
|
description: "Validates shoulda-matchers matchers (validate_presence_of / belong_to / " \
|
|
52
53
|
"have_many / have_db_column / ...) against :model_index from " \
|
|
@@ -34,10 +34,6 @@ module Rigor
|
|
|
34
34
|
|
|
35
35
|
# The worker-call violations for a single call node (0..2), or `[]` when the node is not a
|
|
36
36
|
# `<Worker>.perform_*` entry call on a known worker. ADR-37: the engine owns the walk.
|
|
37
|
-
#
|
|
38
|
-
# @param call_node [Prism::Node]
|
|
39
|
-
# @param worker_index [WorkerIndex]
|
|
40
|
-
# @return [Array<Violation>]
|
|
41
37
|
def violations_for(call_node:, worker_index:)
|
|
42
38
|
return [] unless call_node.is_a?(Prism::CallNode) && entry_call?(call_node)
|
|
43
39
|
|
|
@@ -52,7 +52,7 @@ module Rigor
|
|
|
52
52
|
|
|
53
53
|
module_function
|
|
54
54
|
|
|
55
|
-
# @param marker_modules
|
|
55
|
+
# @param marker_modules — the modules a worker includes — the plugin's own
|
|
56
56
|
# `worker_marker_modules:` config, so one project setting drives the arity check and these rows.
|
|
57
57
|
def attributions(marker_modules)
|
|
58
58
|
marker_modules.flat_map do |marker|
|
|
@@ -51,7 +51,7 @@ module Rigor
|
|
|
51
51
|
@schedule_paths = schedule_paths
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
# @return
|
|
54
|
+
# @return the class names named by a `class:` key in a schedule entry, sorted and
|
|
55
55
|
# unique. NOT yet intersected with the discovered workers — the caller does that.
|
|
56
56
|
def worker_names
|
|
57
57
|
names = Set.new
|
|
@@ -39,8 +39,6 @@ module Rigor
|
|
|
39
39
|
# never `"::WelcomeWorker"`; see {WorkerDiscoverer}), while a QUERY may legitimately arrive rooted:
|
|
40
40
|
# `::WelcomeWorker.perform_async(1)` renders its receiver as `"::WelcomeWorker"`. 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
|
|
@@ -54,6 +54,7 @@ module Rigor
|
|
|
54
54
|
class Sidekiq < Rigor::Plugin::Base
|
|
55
55
|
manifest(
|
|
56
56
|
id: "sidekiq",
|
|
57
|
+
target_gems: ["sidekiq"],
|
|
57
58
|
# Bumped 2026-08-16 — publishes `:reachability_roots` for `rigor unused` (ADR-102 WD3): the workers
|
|
58
59
|
# a schedule file enqueues by name, which no `perform_async` call site writes down.
|
|
59
60
|
# Bumped 2026-09-01 (#534 item 4) — the enqueue methods return the jid `String`.
|
|
@@ -37,8 +37,7 @@ module Rigor
|
|
|
37
37
|
# run, so the same Prism node object is seen in both the `dynamic_return` rule and
|
|
38
38
|
# `diagnostics_for_file`.
|
|
39
39
|
module AbsurdRecognizer
|
|
40
|
-
# @
|
|
41
|
-
# @return [Boolean] true when `call_node` is `T.absurd(x)`.
|
|
40
|
+
# @return true when `call_node` is `T.absurd(x)`.
|
|
42
41
|
def self.absurd_call?(call_node)
|
|
43
42
|
return false unless call_node.is_a?(Prism::CallNode)
|
|
44
43
|
return false unless call_node.name == :absurd
|
|
@@ -50,9 +49,7 @@ module Rigor
|
|
|
50
49
|
arguments&.size == 1
|
|
51
50
|
end
|
|
52
51
|
|
|
53
|
-
# @
|
|
54
|
-
# @param scope [Rigor::Scope, nil]
|
|
55
|
-
# @return [Boolean] true when the discriminant has been narrowed to `bot` (the branch is
|
|
52
|
+
# @return true when the discriminant has been narrowed to `bot` (the branch is
|
|
56
53
|
# unreachable, so `T.absurd` is correct). The caller suppresses the `absurd-reachable` diagnostic
|
|
57
54
|
# in this case.
|
|
58
55
|
def self.exhaustive?(call_node, scope)
|
|
@@ -53,10 +53,7 @@ module Rigor
|
|
|
53
53
|
|
|
54
54
|
module_function
|
|
55
55
|
|
|
56
|
-
# @param
|
|
57
|
-
# @param scope [Rigor::Scope]
|
|
58
|
-
# @param plugin_id [String] used for the contribution's `provenance.source_family`.
|
|
59
|
-
# @return [Rigor::FlowContribution, nil]
|
|
56
|
+
# @param plugin_id — used for the contribution's `provenance.source_family`.
|
|
60
57
|
def recognize(call_node:, scope:, plugin_id:)
|
|
61
58
|
return nil unless TypeTranslator.sorbet_t_namespaced?(call_node.receiver)
|
|
62
59
|
return nil unless SORBET_ASSERTIONS.include?(call_node.name)
|
|
@@ -23,7 +23,6 @@ module Rigor
|
|
|
23
23
|
@frozen_after_build = false
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
# @param signature [MethodSignature]
|
|
27
26
|
def record(signature)
|
|
28
27
|
raise "Catalog already finalised" if @frozen_after_build
|
|
29
28
|
|
|
@@ -31,10 +30,9 @@ module Rigor
|
|
|
31
30
|
@entries[key] = signature
|
|
32
31
|
end
|
|
33
32
|
|
|
34
|
-
# @param class_name
|
|
33
|
+
# @param class_name — the class / module that carries the mixin (`class Post; include Foo;
|
|
35
34
|
# end` records under `"Post"`).
|
|
36
|
-
# @param
|
|
37
|
-
# @param module_name [String] the textual name of the mixed-in module as it appeared at the
|
|
35
|
+
# @param module_name — the textual name of the mixed-in module as it appeared at the
|
|
38
36
|
# include / extend site (`"Foo"`, `"Foo::Bar"`, `"::Foo"`).
|
|
39
37
|
def record_mixin(class_name:, kind:, module_name:)
|
|
40
38
|
raise "Catalog already finalised" if @frozen_after_build
|
|
@@ -55,13 +53,11 @@ module Rigor
|
|
|
55
53
|
freeze
|
|
56
54
|
end
|
|
57
55
|
|
|
58
|
-
# @return [MethodSignature, nil]
|
|
59
56
|
def lookup(class_name:, method_name:, kind:)
|
|
60
57
|
@entries[key_for(class_name, method_name, kind)]
|
|
61
58
|
end
|
|
62
59
|
|
|
63
|
-
# @
|
|
64
|
-
# @return [Hash{Symbol => Array<String>}] frozen mapping `{ include: [...], extend: [...] }`.
|
|
60
|
+
# @return frozen mapping `{ include: [...], extend: [...] }`.
|
|
65
61
|
# Returns {EMPTY_MIXINS} when no mixins were recorded.
|
|
66
62
|
def mixins_for(class_name)
|
|
67
63
|
@mixins[class_name] || EMPTY_MIXINS
|
|
@@ -77,8 +73,6 @@ module Rigor
|
|
|
77
73
|
# static assertion vocabulary), and the precise `(class, kind)` lookup stays in the rule block.
|
|
78
74
|
# Computed fresh per call — the plugin memoises the resolved set, and `freeze!` freezes the
|
|
79
75
|
# catalog itself so a lazy memo ivar here would raise.
|
|
80
|
-
#
|
|
81
|
-
# @return [Array<Symbol>]
|
|
82
76
|
def method_names
|
|
83
77
|
@entries.keys.map { |key| key[1] }.uniq
|
|
84
78
|
end
|
|
@@ -26,10 +26,10 @@ module Rigor
|
|
|
26
26
|
|
|
27
27
|
module_function
|
|
28
28
|
|
|
29
|
-
# @param root
|
|
30
|
-
# @param catalog
|
|
31
|
-
# @param path
|
|
32
|
-
# @return
|
|
29
|
+
# @param root — the file's program node.
|
|
30
|
+
# @param catalog — mutable; signatures are recorded into it.
|
|
31
|
+
# @param path — file path used for diagnostic provenance.
|
|
32
|
+
# @return errors observed during the walk; empty when the file is sig-clean.
|
|
33
33
|
def walk(root:, catalog:, path:)
|
|
34
34
|
state = State.new(catalog: catalog, path: path)
|
|
35
35
|
walk_node(root, state, lexical_path: [], in_singleton_class: false)
|
|
@@ -44,8 +44,7 @@ module Rigor
|
|
|
44
44
|
|
|
45
45
|
module_function
|
|
46
46
|
|
|
47
|
-
# @param sig_call
|
|
48
|
-
# @return [ParseResult, ParseError]
|
|
47
|
+
# @param sig_call — the `sig { ... }` / `sig do ... end` call.
|
|
49
48
|
def parse(sig_call)
|
|
50
49
|
return ParseError.new(reason: :no_block, node: sig_call) if sig_call.block.nil?
|
|
51
50
|
|
|
@@ -30,8 +30,8 @@ module Rigor
|
|
|
30
30
|
|
|
31
31
|
module_function
|
|
32
32
|
|
|
33
|
-
# @param contents
|
|
34
|
-
# @return
|
|
33
|
+
# @param contents — raw file contents.
|
|
34
|
+
# @return one of {VALID_LEVELS}; defaults to {DEFAULT_LEVEL} for sigil-less or
|
|
35
35
|
# malformed-sigil files.
|
|
36
36
|
def detect(contents)
|
|
37
37
|
return DEFAULT_LEVEL if contents.nil? || contents.empty?
|
|
@@ -52,14 +52,13 @@ module Rigor
|
|
|
52
52
|
DEFAULT_LEVEL
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
# @
|
|
56
|
-
# @return [Boolean] true when `# typed: ignore`. The harvest pipeline calls this to short-circuit
|
|
55
|
+
# @return true when `# typed: ignore`. The harvest pipeline calls this to short-circuit
|
|
57
56
|
# walking the file's AST.
|
|
58
57
|
def ignored?(level)
|
|
59
58
|
level == :ignore
|
|
60
59
|
end
|
|
61
60
|
|
|
62
|
-
# @return
|
|
61
|
+
# @return true when `level` is at or above the `# typed: true` mark. Used by the
|
|
63
62
|
# `enforce_sigil` config gate (default `true`): with the gate on, only files marked `:true` /
|
|
64
63
|
# `:strict` / `:strong` contribute their sigs to the catalog. The `:false` (and sigil-less)
|
|
65
64
|
# levels still get walked (so RBI files outside the project can be loaded regardless), but their
|
|
@@ -52,8 +52,7 @@ module Rigor
|
|
|
52
52
|
|
|
53
53
|
module_function
|
|
54
54
|
|
|
55
|
-
# @
|
|
56
|
-
# @return [Rigor::Type] never `nil`; unrecognised forms degrade to `Type::Combinator.untyped`.
|
|
55
|
+
# @return never `nil`; unrecognised forms degrade to `Type::Combinator.untyped`.
|
|
57
56
|
def translate(node)
|
|
58
57
|
return Rigor::Type::Combinator.untyped if node.nil?
|
|
59
58
|
|
|
@@ -67,7 +66,6 @@ module Rigor
|
|
|
67
66
|
end
|
|
68
67
|
end
|
|
69
68
|
|
|
70
|
-
# @param node [Prism::ConstantReadNode]
|
|
71
69
|
def translate_constant_read(node)
|
|
72
70
|
name = node.name.to_s
|
|
73
71
|
return Rigor::Type::Combinator.untyped if name.empty?
|
|
@@ -75,7 +73,6 @@ module Rigor
|
|
|
75
73
|
Rigor::Type::Combinator.nominal_of(name)
|
|
76
74
|
end
|
|
77
75
|
|
|
78
|
-
# @param node [Prism::ConstantPathNode]
|
|
79
76
|
def translate_constant_path(node)
|
|
80
77
|
name = constant_path_name(node)
|
|
81
78
|
return degraded if name.nil?
|
|
@@ -233,7 +230,12 @@ module Rigor
|
|
|
233
230
|
|
|
234
231
|
pairs << [element.key.unescaped.to_sym, translate(element.value)]
|
|
235
232
|
end
|
|
236
|
-
|
|
233
|
+
# `.to_h`: `hash_shape_of` validates its argument is a Hash and raises `ArgumentError` on the
|
|
234
|
+
# array-of-pairs form, so `sig { returns({name: String}) }` degraded to `Dynamic[top]` through
|
|
235
|
+
# the plugin's rescue instead of producing a shape. Found by #673, when relaxing
|
|
236
|
+
# `call.argument-type-mismatch` to check the positional arguments of keyword-bearing
|
|
237
|
+
# signatures made the call site visible.
|
|
238
|
+
Rigor::Type::Combinator.hash_shape_of(pairs.to_h)
|
|
237
239
|
end
|
|
238
240
|
|
|
239
241
|
# Renders a constant-path node (`Foo::Bar`, `::Foo::Bar`) as a `::`-joined String. Mirrors the
|
|
@@ -57,6 +57,7 @@ module Rigor
|
|
|
57
57
|
class Sorbet < Rigor::Plugin::Base
|
|
58
58
|
manifest(
|
|
59
59
|
id: "sorbet",
|
|
60
|
+
target_gems: ["sorbet-runtime"],
|
|
60
61
|
version: "0.1.0",
|
|
61
62
|
description: "Ingests Sorbet `sig` blocks as method-signature contributions.",
|
|
62
63
|
config_schema: {
|
|
@@ -442,11 +443,10 @@ module Rigor
|
|
|
442
443
|
EMPTY_CATALOG_BUNDLE = { catalog: Catalog.new.freeze!, sigil_by_path: {}, parse_errors_by_path: {} }.freeze
|
|
443
444
|
private_constant :EMPTY_CATALOG_BUNDLE
|
|
444
445
|
|
|
445
|
-
# @param root
|
|
446
|
-
# @param
|
|
447
|
-
# @param
|
|
448
|
-
# @param
|
|
449
|
-
# @param extensions [Array<String>] file extensions to accept (e.g. `[".rb"]` for project source,
|
|
446
|
+
# @param root — directory or single file.
|
|
447
|
+
# @param sigil_by_path — accumulator: harvested file → detected sigil level.
|
|
448
|
+
# @param parse_errors_by_path — accumulator: file → `{kind:,line:,column:}` tuples.
|
|
449
|
+
# @param extensions — file extensions to accept (e.g. `[".rb"]` for project source,
|
|
450
450
|
# `[".rbi"]` for Sorbet RBI tree).
|
|
451
451
|
def harvest_path(root, catalog, sigil_by_path, parse_errors_by_path, extensions)
|
|
452
452
|
absolute = canonicalize(root)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module Rigor
|
|
2
|
+
module Analysis
|
|
3
|
+
module Reachability
|
|
4
|
+
class ScanCache
|
|
5
|
+
def self.open: (untyped, ?target_ruby: untyped) -> Rigor::Analysis::Reachability::ScanCache
|
|
6
|
+
def self.source_digest: () -> String
|
|
7
|
+
def initialize: (path: untyped, header: untyped) -> void
|
|
8
|
+
def save: () -> nil
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
data/sig/rigor/cache.rbs
CHANGED
|
@@ -16,9 +16,8 @@ module Rigor
|
|
|
16
16
|
def self.fetch: (loader: untyped, store: untyped) -> untyped
|
|
17
17
|
|
|
18
18
|
# The generation cap this producer declares to `Cache::Store#evict!`
|
|
19
|
-
# (issue #151).
|
|
20
|
-
#
|
|
21
|
-
# override it.
|
|
19
|
+
# (issue #151). The base body answers `2`; the contract is the wider
|
|
20
|
+
# `Integer`, since the cap is a knob a subclass may turn.
|
|
22
21
|
def self.generation_cap: () -> Integer
|
|
23
22
|
end
|
|
24
23
|
end
|
data/sig/rigor/environment.rbs
CHANGED
|
@@ -33,6 +33,7 @@ module Rigor
|
|
|
33
33
|
def constant_for_name: (String | Symbol name) -> Type::t?
|
|
34
34
|
def class_known?: (String | Symbol name) -> bool
|
|
35
35
|
def rbs_module?: (String | Symbol name) -> bool
|
|
36
|
+
def singleton_extended_modules: (String | Symbol name) -> Array[String]
|
|
36
37
|
def class_ordering: (String | Symbol lhs, String | Symbol rhs) -> ordering
|
|
37
38
|
def reflection: () -> untyped?
|
|
38
39
|
def missing_rbs_gem_owner: (String | Symbol root_constant_name) -> String?
|
|
@@ -53,11 +54,14 @@ module Rigor
|
|
|
53
54
|
attr_reader libraries: Array[String]
|
|
54
55
|
attr_reader signature_paths: Array[String | _ToPath]
|
|
55
56
|
attr_reader cache_store: untyped?
|
|
57
|
+
# sig-gen gap: #160 — sig-gen emits no `attr_reader` for the loader's readers; typed as the initializer stores it.
|
|
58
|
+
attr_reader deferred_signature_paths: Array[Pathname]
|
|
56
59
|
|
|
57
60
|
def self.default: () -> RbsLoader
|
|
58
61
|
def self.reset_default!: () -> void
|
|
59
62
|
def self.build_env_for: (libraries: Array[String], signature_paths: Array[String | _ToPath]) -> untyped
|
|
60
63
|
def self.vendored_gem_sig_paths: () -> Array[Pathname]
|
|
64
|
+
def self.capability_role_sig_paths: () -> ([Pathname] | [])
|
|
61
65
|
def self.vendored_gem_names: () -> Array[String]
|
|
62
66
|
|
|
63
67
|
def initialize: (?libraries: Array[String], ?signature_paths: Array[String | _ToPath], ?cache_store: untyped?) -> void
|
|
@@ -72,6 +76,7 @@ module Rigor
|
|
|
72
76
|
def project_declared_class?: (String | Symbol class_name) -> bool
|
|
73
77
|
def class_ordering: (String | Symbol lhs, String | Symbol rhs) -> ordering
|
|
74
78
|
def ancestor_names_for: (String | Symbol class_name) -> Array[String]
|
|
79
|
+
def singleton_extended_module_names: (String | Symbol class_name) -> Array[String]
|
|
75
80
|
def constant_type: (String name) -> Type::t?
|
|
76
81
|
def constant_names: () -> Array[String]
|
|
77
82
|
def each_known_class_name: () { (String name) -> void } -> untyped
|