rigortype 0.2.7 → 0.2.8
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/docs/manual/04-diagnostics.md +7 -4
- data/docs/manual/06-baseline.md +5 -2
- data/docs/manual/15-type-protection-coverage.md +6 -4
- data/lib/rigor/analysis/baseline.rb +56 -96
- data/lib/rigor/analysis/buffer_binding.rb +13 -20
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +29 -48
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +35 -54
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +23 -37
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +16 -23
- data/lib/rigor/analysis/check_rules/rule_walk.rb +48 -71
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +19 -28
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +48 -67
- data/lib/rigor/analysis/check_rules.rb +174 -274
- data/lib/rigor/analysis/dependency_recorder.rb +26 -38
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +13 -25
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +17 -30
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +15 -26
- data/lib/rigor/analysis/dependency_source_inference/index.rb +45 -76
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +23 -40
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +50 -83
- data/lib/rigor/analysis/dependency_source_inference.rb +6 -10
- data/lib/rigor/analysis/diagnostic.rb +48 -75
- data/lib/rigor/analysis/erb_template_detector.rb +13 -18
- data/lib/rigor/analysis/fact_store.rb +12 -22
- data/lib/rigor/analysis/incremental.rb +47 -63
- data/lib/rigor/analysis/incremental_session.rb +74 -100
- data/lib/rigor/analysis/project_scan.rb +12 -23
- data/lib/rigor/analysis/result.rb +3 -5
- data/lib/rigor/analysis/rule_catalog.rb +67 -95
- data/lib/rigor/analysis/run_stats.rb +38 -61
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +101 -160
- data/lib/rigor/analysis/runner/pool_coordinator.rb +121 -188
- data/lib/rigor/analysis/runner/project_pre_passes.rb +62 -96
- data/lib/rigor/analysis/runner/run_snapshots.rb +12 -18
- data/lib/rigor/analysis/runner.rb +220 -333
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +30 -43
- data/lib/rigor/analysis/worker_session.rb +64 -102
- data/lib/rigor/ast/type_node.rb +6 -8
- data/lib/rigor/ast.rb +6 -10
- data/lib/rigor/bleeding_edge.rb +27 -35
- data/lib/rigor/builtins/hkt_builtins.rb +7 -14
- data/lib/rigor/builtins/imported_refinements.rb +106 -149
- data/lib/rigor/builtins/predefined_constant_refinements.rb +51 -65
- data/lib/rigor/builtins/regex_refinement.rb +32 -43
- data/lib/rigor/builtins/static_return_refinements.rb +57 -81
- data/lib/rigor/cache/descriptor.rb +58 -94
- data/lib/rigor/cache/incremental_snapshot.rb +32 -48
- data/lib/rigor/cache/rbs_cache_producer.rb +10 -15
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +10 -16
- data/lib/rigor/cache/rbs_class_type_param_names.rb +10 -16
- data/lib/rigor/cache/rbs_constant_table.rb +8 -13
- data/lib/rigor/cache/rbs_descriptor.rb +8 -13
- data/lib/rigor/cache/rbs_environment.rb +11 -19
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +11 -19
- data/lib/rigor/cache/rbs_known_class_names.rb +8 -12
- data/lib/rigor/cache/store.rb +96 -151
- data/lib/rigor/cli/annotate_command.rb +51 -88
- data/lib/rigor/cli/baseline_command.rb +10 -17
- data/lib/rigor/cli/check_command.rb +121 -199
- data/lib/rigor/cli/check_runner_factory.rb +8 -14
- data/lib/rigor/cli/ci_detector.rb +9 -14
- data/lib/rigor/cli/command.rb +8 -14
- data/lib/rigor/cli/coverage_command.rb +19 -26
- data/lib/rigor/cli/coverage_mutation.rb +14 -19
- data/lib/rigor/cli/coverage_report.rb +0 -2
- data/lib/rigor/cli/coverage_scan.rb +7 -11
- data/lib/rigor/cli/diagnostic_formats.rb +43 -62
- data/lib/rigor/cli/diff_command.rb +10 -20
- data/lib/rigor/cli/docs_command.rb +26 -40
- data/lib/rigor/cli/doctor_command.rb +8 -9
- data/lib/rigor/cli/explain_command.rb +4 -7
- data/lib/rigor/cli/fused_protection_renderer.rb +4 -5
- data/lib/rigor/cli/fused_protection_report.rb +6 -8
- data/lib/rigor/cli/lsp_command.rb +11 -19
- data/lib/rigor/cli/mcp_command.rb +4 -6
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -4
- data/lib/rigor/cli/mutation_protection_report.rb +6 -9
- data/lib/rigor/cli/options.rb +11 -19
- data/lib/rigor/cli/plugin_command.rb +18 -30
- data/lib/rigor/cli/plugins_command.rb +29 -51
- data/lib/rigor/cli/plugins_renderer.rb +12 -20
- data/lib/rigor/cli/prism_colorizer.rb +13 -19
- data/lib/rigor/cli/protection_renderer.rb +5 -7
- data/lib/rigor/cli/protection_report.rb +27 -17
- data/lib/rigor/cli/renderable.rb +4 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +5 -8
- data/lib/rigor/cli/sig_gen_command.rb +14 -26
- data/lib/rigor/cli/skill_command.rb +30 -47
- data/lib/rigor/cli/skill_describe.rb +40 -64
- data/lib/rigor/cli/trace_command.rb +9 -16
- data/lib/rigor/cli/trace_renderer.rb +35 -51
- data/lib/rigor/cli/triage_command.rb +6 -10
- data/lib/rigor/cli/triage_renderer.rb +1 -2
- data/lib/rigor/cli/type_of_command.rb +14 -23
- data/lib/rigor/cli/type_of_renderer.rb +3 -5
- data/lib/rigor/cli/type_scan_command.rb +6 -10
- data/lib/rigor/cli/type_scan_renderer.rb +3 -5
- data/lib/rigor/cli/type_scan_report.rb +2 -3
- data/lib/rigor/cli/upgrade_command.rb +2 -3
- data/lib/rigor/cli.rb +16 -28
- data/lib/rigor/config_audit.rb +35 -44
- data/lib/rigor/configuration/dependencies.rb +41 -70
- data/lib/rigor/configuration/severity_profile.rb +28 -42
- data/lib/rigor/configuration.rb +150 -241
- data/lib/rigor/environment/bundle_sig_discovery.rb +74 -118
- data/lib/rigor/environment/class_registry.rb +19 -26
- data/lib/rigor/environment/constant_type_cache_holder.rb +14 -21
- data/lib/rigor/environment/hkt_registry_holder.rb +6 -12
- data/lib/rigor/environment/lockfile_resolver.rb +31 -47
- data/lib/rigor/environment/rbs_collection_discovery.rb +39 -63
- data/lib/rigor/environment/rbs_coverage_report.rb +24 -42
- data/lib/rigor/environment/rbs_loader.rb +420 -518
- data/lib/rigor/environment/reflection.rb +28 -53
- data/lib/rigor/environment/reporters.rb +13 -24
- data/lib/rigor/environment.rb +176 -291
- data/lib/rigor/flow_contribution/conflict.rb +16 -27
- data/lib/rigor/flow_contribution/element.rb +7 -12
- data/lib/rigor/flow_contribution/fact.rb +33 -57
- data/lib/rigor/flow_contribution/merge_result.rb +6 -9
- data/lib/rigor/flow_contribution/merger.rb +32 -47
- data/lib/rigor/flow_contribution.rb +37 -55
- data/lib/rigor/inference/acceptance.rb +133 -219
- data/lib/rigor/inference/block_parameter_binder.rb +52 -82
- data/lib/rigor/inference/body_fixpoint.rb +22 -30
- data/lib/rigor/inference/budget_trace.rb +43 -63
- data/lib/rigor/inference/builtins/array_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/comparable_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/complex_catalog.rb +13 -19
- data/lib/rigor/inference/builtins/date_catalog.rb +38 -67
- data/lib/rigor/inference/builtins/encoding_catalog.rb +22 -36
- data/lib/rigor/inference/builtins/enumerable_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/exception_catalog.rb +38 -57
- data/lib/rigor/inference/builtins/hash_catalog.rb +9 -14
- data/lib/rigor/inference/builtins/method_catalog.rb +29 -48
- data/lib/rigor/inference/builtins/numeric_catalog.rb +13 -18
- data/lib/rigor/inference/builtins/pathname_catalog.rb +8 -13
- data/lib/rigor/inference/builtins/proc_catalog.rb +38 -62
- data/lib/rigor/inference/builtins/random_catalog.rb +21 -32
- data/lib/rigor/inference/builtins/range_catalog.rb +12 -21
- data/lib/rigor/inference/builtins/rational_catalog.rb +9 -15
- data/lib/rigor/inference/builtins/re_catalog.rb +29 -48
- data/lib/rigor/inference/builtins/set_catalog.rb +18 -27
- data/lib/rigor/inference/builtins/string_catalog.rb +12 -19
- data/lib/rigor/inference/builtins/struct_catalog.rb +18 -30
- data/lib/rigor/inference/builtins/time_catalog.rb +28 -44
- data/lib/rigor/inference/closure_escape_analyzer.rb +44 -67
- data/lib/rigor/inference/coverage_scanner.rb +13 -18
- data/lib/rigor/inference/def_return_typer.rb +8 -14
- data/lib/rigor/inference/dynamic_origin.rb +23 -20
- data/lib/rigor/inference/expression_typer.rb +774 -1152
- data/lib/rigor/inference/fallback.rb +8 -12
- data/lib/rigor/inference/fallback_tracer.rb +4 -10
- data/lib/rigor/inference/flow_tracer.rb +25 -36
- data/lib/rigor/inference/hkt_body.rb +45 -68
- data/lib/rigor/inference/hkt_body_parser.rb +33 -52
- data/lib/rigor/inference/hkt_reducer.rb +38 -59
- data/lib/rigor/inference/hkt_registry.rb +50 -76
- data/lib/rigor/inference/indexed_narrowing.rb +55 -81
- data/lib/rigor/inference/macro_block_self_type.rb +21 -34
- data/lib/rigor/inference/method_dispatcher/array_to_h_folding.rb +11 -18
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +49 -83
- data/lib/rigor/inference/method_dispatcher/call_context.rb +16 -23
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +13 -22
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +307 -454
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +24 -34
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +22 -37
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +52 -81
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +51 -78
- data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +60 -97
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +23 -34
- data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +18 -26
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +37 -71
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +114 -182
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +122 -193
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +15 -24
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +64 -93
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +31 -45
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +6 -9
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +356 -511
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +22 -32
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +15 -21
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +66 -82
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +10 -15
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +9 -13
- data/lib/rigor/inference/method_dispatcher.rb +378 -524
- data/lib/rigor/inference/method_parameter_binder.rb +81 -114
- data/lib/rigor/inference/multi_target_binder.rb +51 -68
- data/lib/rigor/inference/mutation_widening.rb +115 -158
- data/lib/rigor/inference/narrowing.rb +509 -727
- data/lib/rigor/inference/origin_lookup.rb +38 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +100 -100
- data/lib/rigor/inference/precision_scanner.rb +26 -35
- data/lib/rigor/inference/project_patched_methods.rb +20 -32
- data/lib/rigor/inference/project_patched_scanner.rb +23 -37
- data/lib/rigor/inference/protection_scanner.rb +16 -18
- data/lib/rigor/inference/rbs_type_translator.rb +49 -66
- data/lib/rigor/inference/scope_indexer.rb +473 -817
- data/lib/rigor/inference/statement_evaluator.rb +706 -1111
- data/lib/rigor/inference/struct_fold_safety.rb +34 -48
- data/lib/rigor/inference/synthetic_method.rb +9 -16
- data/lib/rigor/inference/synthetic_method_index.rb +20 -35
- data/lib/rigor/inference/synthetic_method_scanner.rb +63 -103
- data/lib/rigor/language_server/buffer_resolution.rb +6 -9
- data/lib/rigor/language_server/buffer_table.rb +11 -18
- data/lib/rigor/language_server/completion_provider.rb +69 -116
- data/lib/rigor/language_server/debouncer.rb +16 -25
- data/lib/rigor/language_server/diagnostic_publisher.rb +26 -40
- data/lib/rigor/language_server/document_symbol_provider.rb +14 -19
- data/lib/rigor/language_server/folding_range_provider.rb +11 -19
- data/lib/rigor/language_server/hover_provider.rb +8 -11
- data/lib/rigor/language_server/hover_renderer.rb +56 -93
- data/lib/rigor/language_server/loop.rb +14 -19
- data/lib/rigor/language_server/project_context.rb +46 -74
- data/lib/rigor/language_server/selection_range_provider.rb +9 -12
- data/lib/rigor/language_server/server.rb +55 -83
- data/lib/rigor/language_server/signature_help_provider.rb +29 -46
- data/lib/rigor/language_server/synchronized_writer.rb +4 -7
- data/lib/rigor/language_server/uri.rb +8 -13
- data/lib/rigor/language_server.rb +4 -6
- data/lib/rigor/mcp/loop.rb +2 -3
- data/lib/rigor/mcp/server.rb +4 -7
- data/lib/rigor/mcp.rb +3 -6
- data/lib/rigor/plugin/access_denied_error.rb +5 -8
- data/lib/rigor/plugin/additional_initializer.rb +21 -31
- data/lib/rigor/plugin/base.rb +335 -518
- data/lib/rigor/plugin/blueprint.rb +14 -23
- data/lib/rigor/plugin/box.rb +18 -29
- data/lib/rigor/plugin/fact_store.rb +16 -26
- data/lib/rigor/plugin/inflector.rb +37 -53
- data/lib/rigor/plugin/io_boundary.rb +33 -56
- data/lib/rigor/plugin/isolation.rb +42 -55
- data/lib/rigor/plugin/load_error.rb +10 -15
- data/lib/rigor/plugin/loader.rb +30 -49
- data/lib/rigor/plugin/macro/block_as_method.rb +20 -32
- data/lib/rigor/plugin/macro/heredoc_template.rb +35 -58
- data/lib/rigor/plugin/macro/nested_class_template.rb +22 -36
- data/lib/rigor/plugin/macro/trait_registry.rb +34 -51
- data/lib/rigor/plugin/macro.rb +10 -15
- data/lib/rigor/plugin/manifest.rb +85 -144
- data/lib/rigor/plugin/node_context.rb +14 -22
- data/lib/rigor/plugin/node_rule_walk.rb +49 -74
- data/lib/rigor/plugin/protocol_contract.rb +25 -39
- data/lib/rigor/plugin/registry.rb +132 -205
- data/lib/rigor/plugin/services.rb +21 -33
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -19
- data/lib/rigor/plugin/trust_policy.rb +24 -38
- data/lib/rigor/plugin/type_node_resolver.rb +15 -24
- data/lib/rigor/protection/diagnostic_oracle.rb +11 -13
- data/lib/rigor/protection/mutation_scanner.rb +27 -35
- data/lib/rigor/protection/mutator.rb +50 -70
- data/lib/rigor/protection/test_suite_oracle.rb +20 -27
- data/lib/rigor/rbs_extended/conformance_checker.rb +48 -68
- data/lib/rigor/rbs_extended/hkt_directives.rb +28 -54
- data/lib/rigor/rbs_extended/reporter.rb +24 -40
- data/lib/rigor/rbs_extended.rb +107 -197
- data/lib/rigor/reflection.rb +68 -86
- data/lib/rigor/scope/discovery_index.rb +14 -19
- data/lib/rigor/scope.rb +255 -310
- data/lib/rigor/sig_gen/classification.rb +6 -10
- data/lib/rigor/sig_gen/generator.rb +197 -323
- data/lib/rigor/sig_gen/layout_index.rb +12 -20
- data/lib/rigor/sig_gen/method_candidate.rb +12 -17
- data/lib/rigor/sig_gen/observation_collector.rb +38 -70
- data/lib/rigor/sig_gen/observed_call.rb +13 -23
- data/lib/rigor/sig_gen/path_mapper.rb +17 -29
- data/lib/rigor/sig_gen/renderer.rb +7 -13
- data/lib/rigor/sig_gen/type_elaborator.rb +15 -28
- data/lib/rigor/sig_gen/write_result.rb +8 -16
- data/lib/rigor/sig_gen/writer.rb +95 -174
- data/lib/rigor/sig_gen.rb +3 -6
- data/lib/rigor/signature_path_audit.rb +24 -30
- data/lib/rigor/source/constant_path.rb +10 -14
- data/lib/rigor/source/literals.rb +31 -45
- data/lib/rigor/source/node_locator.rb +9 -11
- data/lib/rigor/source/node_walker.rb +9 -13
- data/lib/rigor/source.rb +3 -4
- data/lib/rigor/testing.rb +16 -20
- data/lib/rigor/triage/catalogue.rb +38 -62
- data/lib/rigor/triage.rb +31 -52
- data/lib/rigor/trinary.rb +9 -13
- data/lib/rigor/type/acceptance_router.rb +4 -6
- data/lib/rigor/type/accepts_result.rb +10 -14
- data/lib/rigor/type/app.rb +19 -27
- data/lib/rigor/type/bot.rb +4 -6
- data/lib/rigor/type/bound_method.rb +10 -15
- data/lib/rigor/type/combinator.rb +165 -257
- data/lib/rigor/type/constant.rb +23 -34
- data/lib/rigor/type/data_class.rb +10 -15
- data/lib/rigor/type/data_instance.rb +14 -20
- data/lib/rigor/type/difference.rb +21 -32
- data/lib/rigor/type/dynamic.rb +3 -5
- data/lib/rigor/type/hash_shape.rb +32 -18
- data/lib/rigor/type/integer_range.rb +11 -16
- data/lib/rigor/type/intersection.rb +27 -42
- data/lib/rigor/type/nominal.rb +10 -15
- data/lib/rigor/type/plain_lattice.rb +9 -13
- data/lib/rigor/type/refined.rb +67 -114
- data/lib/rigor/type/singleton.rb +4 -6
- data/lib/rigor/type/struct_class.rb +11 -16
- data/lib/rigor/type/struct_instance.rb +15 -21
- data/lib/rigor/type/tuple.rb +14 -19
- data/lib/rigor/type/union.rb +30 -42
- data/lib/rigor/type_node/generic.rb +14 -26
- data/lib/rigor/type_node/identifier.rb +12 -19
- data/lib/rigor/type_node.rb +3 -12
- data/lib/rigor/value_semantics.rb +16 -21
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +18 -30
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +16 -29
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +12 -23
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +22 -39
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +26 -46
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +51 -87
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +10 -17
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +26 -39
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +118 -176
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +30 -51
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +57 -92
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +71 -108
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +13 -22
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +15 -25
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +8 -13
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +16 -24
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +30 -47
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +79 -129
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +36 -57
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +27 -41
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +16 -24
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +148 -235
- data/plugins/rigor-activerecord/lib/rigor-activerecord.rb +3 -5
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +8 -14
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +9 -15
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +4 -7
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +23 -41
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +3 -5
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +6 -10
- data/plugins/rigor-activesupport-core-ext/lib/rigor-activesupport-core-ext.rb +8 -11
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +30 -45
- data/plugins/rigor-devise/lib/rigor-devise.rb +3 -5
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +43 -69
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +26 -35
- data/plugins/rigor-dry-schema/lib/rigor-dry-schema.rb +3 -5
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +30 -47
- data/plugins/rigor-dry-struct/lib/rigor-dry-struct.rb +3 -5
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +67 -113
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +29 -46
- data/plugins/rigor-dry-types/lib/rigor-dry-types.rb +3 -5
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -20
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +14 -23
- data/plugins/rigor-dry-validation/lib/rigor-dry-validation.rb +2 -3
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +22 -37
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +28 -46
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +13 -17
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +30 -47
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +52 -80
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +21 -31
- data/plugins/rigor-graphql/lib/rigor-graphql.rb +3 -5
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +11 -17
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +20 -34
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +45 -75
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +22 -36
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +22 -41
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +17 -32
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +6 -12
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +4 -8
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +17 -32
- data/plugins/rigor-rails/lib/rigor-rails.rb +11 -18
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +58 -92
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +20 -34
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +20 -31
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +49 -82
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +55 -92
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +51 -90
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +17 -30
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +38 -70
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +42 -74
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +280 -497
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +54 -89
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +32 -54
- data/plugins/rigor-rbs-inline/lib/rigor-rbs-inline.rb +8 -13
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +9 -16
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +24 -39
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +13 -22
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +34 -56
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +22 -42
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +43 -75
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +27 -39
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +14 -19
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +20 -34
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +28 -47
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +21 -37
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +13 -22
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +18 -31
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +6 -10
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +14 -29
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +23 -41
- data/plugins/rigor-sinatra/lib/rigor-sinatra.rb +3 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +23 -38
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +57 -96
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +22 -35
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +41 -69
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +6 -10
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +24 -41
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +29 -50
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +55 -88
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +159 -262
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +21 -29
- data/plugins/rigor-statesman/lib/rigor-statesman.rb +3 -5
- data/plugins/rigor-typescript-utility-types/lib/rigor/plugin/typescript_utility_types.rb +23 -37
- data/plugins/rigor-typescript-utility-types/lib/rigor-typescript-utility-types.rb +4 -6
- data/sig/rigor/scope.rbs +6 -0
- metadata +2 -1
|
@@ -6,32 +6,23 @@ module Rigor
|
|
|
6
6
|
module Inference
|
|
7
7
|
# Shared dispatch table for `Rigor::Type#accepts(other, mode:)`.
|
|
8
8
|
#
|
|
9
|
-
# The acceptance query answers "is `other` passable to `self` at a
|
|
10
|
-
#
|
|
11
|
-
# rules from docs/type-specification/value-lattice.md and the
|
|
9
|
+
# The acceptance query answers "is `other` passable to `self` at a method-parameter or assignment
|
|
10
|
+
# boundary?". It uses gradual-typing rules from docs/type-specification/value-lattice.md and the
|
|
12
11
|
# acceptance contract in docs/internal-spec/internal-type-api.md.
|
|
13
12
|
#
|
|
14
|
-
# Each concrete type's `accepts` method delegates here so the
|
|
15
|
-
#
|
|
16
|
-
# objects; routing logic lives in the inference layer.
|
|
13
|
+
# Each concrete type's `accepts` method delegates here so the case-analysis stays in one place. Type
|
|
14
|
+
# instances remain thin value objects; routing logic lives in the inference layer.
|
|
17
15
|
#
|
|
18
|
-
# Slice 4 phase 2c implements the `:gradual` mode in full and
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
# combinator types added through phase 2b: Top, Bot, Dynamic,
|
|
22
|
-
# Nominal, Singleton, Constant, and Union.
|
|
16
|
+
# Slice 4 phase 2c implements the `:gradual` mode in full and reserves `:strict` for later slices (the
|
|
17
|
+
# entry point raises ArgumentError on strict for now). The table covers the leaf and combinator types
|
|
18
|
+
# added through phase 2b: Top, Bot, Dynamic, Nominal, Singleton, Constant, and Union.
|
|
23
19
|
#
|
|
24
|
-
# Slice 5 registers the shape carriers `Tuple` and `HashShape`.
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
# generic
|
|
30
|
-
# shape is projected to its underlying nominal so the existing
|
|
31
|
-
# generic-acceptance pipeline continues to apply; the converse
|
|
32
|
-
# direction (a Tuple receiver accepting a generic Array) stays
|
|
33
|
-
# conservative because the analyzer cannot verify arity from a
|
|
34
|
-
# raw nominal alone.
|
|
20
|
+
# Slice 5 registers the shape carriers `Tuple` and `HashShape`. Tuple/HashShape acceptance compares
|
|
21
|
+
# per-position element types (covariant) and per-key entry types (depth covariant), including HashShape
|
|
22
|
+
# required/optional/closed-extra-key policy. When the receiver side is a generic `Nominal[Array, [E]]` or
|
|
23
|
+
# `Nominal[Hash, [K, V]]` the shape is projected to its underlying nominal so the existing
|
|
24
|
+
# generic-acceptance pipeline continues to apply; the converse direction (a Tuple receiver accepting a
|
|
25
|
+
# generic Array) stays conservative because the analyzer cannot verify arity from a raw nominal alone.
|
|
35
26
|
# rubocop:disable Metrics/ModuleLength
|
|
36
27
|
module Acceptance
|
|
37
28
|
module_function
|
|
@@ -48,13 +39,10 @@ module Rigor
|
|
|
48
39
|
return Type::AcceptsResult.yes(mode: mode, reasons: "gradual: Dynamic[T] passes any boundary")
|
|
49
40
|
end
|
|
50
41
|
|
|
51
|
-
# Structural equality short-circuit. Two identical carriers
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
# carrier kind (the canonical example is
|
|
56
|
-
# `Intersection.accepts(Intersection)`, where the disjunction
|
|
57
|
-
# rule below would otherwise reject equal-but-narrow LHSes).
|
|
42
|
+
# Structural equality short-circuit. Two identical carriers describe the same value set, so they always
|
|
43
|
+
# accept each other. This is sound for any mode and covers cases where neither side has a per-class rule
|
|
44
|
+
# for the other's exact carrier kind (the canonical example is `Intersection.accepts(Intersection)`,
|
|
45
|
+
# where the disjunction rule below would otherwise reject equal-but-narrow LHSes).
|
|
58
46
|
return Type::AcceptsResult.yes(mode: mode, reasons: "structural equality") if self_type == other_type
|
|
59
47
|
|
|
60
48
|
return accepts_union_other(self_type, other_type, mode) if other_type.is_a?(Type::Union)
|
|
@@ -63,10 +51,9 @@ module Rigor
|
|
|
63
51
|
accepts_one(self_type, other_type, mode)
|
|
64
52
|
end
|
|
65
53
|
|
|
66
|
-
# Hash dispatch keeps `accepts_one` linear and lets future shape
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
# subclasses are not expected.
|
|
54
|
+
# Hash dispatch keeps `accepts_one` linear and lets future shape carriers register their handlers
|
|
55
|
+
# without re-tripping the cyclomatic budget on a growing `case` arm. Anonymous Type subclasses are not
|
|
56
|
+
# expected.
|
|
70
57
|
TYPE_HANDLERS = {
|
|
71
58
|
Type::Top => :accepts_top,
|
|
72
59
|
Type::Bot => :accepts_bot,
|
|
@@ -107,9 +94,8 @@ module Rigor
|
|
|
107
94
|
)
|
|
108
95
|
end
|
|
109
96
|
|
|
110
|
-
# Dynamic[T] in gradual mode is liberally inhabited; any concrete
|
|
111
|
-
#
|
|
112
|
-
# crossing. (Other being Dynamic was handled in {.accepts}.)
|
|
97
|
+
# Dynamic[T] in gradual mode is liberally inhabited; any concrete other type is accepted because
|
|
98
|
+
# gradual consistency permits the crossing. (Other being Dynamic was handled in {.accepts}.)
|
|
113
99
|
def accepts_dynamic(_self_type, _other_type, mode)
|
|
114
100
|
Type::AcceptsResult.yes(
|
|
115
101
|
mode: mode,
|
|
@@ -117,10 +103,8 @@ module Rigor
|
|
|
117
103
|
)
|
|
118
104
|
end
|
|
119
105
|
|
|
120
|
-
# Union[A,B].accepts(X) iff some member accepts X. Yes wins as
|
|
121
|
-
#
|
|
122
|
-
# least one member returned maybe (cannot rule out coverage),
|
|
123
|
-
# else "no".
|
|
106
|
+
# Union[A,B].accepts(X) iff some member accepts X. Yes wins as soon as we find one; otherwise we
|
|
107
|
+
# surface "maybe" only when at least one member returned maybe (cannot rule out coverage), else "no".
|
|
124
108
|
def accepts_union_self(union, other_type, mode)
|
|
125
109
|
results = union.members.map { |m| accepts(m, other_type, mode: mode) }
|
|
126
110
|
|
|
@@ -141,12 +125,9 @@ module Rigor
|
|
|
141
125
|
end
|
|
142
126
|
end
|
|
143
127
|
|
|
144
|
-
# self.accepts(Intersection[Y, Z]) iff self accepts at least
|
|
145
|
-
#
|
|
146
|
-
#
|
|
147
|
-
# any one member implies containment of the whole
|
|
148
|
-
# intersection. Symmetric counterpart to
|
|
149
|
-
# `accepts_union_other`.
|
|
128
|
+
# self.accepts(Intersection[Y, Z]) iff self accepts at least one Y_i. Disjunction across members
|
|
129
|
+
# because the intersection is the meet of its members' value sets, so containment in any one member
|
|
130
|
+
# implies containment of the whole intersection. Symmetric counterpart to `accepts_union_other`.
|
|
150
131
|
def accepts_intersection_other(self_type, intersection, mode)
|
|
151
132
|
results = intersection.members.map { |m| accepts(self_type, m, mode: mode) }
|
|
152
133
|
|
|
@@ -162,9 +143,8 @@ module Rigor
|
|
|
162
143
|
end
|
|
163
144
|
end
|
|
164
145
|
|
|
165
|
-
# self.accepts(Union[Y, Z]) iff self accepts every Y_i. Strict
|
|
166
|
-
#
|
|
167
|
-
# "maybe" without a "no" gives maybe, all "yes" gives yes.
|
|
146
|
+
# self.accepts(Union[Y, Z]) iff self accepts every Y_i. Strict AND across members: any "no" turns the
|
|
147
|
+
# whole result no, any "maybe" without a "no" gives maybe, all "yes" gives yes.
|
|
168
148
|
def accepts_union_other(self_type, union, mode)
|
|
169
149
|
results = union.members.map { |m| accepts(self_type, m, mode: mode) }
|
|
170
150
|
|
|
@@ -185,10 +165,9 @@ module Rigor
|
|
|
185
165
|
end
|
|
186
166
|
end
|
|
187
167
|
|
|
188
|
-
# Singleton[C] only accepts another Singleton[D] where D is a
|
|
189
|
-
#
|
|
190
|
-
#
|
|
191
|
-
# are the class objects themselves.
|
|
168
|
+
# Singleton[C] only accepts another Singleton[D] where D is a subclass of (or equal to) C. Any other
|
|
169
|
+
# carrier (instance, constant, ...) is no, because the singleton type's inhabitants are the class
|
|
170
|
+
# objects themselves.
|
|
192
171
|
def accepts_singleton(self_type, other_type, mode)
|
|
193
172
|
unless other_type.is_a?(Type::Singleton)
|
|
194
173
|
return Type::AcceptsResult.no(
|
|
@@ -206,17 +185,15 @@ module Rigor
|
|
|
206
185
|
end
|
|
207
186
|
|
|
208
187
|
# Nominal[C] accepts:
|
|
209
|
-
# - Nominal[D] when D <= C (Ruby class subtype) and the
|
|
210
|
-
#
|
|
211
|
-
# - Constant[v] when v.is_a?(klass(C)). The type_args of self
|
|
212
|
-
#
|
|
213
|
-
#
|
|
214
|
-
#
|
|
215
|
-
#
|
|
216
|
-
#
|
|
217
|
-
#
|
|
218
|
-
# - HashShape{*} when self is the Hash (or a supertype) family,
|
|
219
|
-
# projected to `Nominal[Hash, [union(keys), union(values)]]`.
|
|
188
|
+
# - Nominal[D] when D <= C (Ruby class subtype) and the `type_args` are compatible (see
|
|
189
|
+
# {#accepts_nominal_args});
|
|
190
|
+
# - Constant[v] when v.is_a?(klass(C)). The type_args of self are ignored here because a Constant
|
|
191
|
+
# carries a concrete value, not a generic instantiation, and the analyzer has no way to refute the
|
|
192
|
+
# args from a literal alone.
|
|
193
|
+
# - Tuple[*] when self is the Array (or a supertype) family. The Tuple is projected to
|
|
194
|
+
# `Nominal[Array, [union(elements)]]` so the existing generic-arg machinery handles it.
|
|
195
|
+
# - HashShape{*} when self is the Hash (or a supertype) family, projected to
|
|
196
|
+
# `Nominal[Hash, [union(keys), union(values)]]`.
|
|
220
197
|
# - Singleton: never (wrong value kind).
|
|
221
198
|
def accepts_nominal(self_type, other_type, mode)
|
|
222
199
|
case other_type
|
|
@@ -228,13 +205,10 @@ module Rigor
|
|
|
228
205
|
end
|
|
229
206
|
end
|
|
230
207
|
|
|
231
|
-
# Tail of `accepts_nominal` that handles structural shape
|
|
232
|
-
# carriers (`
|
|
233
|
-
#
|
|
234
|
-
#
|
|
235
|
-
# and re-runs acceptance — soundness follows because the
|
|
236
|
-
# carrier's value set is contained in the projected
|
|
237
|
-
# nominal's value set.
|
|
208
|
+
# Tail of `accepts_nominal` that handles structural shape carriers (`Tuple` / `HashShape`) and
|
|
209
|
+
# refinement carriers (`Difference` / `Refined`). Each branch projects the other-side carrier to the
|
|
210
|
+
# nominal layer it sits above and re-runs acceptance — soundness follows because the carrier's value
|
|
211
|
+
# set is contained in the projected nominal's value set.
|
|
238
212
|
def accepts_nominal_from_shape(self_type, other_type, mode)
|
|
239
213
|
case other_type
|
|
240
214
|
when Type::Tuple
|
|
@@ -244,26 +218,20 @@ module Rigor
|
|
|
244
218
|
accepts(self_type, project_hash_shape_to_nominal(other_type), mode: mode)
|
|
245
219
|
.with_reason("projected HashShape to Nominal[Hash]")
|
|
246
220
|
when Type::DataInstance
|
|
247
|
-
# ADR-48: a class-tagged member instance is exactly one value of
|
|
248
|
-
#
|
|
249
|
-
# anonymous local-bound form projects to `Data` itself).
|
|
221
|
+
# ADR-48: a class-tagged member instance is exactly one value of its tagging class, so it projects
|
|
222
|
+
# to that class's nominal (the anonymous local-bound form projects to `Data` itself).
|
|
250
223
|
accepts(self_type, project_data_instance_to_nominal(other_type), mode: mode)
|
|
251
224
|
.with_reason("projected DataInstance to Nominal[#{other_type.class_name || 'Data'}]")
|
|
252
225
|
when Type::StructInstance
|
|
253
|
-
# ADR-48 Struct follow-up: same projection as DataInstance — a
|
|
254
|
-
#
|
|
255
|
-
# class (the anonymous form projects to `Struct` itself).
|
|
226
|
+
# ADR-48 Struct follow-up: same projection as DataInstance — a class-tagged Struct value is exactly
|
|
227
|
+
# one value of its tagging class (the anonymous form projects to `Struct` itself).
|
|
256
228
|
accepts(self_type, project_struct_instance_to_nominal(other_type), mode: mode)
|
|
257
229
|
.with_reason("projected StructInstance to Nominal[#{other_type.class_name || 'Struct'}]")
|
|
258
230
|
when Type::Difference, Type::Refined
|
|
259
|
-
# A refinement carrier's value set is a subset of its
|
|
260
|
-
# base
|
|
261
|
-
#
|
|
262
|
-
# base
|
|
263
|
-
# through to the base nominal so the standard subtype
|
|
264
|
-
# check applies. The recursion is bounded because
|
|
265
|
-
# every refinement carrier's `base` is closer to the
|
|
266
|
-
# nominal layer.
|
|
231
|
+
# A refinement carrier's value set is a subset of its base. So if `self` (Nominal) accepts the
|
|
232
|
+
# base, it also accepts the refinement; if it rejects the base, it cannot accept any subset of
|
|
233
|
+
# it. Forward through to the base nominal so the standard subtype check applies. The recursion
|
|
234
|
+
# is bounded because every refinement carrier's `base` is closer to the nominal layer.
|
|
267
235
|
accepts(self_type, other_type.base, mode: mode)
|
|
268
236
|
.with_reason("projected #{other_type.class.name.split('::').last} to its base")
|
|
269
237
|
else
|
|
@@ -274,9 +242,9 @@ module Rigor
|
|
|
274
242
|
end
|
|
275
243
|
end
|
|
276
244
|
|
|
277
|
-
# `Nominal[Integer]` (and anything Integer is-a, like Numeric) accepts
|
|
278
|
-
#
|
|
279
|
-
#
|
|
245
|
+
# `Nominal[Integer]` (and anything Integer is-a, like Numeric) accepts any `IntegerRange`; nothing
|
|
246
|
+
# else does. Argument-bearing `Nominal`s never accept `IntegerRange` because IntegerRange has no
|
|
247
|
+
# type args.
|
|
280
248
|
INTEGER_NOMINAL_ANCESTORS = %w[Integer Numeric Comparable Object BasicObject].freeze
|
|
281
249
|
private_constant :INTEGER_NOMINAL_ANCESTORS
|
|
282
250
|
|
|
@@ -301,18 +269,12 @@ module Rigor
|
|
|
301
269
|
end
|
|
302
270
|
end
|
|
303
271
|
|
|
304
|
-
# v0.0.2 — meta-type rule. A `Singleton[T]` is the
|
|
305
|
-
#
|
|
306
|
-
# `Class
|
|
307
|
-
#
|
|
308
|
-
#
|
|
309
|
-
#
|
|
310
|
-
# introspection patterns. The rule conservatively
|
|
311
|
-
# answers `:yes` for `Module` (every singleton is at
|
|
312
|
-
# least a Module) and for `Class` / `Object` /
|
|
313
|
-
# `BasicObject` (the class object inherits from
|
|
314
|
-
# those). Other Nominals fall through to the default
|
|
315
|
-
# `:no`.
|
|
272
|
+
# v0.0.2 — meta-type rule. A `Singleton[T]` is the class object for `T`, so it is an instance of
|
|
273
|
+
# `Class` (when `T` is a class) and always an instance of `Module`. Without this rule a method whose
|
|
274
|
+
# parameter is typed `Class | Module` would reject every `is_a?(SomeClass)` call and similar
|
|
275
|
+
# introspection patterns. The rule conservatively answers `:yes` for `Module` (every singleton is at
|
|
276
|
+
# least a Module) and for `Class` / `Object` / `BasicObject` (the class object inherits from those).
|
|
277
|
+
# Other Nominals fall through to the default `:no`.
|
|
316
278
|
META_NOMINALS_FROM_SINGLETON = %w[Module Class Object BasicObject].freeze
|
|
317
279
|
private_constant :META_NOMINALS_FROM_SINGLETON
|
|
318
280
|
|
|
@@ -339,27 +301,21 @@ module Rigor
|
|
|
339
301
|
)
|
|
340
302
|
return class_result if class_result.no?
|
|
341
303
|
|
|
342
|
-
# Parametrized-ancestor projection. When `actual <:= target`
|
|
343
|
-
#
|
|
344
|
-
# the
|
|
345
|
-
#
|
|
346
|
-
#
|
|
347
|
-
# Hash
|
|
348
|
-
# the inherited parametrization at the Enumerable boundary
|
|
349
|
-
# is `Tuple[K, V]`. RBS encodes this as
|
|
350
|
-
# `include Enumerable[[K, V]]` in `Hash`'s definition.
|
|
304
|
+
# Parametrized-ancestor projection. When `actual <:= target` holds at the class level but the
|
|
305
|
+
# type-arg arities differ, the actual's parametrization has to be projected into the target's view
|
|
306
|
+
# before the element-wise covariance check. The canonical case is `Hash[K, V] <:= Enumerable[[K,
|
|
307
|
+
# V]]`: Hash carries two type_args, Enumerable carries one, and the inherited parametrization at
|
|
308
|
+
# the Enumerable boundary is `Tuple[K, V]`. RBS encodes this as `include Enumerable[[K, V]]` in
|
|
309
|
+
# `Hash`'s definition.
|
|
351
310
|
projected_other = project_to_target_arity(self_type, other_type) || other_type
|
|
352
311
|
args_result = accepts_nominal_args(self_type, projected_other, mode)
|
|
353
312
|
combine_results(class_result, args_result, mode)
|
|
354
313
|
end
|
|
355
314
|
|
|
356
|
-
# Returns `other_type` rewritten so its type_args have the
|
|
357
|
-
#
|
|
358
|
-
#
|
|
359
|
-
#
|
|
360
|
-
# implementation that consults `definition.ancestors[i].args`
|
|
361
|
-
# for arbitrary subclass / module-include relations is the
|
|
362
|
-
# principled follow-up.
|
|
315
|
+
# Returns `other_type` rewritten so its type_args have the same arity as `self_type.type_args`, or
|
|
316
|
+
# `nil` if no projection is known. Today only the Hash → Enumerable projection is hand-rolled; a
|
|
317
|
+
# general RBS-driven implementation that consults `definition.ancestors[i].args` for arbitrary
|
|
318
|
+
# subclass / module-include relations is the principled follow-up.
|
|
363
319
|
def project_to_target_arity(self_type, other_type)
|
|
364
320
|
return nil if self_type.type_args.size == other_type.type_args.size
|
|
365
321
|
return nil if self_type.type_args.empty? || other_type.type_args.empty?
|
|
@@ -410,13 +366,10 @@ module Rigor
|
|
|
410
366
|
)
|
|
411
367
|
end
|
|
412
368
|
|
|
413
|
-
# Slice 4 phase 2d generic acceptance. Type arguments are
|
|
414
|
-
#
|
|
415
|
-
#
|
|
416
|
-
#
|
|
417
|
-
# that historically meant "any instantiation", so we keep
|
|
418
|
-
# backward compatibility for call sites that have not yet
|
|
419
|
-
# learned to carry generics.
|
|
369
|
+
# Slice 4 phase 2d generic acceptance. Type arguments are treated covariantly element-wise (gradual
|
|
370
|
+
# default; declared variance lands in Slice 5+). When either side has no type_args we are lenient:
|
|
371
|
+
# the absent side is the "raw" form that historically meant "any instantiation", so we keep backward
|
|
372
|
+
# compatibility for call sites that have not yet learned to carry generics.
|
|
420
373
|
def accepts_nominal_args(self_type, other_type, mode)
|
|
421
374
|
shortcut = nominal_args_shortcut(self_type, other_type, mode)
|
|
422
375
|
return shortcut if shortcut
|
|
@@ -427,9 +380,8 @@ module Rigor
|
|
|
427
380
|
combine_arg_results(per_arg, mode)
|
|
428
381
|
end
|
|
429
382
|
|
|
430
|
-
# Returns an `AcceptsResult` for the universal short-circuits
|
|
431
|
-
#
|
|
432
|
-
# element-wise check still has to run.
|
|
383
|
+
# Returns an `AcceptsResult` for the universal short-circuits (raw self, raw other, arity mismatch)
|
|
384
|
+
# or `nil` when the full element-wise check still has to run.
|
|
433
385
|
def nominal_args_shortcut(self_type, other_type, mode)
|
|
434
386
|
return Type::AcceptsResult.yes(mode: mode, reasons: "self has no type_args") if self_type.type_args.empty?
|
|
435
387
|
if other_type.type_args.empty?
|
|
@@ -468,14 +420,11 @@ module Rigor
|
|
|
468
420
|
ruby_class = resolve_class(self_type.class_name)
|
|
469
421
|
return constant_is_a_result(ruby_class, constant, self_type, mode) if ruby_class
|
|
470
422
|
|
|
471
|
-
# The host process may not have required the constant's
|
|
472
|
-
#
|
|
473
|
-
#
|
|
474
|
-
#
|
|
475
|
-
#
|
|
476
|
-
# OverloadSelector to reject `Integer#+(BigDecimal) ->
|
|
477
|
-
# BigDecimal` overloads contributed by `bigdecimal`'s
|
|
478
|
-
# RBS reopening when the actual arg is a Constant<Integer>.
|
|
423
|
+
# The host process may not have required the constant's declared self_type (e.g. `BigDecimal`
|
|
424
|
+
# since Ruby 3.4 is no longer a default gem). Fall back to inspecting the value's own class
|
|
425
|
+
# ancestor chain — always loadable because the value already exists. Required for OverloadSelector
|
|
426
|
+
# to reject `Integer#+(BigDecimal) -> BigDecimal` overloads contributed by `bigdecimal`'s RBS
|
|
427
|
+
# reopening when the actual arg is a Constant<Integer>.
|
|
479
428
|
ancestor_names = constant.value.class.ancestors.map(&:name)
|
|
480
429
|
if ancestor_names.include?(self_type.class_name)
|
|
481
430
|
Type::AcceptsResult.yes(
|
|
@@ -518,9 +467,8 @@ module Rigor
|
|
|
518
467
|
# IntegerRange[a..b] accepts:
|
|
519
468
|
# - Constant[n] where n is an Integer covered by [a..b];
|
|
520
469
|
# - IntegerRange[c..d] where [c..d] ⊆ [a..b];
|
|
521
|
-
# - Nominal[Integer] only when self is the universal range
|
|
522
|
-
#
|
|
523
|
-
# could fall outside the bound.
|
|
470
|
+
# - Nominal[Integer] only when self is the universal range (`int<min, max>`), since otherwise an
|
|
471
|
+
# arbitrary Integer could fall outside the bound.
|
|
524
472
|
# Anything else is rejected.
|
|
525
473
|
def accepts_integer_range(self_type, other_type, mode)
|
|
526
474
|
case other_type
|
|
@@ -594,19 +542,13 @@ module Rigor
|
|
|
594
542
|
end
|
|
595
543
|
end
|
|
596
544
|
|
|
597
|
-
# `Difference[base, removed]` accepts another type X when
|
|
598
|
-
#
|
|
599
|
-
#
|
|
600
|
-
#
|
|
601
|
-
#
|
|
602
|
-
#
|
|
603
|
-
#
|
|
604
|
-
# only when X is itself a `Constant` carrier (compare
|
|
605
|
-
# values directly) or another `Difference` with the same
|
|
606
|
-
# removed value (already exhibits the disjointness). Any
|
|
607
|
-
# other shape — Nominal, Union, IntegerRange — could
|
|
608
|
-
# overlap the removed value, so the difference rejects
|
|
609
|
-
# it under gradual mode.
|
|
545
|
+
# `Difference[base, removed]` accepts another type X when the base accepts X *and* X's value set is
|
|
546
|
+
# provably disjoint from `removed`. The disjointness test is the subtle part — it is NOT the same as
|
|
547
|
+
# `removed.accepts(X)`, because `Nominal[String]` includes `""` even though `Constant[""]` does not
|
|
548
|
+
# "accept" `Nominal[String]`. The conservative rule here: we can prove disjointness only when X is
|
|
549
|
+
# itself a `Constant` carrier (compare values directly) or another `Difference` with the same
|
|
550
|
+
# removed value (already exhibits the disjointness). Any other shape — Nominal, Union, IntegerRange
|
|
551
|
+
# — could overlap the removed value, so the difference rejects it under gradual mode.
|
|
610
552
|
def accepts_difference(self_type, other_type, mode)
|
|
611
553
|
base_result = accepts(self_type.base, other_type, mode: mode)
|
|
612
554
|
return base_result if base_result.no?
|
|
@@ -626,43 +568,31 @@ module Rigor
|
|
|
626
568
|
when Type::Constant
|
|
627
569
|
!(removed.is_a?(Type::Constant) && removed.value == other_type.value)
|
|
628
570
|
when Type::Difference
|
|
629
|
-
# `Difference[A, R].accepts(Difference[B, R])`: the
|
|
630
|
-
#
|
|
631
|
-
#
|
|
632
|
-
#
|
|
633
|
-
# into `other_type.base` because that would always fail
|
|
634
|
-
# (a Nominal base contains the removed value).
|
|
571
|
+
# `Difference[A, R].accepts(Difference[B, R])`: the other carrier already excludes `R` at its
|
|
572
|
+
# difference layer, so the disjointness is exhibited regardless of how `B` (its base) relates to
|
|
573
|
+
# `R`. We do NOT recurse into `other_type.base` because that would always fail (a Nominal base
|
|
574
|
+
# contains the removed value).
|
|
635
575
|
other_type.removed == removed
|
|
636
576
|
when Type::Intersection
|
|
637
|
-
# Disjointness is monotonic over Intersection: if any
|
|
638
|
-
#
|
|
639
|
-
# is too.
|
|
577
|
+
# Disjointness is monotonic over Intersection: if any member is provably disjoint from
|
|
578
|
+
# `removed`, the meet is too.
|
|
640
579
|
other_type.members.any? { |m| provably_disjoint_from_removed?(m, removed) }
|
|
641
580
|
end
|
|
642
581
|
end
|
|
643
582
|
|
|
644
|
-
# `Refined[base, predicate]` accepts another type X when
|
|
645
|
-
#
|
|
646
|
-
#
|
|
647
|
-
#
|
|
648
|
-
#
|
|
649
|
-
# Nominal and return their normal answer (the inner
|
|
650
|
-
# `accepts_nominal` does not register `Refined` /
|
|
651
|
-
# `Difference` as direct other-shapes — projecting to
|
|
652
|
-
# the base is what makes the comparison meaningful).
|
|
583
|
+
# `Refined[base, predicate]` accepts another type X when the base accepts the *base* of X *and* X is
|
|
584
|
+
# provably contained in the predicate's value set. The base check is delegated to
|
|
585
|
+
# `accepts(self.base, X.base)` so handlers like `accepts_nominal` see Nominal-vs-Nominal and return
|
|
586
|
+
# their normal answer (the inner `accepts_nominal` does not register `Refined` / `Difference` as
|
|
587
|
+
# direct other-shapes — projecting to the base is what makes the comparison meaningful).
|
|
653
588
|
#
|
|
654
|
-
# Provability rules in gradual mode (the conservative
|
|
655
|
-
# analogue of `accepts_difference`):
|
|
589
|
+
# Provability rules in gradual mode (the conservative analogue of `accepts_difference`):
|
|
656
590
|
#
|
|
657
|
-
# - X is a `Refined` with the *same* predicate_id —
|
|
658
|
-
#
|
|
659
|
-
# - X is a `Constant` whose value the predicate's
|
|
660
|
-
# recogniser accepts — the value is statically
|
|
591
|
+
# - X is a `Refined` with the *same* predicate_id — exact predicate match, accept.
|
|
592
|
+
# - X is a `Constant` whose value the predicate's recogniser accepts — the value is statically
|
|
661
593
|
# contained, accept. A recognised non-match is `:no`.
|
|
662
|
-
# - Anything else (Nominal, Union, IntegerRange,
|
|
663
|
-
#
|
|
664
|
-
# without a runtime test, so reject under gradual
|
|
665
|
-
# mode rather than degrade to `:maybe`. Mirrors the
|
|
594
|
+
# - Anything else (Nominal, Union, IntegerRange, Difference) — predicate-subset cannot be proven
|
|
595
|
+
# without a runtime test, so reject under gradual mode rather than degrade to `:maybe`. Mirrors the
|
|
666
596
|
# `accepts_difference` policy.
|
|
667
597
|
def accepts_refined(self_type, other_type, mode)
|
|
668
598
|
case other_type
|
|
@@ -717,13 +647,10 @@ module Rigor
|
|
|
717
647
|
)
|
|
718
648
|
end
|
|
719
649
|
|
|
720
|
-
# `Intersection[M1, M2, …]` accepts X iff *every* member
|
|
721
|
-
#
|
|
722
|
-
#
|
|
723
|
-
#
|
|
724
|
-
# `:no` makes the result `:maybe`, all `:yes` makes the
|
|
725
|
-
# result `:yes`. The 0-member case is unreachable because
|
|
726
|
-
# `Combinator.intersection` collapses empty intersections
|
|
650
|
+
# `Intersection[M1, M2, …]` accepts X iff *every* member accepts X — the meet of value sets is
|
|
651
|
+
# contained iff the candidate is contained in each. Conjunctive combine: any `:no` makes the result
|
|
652
|
+
# `:no`, any `:maybe` without a `:no` makes the result `:maybe`, all `:yes` makes the result `:yes`.
|
|
653
|
+
# The 0-member case is unreachable because `Combinator.intersection` collapses empty intersections
|
|
727
654
|
# to `Top`.
|
|
728
655
|
def accepts_intersection(self_type, other_type, mode)
|
|
729
656
|
per_member = self_type.members.map { |m| accepts(m, other_type, mode: mode) }
|
|
@@ -748,9 +675,8 @@ module Rigor
|
|
|
748
675
|
end
|
|
749
676
|
end
|
|
750
677
|
|
|
751
|
-
# Constant[v] accepts only Constant[v'] with structurally equal
|
|
752
|
-
#
|
|
753
|
-
# Bot/Dynamic short-circuits already applied upstream).
|
|
678
|
+
# Constant[v] accepts only Constant[v'] with structurally equal value. Any other type is rejected
|
|
679
|
+
# (modulo the universal Bot/Dynamic short-circuits already applied upstream).
|
|
754
680
|
def accepts_constant(self_type, other_type, mode)
|
|
755
681
|
if other_type.is_a?(Type::Constant) && self_type == other_type
|
|
756
682
|
Type::AcceptsResult.yes(mode: mode, reasons: "structural literal match")
|
|
@@ -763,10 +689,8 @@ module Rigor
|
|
|
763
689
|
end
|
|
764
690
|
|
|
765
691
|
# Tuple[A1..An] accepts:
|
|
766
|
-
# - Tuple[B1..Bn] when arities match and each Ai accepts Bi
|
|
767
|
-
#
|
|
768
|
-
# - Anything else: no (we cannot prove the arity from a generic
|
|
769
|
-
# nominal alone).
|
|
692
|
+
# - Tuple[B1..Bn] when arities match and each Ai accepts Bi (covariant per-position).
|
|
693
|
+
# - Anything else: no (we cannot prove the arity from a generic nominal alone).
|
|
770
694
|
def accepts_tuple(self_type, other_type, mode)
|
|
771
695
|
unless other_type.is_a?(Type::Tuple)
|
|
772
696
|
return Type::AcceptsResult.no(
|
|
@@ -788,14 +712,11 @@ module Rigor
|
|
|
788
712
|
combine_arg_results(per_element, mode)
|
|
789
713
|
end
|
|
790
714
|
|
|
791
|
-
# HashShape{k1: T1, ...} accepts another HashShape when every
|
|
792
|
-
#
|
|
793
|
-
#
|
|
794
|
-
#
|
|
795
|
-
#
|
|
796
|
-
# types are rejected; the converse direction (a Nominal
|
|
797
|
-
# accepting a HashShape) is handled by `accepts_nominal` via
|
|
798
|
-
# projection.
|
|
715
|
+
# HashShape{k1: T1, ...} accepts another HashShape when every required key of self is required on
|
|
716
|
+
# the other side and Ti accepts Ui (depth covariant). Optional keys may be absent on the other side;
|
|
717
|
+
# when present, their values are checked. A closed self rejects known or possible extra keys. Other
|
|
718
|
+
# types are rejected; the converse direction (a Nominal accepting a HashShape) is handled by
|
|
719
|
+
# `accepts_nominal` via projection.
|
|
799
720
|
def accepts_hash_shape(self_type, other_type, mode)
|
|
800
721
|
unless other_type.is_a?(Type::HashShape)
|
|
801
722
|
return Type::AcceptsResult.no(
|
|
@@ -832,30 +753,23 @@ module Rigor
|
|
|
832
753
|
Type::AcceptsResult.no(mode: mode, reasons: reason)
|
|
833
754
|
end
|
|
834
755
|
|
|
835
|
-
# Uses Ruby's actual class hierarchy via Object.const_get to answer
|
|
836
|
-
#
|
|
837
|
-
#
|
|
838
|
-
#
|
|
839
|
-
#
|
|
840
|
-
# path does not yet consult it; migration to an RBS-driven lookup
|
|
841
|
-
# is deferred.
|
|
756
|
+
# Uses Ruby's actual class hierarchy via Object.const_get to answer "is D a subclass of C?" for core,
|
|
757
|
+
# stdlib, and application classes. When either name fails to resolve we surface "maybe": the caller
|
|
758
|
+
# (overload selector) treats yes/maybe identically, so the conservative answer keeps overload
|
|
759
|
+
# coverage intact. RbsHierarchy exists but this path does not yet consult it; migration to an
|
|
760
|
+
# RBS-driven lookup is deferred.
|
|
842
761
|
def class_subtype_result(target_name:, actual_name:, mode:, kind:)
|
|
843
762
|
return Type::AcceptsResult.yes(mode: mode, reasons: "exact name match") if target_name == actual_name
|
|
844
763
|
|
|
845
764
|
target_class = resolve_class(target_name)
|
|
846
765
|
actual_class = resolve_class(actual_name)
|
|
847
|
-
# When only `actual` resolves, we can still rule out
|
|
848
|
-
#
|
|
849
|
-
#
|
|
850
|
-
#
|
|
851
|
-
#
|
|
852
|
-
#
|
|
853
|
-
#
|
|
854
|
-
# `:maybe`. The reverse asymmetry (target resolves,
|
|
855
|
-
# actual doesn't) does not let us conclude anything —
|
|
856
|
-
# the unloaded `actual` could be an unrelated class or
|
|
857
|
-
# a subclass of `target` we can't see, so we still
|
|
858
|
-
# answer `:maybe` there.
|
|
766
|
+
# When only `actual` resolves, we can still rule out `actual <:= target` by inspecting `actual`'s
|
|
767
|
+
# ancestor chain. The canonical case: `target=BigDecimal` is not loadable in the host process (no
|
|
768
|
+
# `require` in rigor's own runtime), but `actual=Integer` IS, and Integer's ancestors do not
|
|
769
|
+
# include `BigDecimal`, so the subtype relation MUST be `:no` rather than the conservative
|
|
770
|
+
# `:maybe`. The reverse asymmetry (target resolves, actual doesn't) does not let us conclude
|
|
771
|
+
# anything — the unloaded `actual` could be an unrelated class or a subclass of `target` we can't
|
|
772
|
+
# see, so we still answer `:maybe` there.
|
|
859
773
|
return subtype_result_via_ancestors(actual_class, target_name, mode) if target_class.nil? && actual_class
|
|
860
774
|
if target_class.nil? || actual_class.nil?
|
|
861
775
|
return Type::AcceptsResult.maybe(
|