rigortype 0.4.6 → 0.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/data/gem_overlay/activesupport/core_ext.rbs +16 -13
- data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
- data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
- data/docs/handbook/01-getting-started.md +5 -2
- data/docs/handbook/11-sig-gen.md +50 -3
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/manual/02-cli-reference.md +152 -26
- data/docs/manual/03-configuration.md +36 -3
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/09-editor-integration.md +13 -2
- data/docs/manual/10-mcp-server.md +38 -2
- data/docs/manual/12-caching.md +4 -3
- data/docs/manual/plugins/rigor-actionpack.md +19 -11
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +20 -1
- data/lib/rigor/analysis/baseline.rb +24 -27
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +4 -4
- data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +85 -13
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +3 -2
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +0 -1
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +1 -1
- data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +3 -3
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
- data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules.rb +124 -44
- data/lib/rigor/analysis/crash_signature.rb +2 -2
- data/lib/rigor/analysis/dependency_recorder.rb +4 -4
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/edit_replay.rb +93 -6
- data/lib/rigor/analysis/fact_store.rb +3 -3
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +2 -2
- data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
- data/lib/rigor/analysis/incremental_session.rb +27 -16
- data/lib/rigor/analysis/rule_catalog.rb +2 -2
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +2 -2
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -1
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +193 -93
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +1 -1
- data/lib/rigor/analysis/runner/pool_coordinator.rb +147 -323
- data/lib/rigor/analysis/runner/project_pre_passes.rb +3 -3
- data/lib/rigor/analysis/runner/run_level_facts.rb +304 -0
- data/lib/rigor/analysis/runner/run_snapshots.rb +44 -54
- data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
- data/lib/rigor/analysis/runner.rb +77 -71
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +1 -1
- data/lib/rigor/analysis/template_unit_positions.rb +2 -3
- data/lib/rigor/analysis/template_units.rb +9 -1
- data/lib/rigor/analysis/worker_session.rb +24 -31
- data/lib/rigor/bleeding_edge.rb +3 -6
- data/lib/rigor/builtins/imported_refinements.rb +6 -8
- data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
- data/lib/rigor/builtins/regex_refinement.rb +4 -6
- data/lib/rigor/cache/descriptor.rb +6 -5
- data/lib/rigor/cache/incremental_snapshot.rb +31 -12
- data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
- data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
- data/lib/rigor/cache/rbs_constant_table.rb +1 -1
- data/lib/rigor/cache/rbs_descriptor.rb +54 -3
- data/lib/rigor/cache/rbs_environment.rb +4 -1
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
- data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
- data/lib/rigor/cache/store.rb +11 -10
- data/lib/rigor/ci_detector.rb +1 -1
- data/lib/rigor/cli/annotate_command.rb +24 -19
- data/lib/rigor/cli/baseline_command.rb +145 -55
- data/lib/rigor/cli/catalogue.rb +256 -0
- data/lib/rigor/cli/check_command.rb +108 -102
- data/lib/rigor/cli/check_runner_factory.rb +2 -2
- data/lib/rigor/cli/command.rb +15 -1
- data/lib/rigor/cli/coverage_command.rb +21 -8
- data/lib/rigor/cli/coverage_mutation.rb +5 -7
- data/lib/rigor/cli/coverage_renderer.rb +2 -1
- data/lib/rigor/cli/coverage_scan.rb +3 -5
- data/lib/rigor/cli/diff_command.rb +25 -20
- data/lib/rigor/cli/docs_command.rb +1 -2
- data/lib/rigor/cli/doctor_command.rb +12 -12
- data/lib/rigor/cli/effects_command.rb +50 -40
- data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
- data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
- data/lib/rigor/cli/effects_renderer.rb +2 -1
- data/lib/rigor/cli/effects_snapshot_command.rb +18 -17
- data/lib/rigor/cli/explain_command.rb +18 -8
- data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
- data/lib/rigor/cli/json_document.rb +41 -0
- data/lib/rigor/cli/lens_command.rb +12 -8
- data/lib/rigor/cli/lsp_command.rb +250 -23
- data/lib/rigor/cli/mcp_command.rb +12 -7
- data/lib/rigor/cli/mutation_fork_scan.rb +1 -1
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -2
- data/lib/rigor/cli/options.rb +40 -1
- data/lib/rigor/cli/plugin_command.rb +93 -46
- data/lib/rigor/cli/plugins_command.rb +17 -18
- data/lib/rigor/cli/plugins_renderer.rb +10 -6
- data/lib/rigor/cli/protection_fork_scan.rb +2 -2
- data/lib/rigor/cli/protection_renderer.rb +2 -1
- data/lib/rigor/cli/show_bleedingedge_command.rb +21 -15
- data/lib/rigor/cli/sig_gen_command.rb +84 -27
- data/lib/rigor/cli/skill_command.rb +32 -3
- data/lib/rigor/cli/skill_deep_probe.rb +1 -1
- data/lib/rigor/cli/skill_describe.rb +1 -1
- data/lib/rigor/cli/trace_command.rb +14 -9
- data/lib/rigor/cli/triage_command.rb +10 -11
- data/lib/rigor/cli/triage_renderer.rb +2 -1
- data/lib/rigor/cli/type_of_command.rb +16 -11
- data/lib/rigor/cli/type_of_project_scope.rb +1 -4
- data/lib/rigor/cli/type_of_renderer.rb +4 -1
- data/lib/rigor/cli/type_scan_command.rb +14 -9
- data/lib/rigor/cli/type_scan_renderer.rb +2 -1
- data/lib/rigor/cli/unused_command.rb +27 -17
- data/lib/rigor/cli/upgrade_command.rb +5 -0
- data/lib/rigor/cli.rb +209 -54
- data/lib/rigor/config_audit.rb +1 -1
- data/lib/rigor/configuration/severity_profile.rb +5 -5
- data/lib/rigor/configuration.rb +72 -30
- data/lib/rigor/effects/definition_context.rb +0 -2
- data/lib/rigor/effects/envelope.rb +1 -1
- data/lib/rigor/effects/framework_units.rb +1 -1
- data/lib/rigor/effects/mutation_classifier.rb +4 -7
- data/lib/rigor/effects/propagator.rb +1 -1
- data/lib/rigor/effects/registry.rb +2 -2
- data/lib/rigor/effects/snapshot.rb +1 -1
- data/lib/rigor/effects/taint_cause.rb +0 -2
- data/lib/rigor/effects/unit_scan.rb +9 -13
- data/lib/rigor/environment/bundle_sig_discovery.rb +5 -9
- data/lib/rigor/environment/class_registry.rb +3 -3
- data/lib/rigor/environment/core_root.rb +77 -0
- data/lib/rigor/environment/default_libraries.rb +1 -1
- data/lib/rigor/environment/deferred_signature_plan.rb +283 -0
- data/lib/rigor/environment/gated_signature_guard.rb +1 -1
- data/lib/rigor/environment/gated_signature_plan.rb +5 -2
- data/lib/rigor/environment/lockfile_resolver.rb +28 -12
- data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
- data/lib/rigor/environment/member_consistency.rb +56 -7
- data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
- data/lib/rigor/environment/project_path_gems.rb +124 -0
- data/lib/rigor/environment/rbs_hierarchy.rb +6 -15
- data/lib/rigor/environment/rbs_loader.rb +480 -67
- data/lib/rigor/environment/reflection.rb +7 -7
- data/lib/rigor/environment/required_features.rb +0 -3
- data/lib/rigor/environment/ruby_library_roots.rb +1 -1
- data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
- data/lib/rigor/environment.rb +41 -10
- data/lib/rigor/flow_contribution/merger.rb +4 -4
- data/lib/rigor/inference/acceptance.rb +93 -32
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/block_run_count.rb +178 -0
- data/lib/rigor/inference/budget_trace.rb +8 -2
- data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
- data/lib/rigor/inference/callee_mutation_chain.rb +88 -77
- data/lib/rigor/inference/callee_write_census.rb +815 -0
- data/lib/rigor/inference/construction_escape.rb +217 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
- data/lib/rigor/inference/deep_erasure.rb +39 -7
- data/lib/rigor/inference/def_handle.rb +1 -1
- data/lib/rigor/inference/def_node_resolver.rb +1 -1
- data/lib/rigor/inference/def_return_typer.rb +46 -4
- data/lib/rigor/inference/definer_resolution.rb +4 -2
- data/lib/rigor/inference/definers_index.rb +106 -0
- data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
- data/lib/rigor/inference/element_read_widening.rb +49 -3
- data/lib/rigor/inference/expression_typer.rb +116 -49
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/global_write_census.rb +76 -2
- data/lib/rigor/inference/global_writer_reach.rb +260 -0
- data/lib/rigor/inference/hkt_body.rb +1 -2
- data/lib/rigor/inference/hkt_body_parser.rb +3 -5
- data/lib/rigor/inference/hkt_reducer.rb +2 -2
- data/lib/rigor/inference/hkt_registry.rb +3 -5
- data/lib/rigor/inference/indexed_narrowing.rb +4 -7
- data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
- data/lib/rigor/inference/ivar_writer_census.rb +51 -0
- data/lib/rigor/inference/key_presence_guard.rb +2 -1
- data/lib/rigor/inference/loop_value_record.rb +8 -4
- data/lib/rigor/inference/match_rebinding/frame.rb +46 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +45 -0
- data/lib/rigor/inference/match_rebinding.rb +11 -1
- data/lib/rigor/inference/memo_audit.rb +55 -0
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +3 -3
- data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +39 -27
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +203 -17
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +9 -7
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +83 -20
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +64 -14
- data/lib/rigor/inference/module_function_state.rb +11 -1
- data/lib/rigor/inference/multi_target_binder.rb +1 -1
- data/lib/rigor/inference/mutation_rejoin.rb +240 -4
- data/lib/rigor/inference/mutation_widening.rb +157 -27
- data/lib/rigor/inference/narrowing.rb +47 -33
- data/lib/rigor/inference/optimistic_origin.rb +1 -1
- data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +1 -1
- data/lib/rigor/inference/project_method_ownership.rb +6 -3
- data/lib/rigor/inference/project_yielder.rb +11 -17
- data/lib/rigor/inference/receiver_alias.rb +20 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +3 -1
- data/lib/rigor/inference/refined_dispatch/own_method.rb +2 -1
- data/lib/rigor/inference/remover_holder_scan.rb +233 -0
- data/lib/rigor/inference/remover_narrowing.rb +224 -0
- data/lib/rigor/inference/rewrite_mutation.rb +53 -2
- data/lib/rigor/inference/scope_indexer/certainty.rb +0 -1
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +58 -15
- data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
- data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
- data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +135 -0
- data/lib/rigor/inference/scope_indexer.rb +137 -46
- data/lib/rigor/inference/self_call_ivar_writes.rb +84 -12
- data/lib/rigor/inference/set_mutation.rb +189 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +1 -2
- data/lib/rigor/inference/statement_evaluator.rb +2328 -138
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_scanner.rb +5 -6
- data/lib/rigor/inference/toplevel_def_census.rb +59 -0
- data/lib/rigor/inference/unknown_store_widening.rb +103 -4
- data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -1
- data/lib/rigor/language_server/buffer_table.rb +3 -3
- data/lib/rigor/language_server/completion_provider.rb +26 -20
- data/lib/rigor/language_server/config_warning_announcer.rb +28 -0
- data/lib/rigor/language_server/diagnostic_publisher.rb +8 -3
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -2
- data/lib/rigor/language_server/hover_provider.rb +1 -7
- data/lib/rigor/language_server/hover_renderer.rb +6 -8
- data/lib/rigor/language_server/project_context.rb +39 -1
- data/lib/rigor/language_server/publish_batcher.rb +3 -4
- data/lib/rigor/language_server/selection_range_provider.rb +2 -4
- data/lib/rigor/language_server/server.rb +7 -3
- data/lib/rigor/language_server/signature_help_provider.rb +4 -4
- data/lib/rigor/lens/plugins.rb +1 -1
- data/lib/rigor/mcp/server.rb +73 -2
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- data/lib/rigor/plugin/bundled_catalog.rb +1 -12
- data/lib/rigor/plugin/io_boundary.rb +1 -1
- data/lib/rigor/plugin/loader.rb +25 -8
- data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
- data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
- data/lib/rigor/plugin/manifest.rb +9 -13
- data/lib/rigor/plugin/registry.rb +3 -3
- data/lib/rigor/plugin/services.rb +2 -3
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
- data/lib/rigor/plugin/trust_policy.rb +2 -2
- data/lib/rigor/plugin/type_node_resolver.rb +2 -4
- data/lib/rigor/plugin.rb +6 -8
- data/lib/rigor/plugin_gap_advisory.rb +5 -1
- data/lib/rigor/project_environment.rb +16 -2
- data/lib/rigor/protection/analysis_guard.rb +1 -1
- data/lib/rigor/protection/discovery_seed.rb +44 -5
- data/lib/rigor/protection/mutator.rb +1 -1
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
- data/lib/rigor/rbs_extended/reporter.rb +27 -3
- data/lib/rigor/rbs_extended.rb +2 -3
- data/lib/rigor/reflection/constant_ancestors.rb +5 -3
- data/lib/rigor/rooted_glob.rb +15 -1
- data/lib/rigor/run_scoped_slots.rb +38 -0
- data/lib/rigor/runtime/jit.rb +1 -1
- data/lib/rigor/scope/discovery_index.rb +19 -6
- data/lib/rigor/scope/resolution_chain.rb +3 -1
- data/lib/rigor/scope.rb +365 -31
- data/lib/rigor/sig_gen/classification.rb +4 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
- data/lib/rigor/sig_gen/generator.rb +371 -58
- data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
- data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
- data/lib/rigor/sig_gen/layout_index.rb +3 -3
- data/lib/rigor/sig_gen/method_candidate.rb +23 -4
- data/lib/rigor/sig_gen/observation_collector.rb +3 -5
- data/lib/rigor/sig_gen/observed_call.rb +1 -1
- data/lib/rigor/sig_gen/path_mapper.rb +6 -0
- data/lib/rigor/sig_gen/renderer.rb +15 -9
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +15 -2
- data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
- data/lib/rigor/sig_gen/write_reporter.rb +63 -14
- data/lib/rigor/sig_gen/write_result.rb +23 -12
- data/lib/rigor/sig_gen/writer.rb +217 -37
- data/lib/rigor/type/acceptance_router.rb +2 -2
- data/lib/rigor/type/accepts_result.rb +1 -1
- data/lib/rigor/type/app.rb +4 -6
- data/lib/rigor/type/combinator.rb +9 -13
- data/lib/rigor/type/constant.rb +4 -5
- data/lib/rigor/type/hash_shape.rb +21 -40
- data/lib/rigor/type/union.rb +1 -1
- data/lib/rigor/type_node/identifier.rb +1 -2
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node/string_literal.rb +1 -2
- data/lib/rigor/type_node/symbol_literal.rb +1 -2
- data/lib/rigor/type_node/union.rb +3 -6
- data/lib/rigor/value_semantics.rb +1 -10
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +38 -36
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +66 -19
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +2 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +0 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +5 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +7 -12
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +19 -6
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +26 -13
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +1 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +6 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +5 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +1 -1
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +4 -5
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -2
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +1 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -2
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +3 -4
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +6 -8
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +3 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +1 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +17 -21
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +3 -2
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
- data/sig/rigor/analysis/baseline.rbs +0 -3
- data/sig/rigor/analysis/fact_store.rbs +1 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
- data/sig/rigor/cli/diff_command.rbs +1 -1
- data/sig/rigor/cli/explain_command.rbs +1 -1
- data/sig/rigor/effects/registry.rbs +2 -2
- data/sig/rigor/inference.rbs +4 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +1 -0
- data/sig/rigor/plugin/effect_attribution.rbs +3 -3
- data/sig/rigor/plugin/effect_edge.rbs +1 -1
- data/sig/rigor/plugin/effect_entry_points.rbs +3 -3
- data/sig/rigor/plugin/io_boundary.rbs +2 -1
- data/sig/rigor/plugin/manifest.rbs +9 -9
- data/sig/rigor/plugin/protocol_contract.rbs +3 -7
- data/sig/rigor/scope.rbs +23 -3
- data/skills/rigor-plugin-author/SKILL.md +4 -4
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
- data/skills/rigor-plugin-review/SKILL.md +3 -3
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +12 -1
- metadata +36 -3
|
@@ -20,7 +20,7 @@ module Rigor
|
|
|
20
20
|
# fact published by `rigor-rails-routes` (ADR-9 cross-plugin API) and validates every implicit-self
|
|
21
21
|
# `*_path` / `*_url` call inside files under `controller_search_paths` (default `app/controllers`).
|
|
22
22
|
#
|
|
23
|
-
# Tier 2 of the [Rails plugins roadmap](
|
|
23
|
+
# Tier 2 of the [Rails plugins roadmap](../../../../../docs/design/20260508-rails-plugins-roadmap.md).
|
|
24
24
|
# Phase 1 (strong-parameters → AR column validation), Phase 2 (filter chains), and Phase 3 (render
|
|
25
25
|
# targets) ship as separate slices; each phase composes additively under the same plugin id.
|
|
26
26
|
#
|
|
@@ -37,14 +37,10 @@ module Rigor
|
|
|
37
37
|
#
|
|
38
38
|
# ## What it checks
|
|
39
39
|
#
|
|
40
|
-
# - **
|
|
41
|
-
# the
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
# recorded arity (a trailing `KeywordHashNode` like `users_path(format: :json)` is excluded; same
|
|
45
|
-
# convention `rigor-rails-routes` uses). Mismatches emit `wrong-helper-arity`.
|
|
46
|
-
# - **Trace** — recognised helpers also emit a `helper-call` info diagnostic naming the action and
|
|
47
|
-
# path, mirroring the trace shape of the upstream plugin.
|
|
40
|
+
# - **Trace** — recognised `*_path` / `*_url` helpers emit a `helper-call` info diagnostic naming
|
|
41
|
+
# the action and path. Unknown helpers (`unknown-helper`) and arity mismatches (`wrong-arity`)
|
|
42
|
+
# are reported by `rigor-rails-routes`, which owns the helper table, so this plugin does not
|
|
43
|
+
# repeat them.
|
|
48
44
|
#
|
|
49
45
|
# ## Limitations
|
|
50
46
|
#
|
|
@@ -88,7 +84,9 @@ module Rigor
|
|
|
88
84
|
# the engine reads as "a template exists here and has no unit". The bump is belt-and-braces: what
|
|
89
85
|
# actually invalidates is the claim itself, through the glob row in `TemplateUnits#digest` and the
|
|
90
86
|
# `claimed_globs` equality the carry checks. A manifest version is in no cache key of its own.
|
|
91
|
-
|
|
87
|
+
# Bumped 2026-10-27 (#2430) — `params[...]` answers the gradual `Dynamic[Parameters | nil]` rather than
|
|
88
|
+
# the bare union, so a cached 1.5.0 run carries the non-gradual answer at every `params[:k]` argument.
|
|
89
|
+
version: "1.6.0",
|
|
92
90
|
description: "Validates Action Pack route-helper calls and filter chains inside controllers, and types the request-context readers (`params` / `session` / `request` / `flash`) and their chains.",
|
|
93
91
|
config_schema: {
|
|
94
92
|
"controller_search_paths" => { kind: :array, default: ["app/controllers"] },
|
|
@@ -475,7 +473,7 @@ module Rigor
|
|
|
475
473
|
# `ActionController::Parameters` (above), but the chained `params.require(:user).permit(:name)` /
|
|
476
474
|
# `params.permit(...)` calls returned `Dynamic` at the first hop (Parameters ships no bundled RBS,
|
|
477
475
|
# so a call on it resolves lenient-to-Dynamic), leaking every downstream site (`.permit`, `.to_h`,
|
|
478
|
-
# `.each`) to unprotected. These
|
|
476
|
+
# `.each`) to unprotected. These methods return another `Parameters`, so gate on a
|
|
479
477
|
# `Parameters` receiver and re-type the result as the same lenient nominal — the chain stays a
|
|
480
478
|
# concrete receiver end-to-end and `coverage --protection` counts the sites as protected.
|
|
481
479
|
#
|
|
@@ -497,9 +495,9 @@ module Rigor
|
|
|
497
495
|
# entry FP-safe, because the RBS-less nominal buys leniency on the *method surface* only:
|
|
498
496
|
#
|
|
499
497
|
# - Method-surface rules DO decline on it. `call.undefined-method` bails at
|
|
500
|
-
# `Rigor::Reflection.rbs_class_known?` (lib/rigor/analysis/check_rules.rb
|
|
501
|
-
# ships no RBS for Parameters, and the argument / arity rules gate on the same predicate
|
|
502
|
-
#
|
|
498
|
+
# `Rigor::Reflection.rbs_class_known?` (lib/rigor/analysis/check_rules.rb) because Rigor
|
|
499
|
+
# ships no RBS for Parameters, and the argument / arity rules gate on the same predicate. So
|
|
500
|
+
# `params.slice(:a).whatever_rails_adds` never fires, and a scalar-shaped
|
|
503
501
|
# use of a Parameters-typed value (`.to_i`, `.strip`, string interpolation) resolves lenient
|
|
504
502
|
# -to-Dynamic rather than to a diagnostic.
|
|
505
503
|
# - **Flow rules do NOT.** Truthiness folding, ternary arm selection and `.nil?` folding key on
|
|
@@ -542,7 +540,7 @@ module Rigor
|
|
|
542
540
|
# and still carries the chain — `params[:user][:name]` types `Parameters?` and
|
|
543
541
|
# `params[:user].permit(:name)` types `Parameters`, because the dispatcher strips the nil arm
|
|
544
542
|
# for the receiver gate. Idiomatic *chained* reads stay silent too: `call.possible-nil-receiver`
|
|
545
|
-
# restricts itself to `Prism::LocalVariableReadNode` receivers (`CheckRules#nil_receiver_diagnostic`
|
|
543
|
+
# restricts itself to `Prism::LocalVariableReadNode` receivers (`CheckRules#nil_receiver_diagnostic`), so
|
|
546
544
|
# `params[:q].strip` cannot fire. What it does fire on is the assigned form — `q = params[:q];
|
|
547
545
|
# q.strip` — at **error** severity, once per unguarded use. Guarded forms (`return if q.nil?`,
|
|
548
546
|
# `if q`, `q&.strip`, `|| ""`) all narrow correctly and stay silent, so this is not a broken
|
|
@@ -564,7 +562,8 @@ module Rigor
|
|
|
564
562
|
# The control specs stay, and they still bite: they pin the HAZARD (no truthiness fold, no `bot`
|
|
565
563
|
# branch, no possible-nil firing), not the Dynamic answer, so they go red if either typing's
|
|
566
564
|
# hazard ever returns — and they now carry a positive assertion that `params[:k]` types
|
|
567
|
-
# `Parameters
|
|
565
|
+
# `Dynamic[ActionController::Parameters?]` (#2430), so a silent regression to bare `Dynamic[top]`
|
|
566
|
+
# or to the non-nil nominal fails them too.
|
|
568
567
|
STRONG_PARAMS_CHAIN_METHODS = %i[
|
|
569
568
|
require permit permit! expect slice
|
|
570
569
|
except without extract! slice!
|
|
@@ -578,6 +577,16 @@ module Rigor
|
|
|
578
577
|
Rigor::Type::Combinator.nominal_of(REQUEST_CONTEXT_READER_TYPES[:params])
|
|
579
578
|
end
|
|
580
579
|
|
|
580
|
+
# Issue #2430 — the chain on a `params[...]` carrier (`params[:user].permit(:name)`). The result stays
|
|
581
|
+
# a `Dynamic` of the same nominal, so it crosses a declared boundary as the carrier does.
|
|
582
|
+
dynamic_return methods: STRONG_PARAMS_CHAIN_METHODS do |call_node, scope|
|
|
583
|
+
next nil unless call_node.is_a?(Prism::CallNode) && call_node.receiver
|
|
584
|
+
next nil unless controller_scope?(scope)
|
|
585
|
+
next nil unless params_carrier_facet?(scope.type_of(call_node.receiver))
|
|
586
|
+
|
|
587
|
+
Rigor::Type::Combinator.dynamic(Rigor::Type::Combinator.nominal_of(REQUEST_CONTEXT_READER_TYPES[:params]))
|
|
588
|
+
end
|
|
589
|
+
|
|
581
590
|
# Issue #534 item 1, reopened by #574. `#[]` is the single largest named-receiver pair on both survey
|
|
582
591
|
# apps (redmine 581, mastodon ~475). The honest contract is `Parameters | nil` — a missing key really
|
|
583
592
|
# returns nil — and the block comment above records why that typing was REJECTED in 2026-09-01: it put
|
|
@@ -593,15 +602,53 @@ module Rigor
|
|
|
593
602
|
#
|
|
594
603
|
# The non-nil `-> Parameters` typing stays rejected on its own merits, unchanged: it makes every
|
|
595
604
|
# `params[:k]` condition always-truthy and folds a ternary over one to its true arm.
|
|
605
|
+
#
|
|
606
|
+
# Issue #2430 — the answer is a GRADUAL carrier, `Dynamic[Parameters | nil]`, not the bare union.
|
|
607
|
+
# A declared boundary (a `String` parameter, a `-> String` return) rejects the bare union, because
|
|
608
|
+
# `Acceptance` passes only a `Dynamic` through every boundary, and a scalar param is a `String` at
|
|
609
|
+
# runtime. The static facet keeps the strong-params chain typed: the carrier-gated rules below
|
|
610
|
+
# dispatch on the facet, so `params[:user][:name]` and `params[:user].permit(:name)` keep resolving.
|
|
611
|
+
# The control hazards (no truthiness fold, no `bot`, no possible-nil on `q = params[:q]`) are
|
|
612
|
+
# unchanged, because the facet is the same `Parameters | nil` union.
|
|
596
613
|
dynamic_return receivers: [REQUEST_CONTEXT_READER_TYPES[:params]], methods: %i[[]] do |call_node, _scope|
|
|
597
614
|
next nil unless call_node.is_a?(Prism::CallNode)
|
|
598
615
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
616
|
+
params_read_type
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
# Issue #2430 — `Dynamic` is not a `Nominal`, so the receiver-gated rules never see a carrier
|
|
620
|
+
# receiver. These two rules gate on the carrier's facet instead, and only inside a controller: a
|
|
621
|
+
# carrier is minted by the `params` reader, which is controller-only, so no other scope can hold one.
|
|
622
|
+
dynamic_return methods: %i[[]] do |call_node, scope|
|
|
623
|
+
next nil unless call_node.is_a?(Prism::CallNode) && call_node.receiver
|
|
624
|
+
next nil unless controller_scope?(scope)
|
|
625
|
+
next nil unless params_carrier_facet?(scope.type_of(call_node.receiver))
|
|
626
|
+
|
|
627
|
+
params_read_type
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
# The `params` carrier's static facet as a `Dynamic`: `Parameters | nil`, gradual at every boundary.
|
|
631
|
+
def params_read_type
|
|
632
|
+
Rigor::Type::Combinator.dynamic(
|
|
633
|
+
Rigor::Type::Combinator.union(
|
|
634
|
+
Rigor::Type::Combinator.nominal_of(REQUEST_CONTEXT_READER_TYPES[:params]),
|
|
635
|
+
Rigor::Type::Combinator.constant_of(nil)
|
|
636
|
+
)
|
|
602
637
|
)
|
|
603
638
|
end
|
|
604
639
|
|
|
640
|
+
# True when `type` is a `Dynamic` whose facet is `Parameters` or `Parameters | nil`: the carrier a
|
|
641
|
+
# `params[...]` read answers, and the value a chained `[]` / strong-params call receives.
|
|
642
|
+
def params_carrier_facet?(type)
|
|
643
|
+
return false unless type.is_a?(Rigor::Type::Dynamic)
|
|
644
|
+
|
|
645
|
+
facet = type.static_facet
|
|
646
|
+
members = facet.is_a?(Rigor::Type::Union) ? facet.members : [facet]
|
|
647
|
+
non_nil = members.reject { |member| member.is_a?(Rigor::Type::Constant) && member.value.nil? }
|
|
648
|
+
non_nil.size == 1 && non_nil.first.is_a?(Rigor::Type::Nominal) &&
|
|
649
|
+
non_nil.first.class_name == REQUEST_CONTEXT_READER_TYPES[:params]
|
|
650
|
+
end
|
|
651
|
+
|
|
605
652
|
# Phase 5c (2026-09-02, issue #534 "same lane") — the rest of the request-context surface the
|
|
606
653
|
# corpus sweep measured after the #578 / #585 batch: `request.post?` 28 and `request.xhr?` 27 on
|
|
607
654
|
# the two apps, `flash.now` inside the redmine 147 / mastodon 29 flash cluster. Each is typed from
|
|
@@ -35,7 +35,7 @@ module Rigor
|
|
|
35
35
|
|
|
36
36
|
# Where a Rails app declares its adapter. Per-environment files are read too, because an app that
|
|
37
37
|
# sets the adapter only in `config/environments/production.rb` is common; agreement across every
|
|
38
|
-
# file that names one is what licenses the narrowing (see {.
|
|
38
|
+
# file that names one is what licenses the narrowing (see {.detect_adapter}).
|
|
39
39
|
CONFIG_PATHS = ["config/application.rb", "config/environments"].freeze
|
|
40
40
|
|
|
41
41
|
SETTING = /queue_adapter\s*=\s*[:"']([a-z_]+)["']?/
|
|
@@ -13,7 +13,7 @@ module Rigor
|
|
|
13
13
|
# rigor-activejob — validates `Job.perform_later(...)` / `.perform_now(...)` / `.perform(...)`
|
|
14
14
|
# argument arity against the discovered `#perform` definitions.
|
|
15
15
|
#
|
|
16
|
-
# Tier 1D of the [Rails plugins roadmap](
|
|
16
|
+
# Tier 1D of the [Rails plugins roadmap](../../../../../docs/design/20260508-rails-plugins-roadmap.md).
|
|
17
17
|
# Statically discovers ActiveJob subclasses by walking the configured `job_search_paths` and
|
|
18
18
|
# parsing each file with Prism — no `active_job` runtime dependency.
|
|
19
19
|
#
|
|
@@ -98,9 +98,10 @@ module Rigor
|
|
|
98
98
|
# Cached separately from `:job_index` because the two invalidate on different files: editing a
|
|
99
99
|
# schedule changes which jobs are reached without touching `app/jobs` at all. The `watch:` roots the
|
|
100
100
|
# glob at the working directory so that CREATING a schedule file — not just editing one — is seen. Each
|
|
101
|
-
# configured path is escaped to a literal name, so `recurring[1].yml` is watched as that file
|
|
101
|
+
# configured path is escaped to a literal name, so `recurring[1].yml` is watched as that file, and an
|
|
102
|
+
# absolute path is watched under its own directory (`RootedGlob.split_file`, #2222).
|
|
102
103
|
producer :recurring_jobs,
|
|
103
|
-
watch: -> {
|
|
104
|
+
watch: -> { @recurring_paths.map { |path| Rigor::RootedGlob.split_file(path) } } do |_params|
|
|
104
105
|
RecurringScan.new(
|
|
105
106
|
io_boundary: io_boundary,
|
|
106
107
|
recurring_paths: @recurring_paths
|
|
@@ -20,8 +20,9 @@ module Rigor
|
|
|
20
20
|
#
|
|
21
21
|
# Declined — no root, never a guess: a polymorphic association (no single target), a non-literal
|
|
22
22
|
# `class_name:` / `**opts`, `anonymous_class:`, a `with_options` group with non-literal options, and a
|
|
23
|
-
# `through:` association without a literal `class_name:` / `source_type:`. Without them a `through:`
|
|
24
|
-
#
|
|
23
|
+
# `through:` association without a literal `class_name:` / `source_type:`. Without them a `through:`
|
|
24
|
+
# target is the SOURCE association's class on the through model, and that association (declared
|
|
25
|
+
# there, hence in this same index) roots it itself.
|
|
25
26
|
#
|
|
26
27
|
# A computed name equal to the owner's own demodulized name declines too (Rails 7.2+ tries `::Name`
|
|
27
28
|
# first; the target is then the owner itself or a top-level class).
|
|
@@ -28,8 +28,8 @@ module Rigor
|
|
|
28
28
|
#
|
|
29
29
|
# An `ActiveRecord::Base` row reaches `User.find` through the project's own
|
|
30
30
|
# `User < ApplicationRecord < ActiveRecord::Base` lines, so one row covers every model in the app.
|
|
31
|
-
# That is the whole economy of the framework layer:
|
|
32
|
-
# database touches a Rails app performs, and none of them needed a line of application code.
|
|
31
|
+
# That is the whole economy of the framework layer: a fixed set of rows here colours the several
|
|
32
|
+
# thousand database touches a Rails app performs, and none of them needed a line of application code.
|
|
33
33
|
module Effects
|
|
34
34
|
BASE = "ActiveRecord::Base"
|
|
35
35
|
RELATION = "ActiveRecord::Relation"
|
|
@@ -186,27 +186,20 @@ module Rigor
|
|
|
186
186
|
merge_redeclarations(rows)
|
|
187
187
|
end
|
|
188
188
|
|
|
189
|
-
# Collapses the rows that declare the SAME constant into one, so {ModelIndex.build}
|
|
190
|
-
#
|
|
189
|
+
# Collapses the rows that declare the SAME constant into one, so {ModelIndex.build} (keyed by
|
|
190
|
+
# `class_name`) receives at most one row per model.
|
|
191
191
|
#
|
|
192
|
-
# A model is routinely declared more than once
|
|
193
|
-
#
|
|
194
|
-
#
|
|
195
|
-
#
|
|
196
|
-
# rows are UNIONed here. Taking the last row instead dropped the real declaration's associations,
|
|
197
|
-
# scopes, enums, validations, callbacks and `alias_attribute`s whenever the reopen sorted later in
|
|
198
|
-
# the glob, and `where(<a declared alias>: …)` then surfaced a false `unknown-column` on correct
|
|
199
|
-
# code; taking the first dropped whatever the reopen added. Neither order loses anything now.
|
|
192
|
+
# A model is routinely declared more than once (`app/models/user.rb` plus a reopening file). Ruby's
|
|
193
|
+
# semantics are ADDITIVE, so the rows are UNIONed. Taking the last row dropped the real declaration's
|
|
194
|
+
# associations, scopes, enums, validations, callbacks and `alias_attribute`s whenever the reopen sorted
|
|
195
|
+
# later, producing false `unknown-column`s; taking the first dropped what the reopen added.
|
|
200
196
|
#
|
|
201
|
-
# Merge is by field: the first non-nil `superclass_name` wins
|
|
202
|
-
#
|
|
203
|
-
#
|
|
204
|
-
#
|
|
205
|
-
#
|
|
206
|
-
#
|
|
207
|
-
# inexact); name-keyed rows (associations, enums, aliases) let the LAST declaration override an
|
|
208
|
-
# earlier same-name row exactly as {ModelIndex.merge_named_rows} does across an STI chain, and the
|
|
209
|
-
# plain lists union.
|
|
197
|
+
# Merge is by field: the first non-nil `superclass_name` wins. `self.table_name =` is an ASSIGNMENT, so
|
|
198
|
+
# the later `table_name_override` wins, but when two declarations disagree the name is marked computed
|
|
199
|
+
# so no value is pinned: glob order is not load order, and a pinned wrong literal would fold
|
|
200
|
+
# `Model.table_name == "…"` on correct code. `table_name_computed` is otherwise an OR. Name-keyed rows
|
|
201
|
+
# let the LAST declaration override an earlier same-name row, as {ModelIndex.merge_named_rows} does
|
|
202
|
+
# across an STI chain; plain lists union.
|
|
210
203
|
def merge_redeclarations(rows)
|
|
211
204
|
return rows if rows.length < 2
|
|
212
205
|
|
|
@@ -459,7 +452,6 @@ module Rigor
|
|
|
459
452
|
macro_methods: lookup_macro_methods(node.body)
|
|
460
453
|
})
|
|
461
454
|
|
|
462
|
-
# Recurse into the body in case nested classes exist.
|
|
463
455
|
walk_for_classes(node.body, [full_name], &) if node.body
|
|
464
456
|
end
|
|
465
457
|
|
|
@@ -869,8 +861,8 @@ module Rigor
|
|
|
869
861
|
|
|
870
862
|
# Walks the namespace body's TOP-LEVEL statements only — a decorator declared inside a nested
|
|
871
863
|
# `module`/`class` belongs to THAT namespace, not this one, and must not be attributed here.
|
|
872
|
-
# Returns
|
|
873
|
-
#
|
|
864
|
+
# Returns the decorators keyed by `:prefix` / `:suffix`, only for the names this body actually mentions.
|
|
865
|
+
# A later statement overrides an earlier one within the SAME body — real Ruby
|
|
874
866
|
# assignment order, not a guess: `mattr_accessor :table_name_prefix` (no `default:`, so `computed:
|
|
875
867
|
# true`) followed two lines down by a real `self.table_name_prefix = "blog_"` folds to the literal.
|
|
876
868
|
#
|
|
@@ -1234,8 +1226,8 @@ module Rigor
|
|
|
1234
1226
|
end
|
|
1235
1227
|
|
|
1236
1228
|
# Recognised single-instance and collection association DSL methods. The kind drives the eventual
|
|
1237
|
-
# return-type contribution: singular associations narrow to
|
|
1238
|
-
#
|
|
1229
|
+
# return-type contribution: singular associations narrow to the target or nil, plural ones to a
|
|
1230
|
+
# relation of the target.
|
|
1239
1231
|
#
|
|
1240
1232
|
# `composed_of` value-object aggregations and `delegated_type` roles are folded in here too — both
|
|
1241
1233
|
# accept the association name as a `where` / `find_by` query key, so omitting them turns every
|
|
@@ -1336,7 +1328,7 @@ module Rigor
|
|
|
1336
1328
|
association_option(args, "polymorphic") == true
|
|
1337
1329
|
|
|
1338
1330
|
# A polymorphic association has no single static target class — `target` is nil and the flow
|
|
1339
|
-
# contribution declines to narrow rather than inventing a wrong
|
|
1331
|
+
# contribution declines to narrow rather than inventing a wrong class-name target.
|
|
1340
1332
|
if polymorphic
|
|
1341
1333
|
target = nil
|
|
1342
1334
|
else
|
|
@@ -1460,8 +1452,7 @@ module Rigor
|
|
|
1460
1452
|
end
|
|
1461
1453
|
|
|
1462
1454
|
# `enum status: { active: 0, archived: 1 }` (Rails ≤6) and `enum :status, [:active, :archived]`
|
|
1463
|
-
# (Rails 7+).
|
|
1464
|
-
# guess.
|
|
1455
|
+
# (Rails 7+). Non-literal forms decline rather than guess.
|
|
1465
1456
|
def lookup_enums(body)
|
|
1466
1457
|
return {} if body.nil?
|
|
1467
1458
|
|
|
@@ -1516,7 +1507,7 @@ module Rigor
|
|
|
1516
1507
|
end
|
|
1517
1508
|
|
|
1518
1509
|
# `scope :active, -> { ... }`. Records the scope name only (the body is intentionally NOT
|
|
1519
|
-
# introspected — the caller contributes
|
|
1510
|
+
# introspected — the caller contributes a relation of the model based on the name alone
|
|
1520
1511
|
# via `class_scope_return_type`).
|
|
1521
1512
|
def lookup_scopes(body)
|
|
1522
1513
|
return [] if body.nil?
|
|
@@ -1585,8 +1576,7 @@ module Rigor
|
|
|
1585
1576
|
|
|
1586
1577
|
# `alias_attribute :new_name, :old_name`. Records the mapping so the analyzer accepts the alias
|
|
1587
1578
|
# as a query key — without it every `where(<alias>: ...)` / `find_by(<alias>: ...)` call surfaces
|
|
1588
|
-
# as a false `unknown-column`.
|
|
1589
|
-
# rather than guess.
|
|
1579
|
+
# as a false `unknown-column`. Non-Symbol-literal forms decline rather than guess.
|
|
1590
1580
|
def lookup_aliases(body)
|
|
1591
1581
|
return {} if body.nil?
|
|
1592
1582
|
|
|
@@ -1626,7 +1616,7 @@ module Rigor
|
|
|
1626
1616
|
# macro and `images`, `images=`, `images_attachments`, `images_blobs` for the collection one — the
|
|
1627
1617
|
# `has_one_attached` / `has_many_attached` generated-methods surface, none of it column-backed.
|
|
1628
1618
|
# This is name MEMBERSHIP only, and does not overlap `rigor-activestorage`, which contributes the
|
|
1629
|
-
# accessor's
|
|
1619
|
+
# accessor's type and publishes no fact: a consumer that reads
|
|
1630
1620
|
# `:model_index` to ask "does this model answer `avatar`?" gets nothing from that plugin, and gets
|
|
1631
1621
|
# nothing at all when the project does not load it.
|
|
1632
1622
|
ATTACHMENT_MACROS = {
|
|
@@ -29,14 +29,12 @@ module Rigor
|
|
|
29
29
|
# {Entry} resolves its table from the root model and inherits the chain's declared associations /
|
|
30
30
|
# enums / aliases / scopes / validations / callbacks / macro methods.
|
|
31
31
|
class ModelIndex
|
|
32
|
-
# `associations` is a frozen
|
|
33
|
-
# nullable: }`:
|
|
32
|
+
# `associations` is a frozen list of rows `{ name:, kind:, target:, nullable: }`:
|
|
34
33
|
#
|
|
35
|
-
# - `name` —
|
|
34
|
+
# - `name` — the association method name as the user invokes it (`"posts"`).
|
|
36
35
|
# - `kind` — `:singular` (`belongs_to` / `has_one`) or `:collection` (`has_many`).
|
|
37
|
-
# - `target` —
|
|
38
|
-
#
|
|
39
|
-
# - `nullable` — Boolean; whether a `:singular` accessor can return `nil`. `has_one` → `true`;
|
|
36
|
+
# - `target` — the target class name, from an explicit `class_name:` option or via {Inflector.classify}.
|
|
37
|
+
# - `nullable` — whether a `:singular` accessor can return `nil`. `has_one` → `true`;
|
|
40
38
|
# `belongs_to` → `false` (required by default since Rails 5) unless `optional: true`
|
|
41
39
|
# / `required: false`. Meaningless for `:collection` rows.
|
|
42
40
|
#
|
|
@@ -71,27 +69,23 @@ module Rigor
|
|
|
71
69
|
|
|
72
70
|
def association_names = associations.map { |a| a[:name] }
|
|
73
71
|
|
|
74
|
-
# `enums`
|
|
72
|
+
# `enums` maps a column name to its value names.
|
|
75
73
|
def enum?(name) = enums.key?(name.to_s)
|
|
76
74
|
def enum_values(name) = enums.fetch(name.to_s, [])
|
|
77
75
|
|
|
78
|
-
# `scopes` is `Array<scope_name>`.
|
|
79
76
|
def scope?(name) = scopes.include?(name.to_s)
|
|
80
77
|
|
|
81
|
-
# `validations`
|
|
82
|
-
# `validates_*_of :name, ...` shorthand families.
|
|
78
|
+
# `validations` covers both `validates :name, ...` and the `validates_*_of :name, ...` shorthand families.
|
|
83
79
|
def validation?(name) = validations.include?(name.to_s)
|
|
84
80
|
def validated_attributes = validations
|
|
85
81
|
|
|
86
|
-
# `callbacks` is `Array<{ name:, callback: }>`.
|
|
87
82
|
def callback_targets = callbacks.map { |c| c[:name] }
|
|
88
83
|
|
|
89
|
-
# `aliases`
|
|
90
|
-
# declarations.
|
|
84
|
+
# `aliases` maps an alias name to its target attribute, from `alias_attribute` declarations.
|
|
91
85
|
def alias?(name) = aliases.key?(name.to_s)
|
|
92
86
|
def resolve_alias(name) = aliases[name.to_s]
|
|
93
87
|
|
|
94
|
-
# `macro_methods`
|
|
88
|
+
# `macro_methods` lists the instance methods a declaration macro installs that
|
|
95
89
|
# none of the sets above already carries (#1049): `delegate`'s names, the Paperclip / Active
|
|
96
90
|
# Storage attachment readers, and an `enum`'s per-value predicates. Names only, with no type: a
|
|
97
91
|
# consumer asks "does this model answer `followers_count`?", which is what a fail-closed check
|
|
@@ -312,7 +306,7 @@ module Rigor
|
|
|
312
306
|
seen.values.map(&:freeze).freeze
|
|
313
307
|
end
|
|
314
308
|
|
|
315
|
-
#
|
|
309
|
+
# Enum value lists merged across the STI chain; a child redeclaration of the same
|
|
316
310
|
# enum column overrides the ancestor's value list.
|
|
317
311
|
def self.merge_enums(chain)
|
|
318
312
|
chain.each_with_object({}) do |row, acc|
|
|
@@ -320,7 +314,7 @@ module Rigor
|
|
|
320
314
|
end.freeze
|
|
321
315
|
end
|
|
322
316
|
|
|
323
|
-
#
|
|
317
|
+
# Alias map merged across the STI chain.
|
|
324
318
|
def self.merge_aliases(chain)
|
|
325
319
|
chain.each_with_object({}) do |row, acc|
|
|
326
320
|
(row[:aliases] || {}).each { |name, target| acc[name.to_s] = target.to_s }
|
|
@@ -108,9 +108,8 @@ module Rigor
|
|
|
108
108
|
false
|
|
109
109
|
end
|
|
110
110
|
|
|
111
|
-
# Walks the block body collecting `t.<method>(...)` calls.
|
|
112
|
-
# `if
|
|
113
|
-
# back silently.
|
|
111
|
+
# Walks the block body collecting `t.<method>(...)` calls. It descends through nested constructs
|
|
112
|
+
# (e.g. `if` branches) but does not recurse into a matched call.
|
|
114
113
|
def collect_column_calls(node, &)
|
|
115
114
|
return if node.nil?
|
|
116
115
|
|
|
@@ -59,8 +59,8 @@ module Rigor
|
|
|
59
59
|
# capital R) is intentional — keeps the constant lookup distinct from `::ActiveRecord` even though the
|
|
60
60
|
# gem name is hyphenated.
|
|
61
61
|
#
|
|
62
|
-
# Note: this plugin
|
|
63
|
-
#
|
|
62
|
+
# Note: this plugin does NOT require `active_record` at runtime — it only reads project source, the
|
|
63
|
+
# same way the other examples do. Rigor stays decoupled from Rails.
|
|
64
64
|
class Activerecord < Rigor::Plugin::Base
|
|
65
65
|
manifest(
|
|
66
66
|
id: "activerecord",
|
|
@@ -553,8 +553,9 @@ module Rigor
|
|
|
553
553
|
end
|
|
554
554
|
|
|
555
555
|
# Class-side finders + the class-side relation entry points. `find` returns the model, or an Array of
|
|
556
|
-
# them for two or more ids, or with a block the model or `nil`; `find_by!` returns the model;
|
|
557
|
-
# `all` / `order` / `limit` / `none` open a relation. The
|
|
556
|
+
# them for two or more ids, or with a block the model or `nil`; `find_by!` returns the model;
|
|
557
|
+
# `find_by` adds the `nil` arm; `where` / `all` / `order` / `limit` / `none` open a relation. The
|
|
558
|
+
# relation then carries its element type
|
|
558
559
|
# through any further chained query method via the bundled `ActiveRecord::Relation` RBS.
|
|
559
560
|
def finder_return_type(call_node, entry, scope)
|
|
560
561
|
case call_node.name
|
|
@@ -105,10 +105,9 @@ module Rigor
|
|
|
105
105
|
walk(node.body, inner_path, &) if node.body
|
|
106
106
|
end
|
|
107
107
|
|
|
108
|
-
# The full constant name a `class` / `module` declaration defines
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
# appended to the path (`class User` inside `module Admin` → `Admin::User`).
|
|
108
|
+
# The full constant name a `class` / `module` declaration defines. A ROOTED local name (`class ::User`)
|
|
109
|
+
# names the top-level constant whatever the nesting, so the lexical path is dropped; otherwise the
|
|
110
|
+
# name is appended to it (`class User` inside `module Admin` → `Admin::User`).
|
|
112
111
|
def declared_constant_name(local_name, lexical_path)
|
|
113
112
|
return local_name.delete_prefix("::") if local_name.start_with?("::")
|
|
114
113
|
|
|
@@ -8,7 +8,7 @@ module Rigor
|
|
|
8
8
|
#
|
|
9
9
|
# Each row is `{ name:, kind: }`:
|
|
10
10
|
#
|
|
11
|
-
# - `name` —
|
|
11
|
+
# - `name` — the attachment method name as the user invokes it (`"avatar"`).
|
|
12
12
|
# - `kind` — `:singular` (`has_one_attached`) or `:collection` (`has_many_attached`).
|
|
13
13
|
class AttachmentIndex
|
|
14
14
|
attr_reader :entries
|
|
@@ -137,19 +137,14 @@ module Rigor
|
|
|
137
137
|
nil
|
|
138
138
|
end
|
|
139
139
|
|
|
140
|
-
# Issue #1056 — "the attachment index did not load" is a fact about the run's INPUTS, not
|
|
141
|
-
#
|
|
142
|
-
#
|
|
143
|
-
# carried no once-guard at all — the `.uniq` collapsed repeats within one instance's list, but the
|
|
144
|
-
# whole list was re-emitted on every analysed file.
|
|
140
|
+
# Issue #1056 — "the attachment index did not load" is a fact about the run's INPUTS, not the file
|
|
141
|
+
# being analysed, so it is registered via {Plugin::Base#disclose_once} rather than returned from the
|
|
142
|
+
# per-file hook (which had no once-guard and re-emitted the list on every file).
|
|
145
143
|
#
|
|
146
|
-
# The `key`s carry an ordinal prefix because the engine emits
|
|
147
|
-
#
|
|
148
|
-
#
|
|
149
|
-
#
|
|
150
|
-
# time (the unbounded-growth shape #569 measured at 4.2 M retained strings on Redmine). The key, not
|
|
151
|
-
# the message, is the identity: a second refusal naming a different path collapses into the first row
|
|
152
|
-
# rather than adding one — the notice is "the index did not load", said once.
|
|
144
|
+
# The `key`s carry an ordinal prefix because the engine emits disclosures in KEY order (#1051), and
|
|
145
|
+
# a refused read must precede a discovery failure. `@load_errors` is keyed by the same string, which
|
|
146
|
+
# caps it: `#attachment_index` re-attempts the load per call site (#569). The key, not the message,
|
|
147
|
+
# is the identity, so a second refusal collapses into the first row.
|
|
153
148
|
def disclose_load_errors
|
|
154
149
|
@load_errors.each do |key, message|
|
|
155
150
|
disclose_once(key, message: message, severity: :warning, rule: "load-error")
|
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
# Entry point matching the `rigor-<id>` gem-name convention. The Plugin::Loader's `require_gem!` step
|
|
4
4
|
# calls `require "rigor-activestorage"`, and this file loads the plugin's actual implementation under
|
|
5
5
|
# the `rigor/plugin/` namespace so the require / register pair fires.
|
|
6
|
-
|
|
6
|
+
require_relative "rigor/plugin/activestorage"
|
data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb
CHANGED
|
@@ -12,7 +12,7 @@ module Rigor
|
|
|
12
12
|
# the core_ext predicate surface — the single cheapest purity win in a Rails app, per WD10 — is issue
|
|
13
13
|
# #388 and lands in this plugin's shipped RBS, not here (`try` was audited and skipped: it dispatches
|
|
14
14
|
# to whatever method the caller names, so its purity is a fact about the call site, not about `try`).
|
|
15
|
-
#
|
|
15
|
+
# The `in_time_zone_rows` group is #388's own addition: `DateTime#in_time_zone` reads `Time.zone`
|
|
16
16
|
# through a default argument, a gap the RBS sweep found but can only fix here.
|
|
17
17
|
#
|
|
18
18
|
# ## The clock
|
data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rigor/type"
|
|
4
|
+
|
|
5
|
+
module Rigor
|
|
6
|
+
module Plugin
|
|
7
|
+
class ActivesupportCoreExt < Rigor::Plugin::Base
|
|
8
|
+
# #2433 — the result type of `Hash#symbolize_keys` / `#to_options`.
|
|
9
|
+
#
|
|
10
|
+
# ActiveSupport implements them as `transform_keys { |key| key.to_sym rescue key }`, so a String or
|
|
11
|
+
# Symbol key becomes a Symbol and a key without `to_sym` (an Integer, a Float) survives unchanged. One
|
|
12
|
+
# RBS return type cannot say that: `Hash[Symbol, V]` reads an Integer key as a Symbol, and
|
|
13
|
+
# `Hash[Symbol | K, V]` makes a String-keyed hash fail a declared `Hash[Symbol, untyped]` parameter.
|
|
14
|
+
# So the RBS rows answer `Hash[untyped, V]` and this module maps each key member where it can.
|
|
15
|
+
#
|
|
16
|
+
# A key that is not a String or Symbol (an Integer, a project class) reads `Dynamic[top]`: no Rigor fact proves
|
|
17
|
+
# a class has no `to_sym` (a mixin, a `pre_eval` file or a reopening can add one), so a kept key could claim
|
|
18
|
+
# a type the program does not have. It declines (`nil`, the RBS row answers) when a receiver member is
|
|
19
|
+
# neither a `Hash[K, V]` nor `nil`. A `nil` member is dropped: `nil.symbolize_keys` raises, and the engine
|
|
20
|
+
# adds `nil` back for `&.`.
|
|
21
|
+
module SymbolizeKeys
|
|
22
|
+
HASH = "Hash"
|
|
23
|
+
SYMBOLIZED_CLASSES = %w[String Symbol].freeze
|
|
24
|
+
|
|
25
|
+
module_function
|
|
26
|
+
|
|
27
|
+
# Answers `Hash[K', V]` (the union of such over the receiver's Hash members), or nil to decline.
|
|
28
|
+
def result(receiver_type, scope)
|
|
29
|
+
members = receiver_type.is_a?(Rigor::Type::Union) ? receiver_type.members : [receiver_type]
|
|
30
|
+
answers = members.reject { |member| nil_member?(member) }.map { |member| member_answer(member, scope) }
|
|
31
|
+
return nil if answers.empty? || answers.any?(:decline)
|
|
32
|
+
|
|
33
|
+
hashes = answers.reject { |answer| answer == :empty }
|
|
34
|
+
hashes.empty? ? nil : Rigor::Type::Combinator.union(*hashes)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# An empty closed shape is `{}`: it adds no key, so it drops out of the union rather than declining it.
|
|
38
|
+
def member_answer(member, scope)
|
|
39
|
+
return :empty if member.is_a?(Rigor::Type::HashShape) && member.closed? && member.pairs.empty?
|
|
40
|
+
|
|
41
|
+
key_type, value_type = hash_arguments(member)
|
|
42
|
+
return :decline if key_type.nil?
|
|
43
|
+
|
|
44
|
+
Rigor::Type::Combinator.nominal_of(HASH, type_args: [map_key(key_type, scope), value_type])
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def nil_member?(type)
|
|
48
|
+
(type.is_a?(Rigor::Type::Constant) && type.value.nil?) ||
|
|
49
|
+
(type.is_a?(Rigor::Type::Nominal) && type.class_name == "NilClass")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def hash_arguments(type)
|
|
53
|
+
case type
|
|
54
|
+
when Rigor::Type::Nominal
|
|
55
|
+
type.class_name == HASH && type.type_args.size == 2 ? type.type_args : nil
|
|
56
|
+
when Rigor::Type::Difference
|
|
57
|
+
hash_arguments(type.base)
|
|
58
|
+
when Rigor::Type::HashShape
|
|
59
|
+
shape_arguments(type)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# A closed, non-empty shape reads as `Hash[<its keys>, <union of its values>]`.
|
|
64
|
+
def shape_arguments(shape)
|
|
65
|
+
return nil if shape.open? || shape.pairs.empty?
|
|
66
|
+
|
|
67
|
+
keys = shape.pairs.keys.map { |key| Rigor::Type::Combinator.constant_of(key) }
|
|
68
|
+
[Rigor::Type::Combinator.union(*keys), Rigor::Type::Combinator.union(*shape.pairs.values)]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def map_key(type, scope)
|
|
72
|
+
members = type.is_a?(Rigor::Type::Union) ? type.members : [type]
|
|
73
|
+
Rigor::Type::Combinator.union(*members.map { |member| map_member(member, scope) })
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def map_member(member, scope)
|
|
77
|
+
case member
|
|
78
|
+
when Rigor::Type::Constant
|
|
79
|
+
member.value.is_a?(String) || member.value.is_a?(Symbol) ? symbol : Rigor::Type::Combinator.untyped
|
|
80
|
+
when Rigor::Type::Nominal
|
|
81
|
+
symbolized_class?(member.class_name, scope) ? symbol : Rigor::Type::Combinator.untyped
|
|
82
|
+
else Rigor::Type::Combinator.untyped
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def symbol
|
|
87
|
+
Rigor::Type::Combinator.nominal_of("Symbol")
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# String, Symbol and their subclasses go through `to_sym`.
|
|
91
|
+
def symbolized_class?(name, scope)
|
|
92
|
+
SYMBOLIZED_CLASSES.include?(name) ||
|
|
93
|
+
SYMBOLIZED_CLASSES.any? { |base| %i[equal subclass].include?(ordering(name, base, scope)) }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def ordering(name, base, scope)
|
|
97
|
+
Rigor::Reflection.class_ordering(name, base, scope: scope)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|