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
|
@@ -21,14 +21,11 @@ require_relative "../inference/flow_tracer"
|
|
|
21
21
|
|
|
22
22
|
module Rigor
|
|
23
23
|
module Type
|
|
24
|
-
# Factory entry point that routes every public construction through the
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
# internal escape hatch for tests and for combinator's own
|
|
28
|
-
# implementation.
|
|
24
|
+
# Factory entry point that routes every public construction through the deterministic normalization
|
|
25
|
+
# rules. Production code paths MUST go through Rigor::Type::Combinator. Direct constructor calls are
|
|
26
|
+
# an internal escape hatch for tests and for combinator's own implementation.
|
|
29
27
|
#
|
|
30
|
-
# See docs/internal-spec/internal-type-api.md and
|
|
31
|
-
# docs/type-specification/normalization.md.
|
|
28
|
+
# See docs/internal-spec/internal-type-api.md and docs/type-specification/normalization.md.
|
|
32
29
|
module Combinator # rubocop:disable Metrics/ModuleLength
|
|
33
30
|
module_function
|
|
34
31
|
|
|
@@ -40,19 +37,16 @@ module Rigor
|
|
|
40
37
|
Bot.instance
|
|
41
38
|
end
|
|
42
39
|
|
|
43
|
-
# ADR-15 Phase 4b.x — read the eagerly-allocated
|
|
44
|
-
# `@untyped`
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
# READ the populated ivar without performing the lazy
|
|
48
|
-
# write that non-main Ractors are forbidden from doing.
|
|
40
|
+
# ADR-15 Phase 4b.x — read the eagerly-allocated `@untyped` ivar instead of `||=`. The
|
|
41
|
+
# singleton-class `@untyped = Dynamic.new(top)` initializer runs at module body (below) on the main
|
|
42
|
+
# Ractor at load time. Workers READ the populated ivar without performing the lazy write that
|
|
43
|
+
# non-main Ractors are forbidden from doing.
|
|
49
44
|
def untyped
|
|
50
45
|
@untyped
|
|
51
46
|
end
|
|
52
47
|
|
|
53
|
-
# Wraps the static facet in a Dynamic[T] carrier. Idempotent on the
|
|
54
|
-
#
|
|
55
|
-
# value-lattice algebra.
|
|
48
|
+
# Wraps the static facet in a Dynamic[T] carrier. Idempotent on the static facet so
|
|
49
|
+
# Dynamic[Dynamic[T]] collapses to Dynamic[T] per the value-lattice algebra.
|
|
56
50
|
def dynamic(static_facet)
|
|
57
51
|
return untyped if static_facet.equal?(top)
|
|
58
52
|
|
|
@@ -62,12 +56,10 @@ module Rigor
|
|
|
62
56
|
Dynamic.new(facet)
|
|
63
57
|
end
|
|
64
58
|
|
|
65
|
-
# Constructs a Nominal type. Slice 4 phase 2d accepts an optional
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
# `Nominal["Array"
|
|
69
|
-
# produces the raw form `Nominal["Array"]`, which is structurally
|
|
70
|
-
# distinct from any applied form.
|
|
59
|
+
# Constructs a Nominal type. Slice 4 phase 2d accepts an optional `type_args:` array, an ordered
|
|
60
|
+
# list of Rigor::Type values that carry the receiver's generic instantiation (`Array[Integer]` is
|
|
61
|
+
# `Nominal["Array", [Nominal["Integer"]]]`). Omitting the keyword produces the raw form
|
|
62
|
+
# `Nominal["Array"]`, which is structurally distinct from any applied form.
|
|
71
63
|
def nominal_of(class_name_or_object, type_args: [])
|
|
72
64
|
Nominal.new(resolve_class_name(class_name_or_object), type_args)
|
|
73
65
|
end
|
|
@@ -80,27 +72,23 @@ module Rigor
|
|
|
80
72
|
Constant.new(value)
|
|
81
73
|
end
|
|
82
74
|
|
|
83
|
-
# Widens every value-pinned (`Constant`) constituent of `type` to its
|
|
84
|
-
#
|
|
85
|
-
#
|
|
86
|
-
#
|
|
87
|
-
# by the ADR-55 recursive-return fixpoint and the ADR-56 block /
|
|
88
|
-
# loop-body fixpoint (`BodyFixpoint`) to force convergence on the
|
|
75
|
+
# Widens every value-pinned (`Constant`) constituent of `type` to its nominal base (`Constant[1]` ->
|
|
76
|
+
# `Nominal["Integer"]`), recursing through unions and leaving non-pinned constituents untouched.
|
|
77
|
+
# `Constant[nil]` is preserved (no nominal base of interest). Shared by the ADR-55 recursive-return
|
|
78
|
+
# fixpoint and the ADR-56 block / loop-body fixpoint (`BodyFixpoint`) to force convergence on the
|
|
89
79
|
# final permitted iteration.
|
|
90
80
|
def widen_value_pinned(type)
|
|
91
81
|
case type
|
|
92
82
|
when Constant
|
|
93
83
|
type.value.nil? ? type : nominal_of(type.value.class.name)
|
|
94
84
|
when Refined
|
|
95
|
-
# A refinement (`non-empty-string`) is a value-narrowed nominal;
|
|
96
|
-
#
|
|
97
|
-
#
|
|
98
|
-
# than flooring to `Dynamic[top]` (ADR-56).
|
|
85
|
+
# A refinement (`non-empty-string`) is a value-narrowed nominal; widen it to its base so an
|
|
86
|
+
# accumulator whose per-pass result is a bounded refinement converges on the final iteration
|
|
87
|
+
# rather than flooring to `Dynamic[top]` (ADR-56).
|
|
99
88
|
widen_value_pinned(type.base)
|
|
100
89
|
when IntegerRange
|
|
101
|
-
# `int<1, 6>` is likewise a value-narrowed `Integer` (it erases to
|
|
102
|
-
#
|
|
103
|
-
# converges (ADR-56).
|
|
90
|
+
# `int<1, 6>` is likewise a value-narrowed `Integer` (it erases to `Integer` in RBS); widen it
|
|
91
|
+
# so a bounded-int accumulator converges (ADR-56).
|
|
104
92
|
nominal_of("Integer")
|
|
105
93
|
when Union
|
|
106
94
|
union(*type.members.map { |member| widen_value_pinned(member) })
|
|
@@ -109,24 +97,20 @@ module Rigor
|
|
|
109
97
|
end
|
|
110
98
|
end
|
|
111
99
|
|
|
112
|
-
# `Object#method(:name)` carrier. Stores the bound
|
|
113
|
-
# `(
|
|
114
|
-
# substitute the original dispatch at `.call` / `.()` /
|
|
115
|
-
# `[]` time. See {Type::BoundMethod}.
|
|
100
|
+
# `Object#method(:name)` carrier. Stores the bound `(receiver, method_name)` pair so the dispatcher
|
|
101
|
+
# can substitute the original dispatch at `.call` / `.()` / `[]` time. See {Type::BoundMethod}.
|
|
116
102
|
def bound_method_of(receiver_type, method_name)
|
|
117
103
|
BoundMethod.new(receiver_type: receiver_type, method_name: method_name)
|
|
118
104
|
end
|
|
119
105
|
|
|
120
|
-
# Bounded-integer carrier. Each bound is either an `Integer` or
|
|
121
|
-
#
|
|
122
|
-
# `IntegerRange::NEG_INFINITY` / `POS_INFINITY`).
|
|
106
|
+
# Bounded-integer carrier. Each bound is either an `Integer` or one of `:neg_infinity` /
|
|
107
|
+
# `:pos_infinity` (sentinels exposed as `IntegerRange::NEG_INFINITY` / `POS_INFINITY`).
|
|
123
108
|
def integer_range(min, max)
|
|
124
109
|
IntegerRange.new(min, max)
|
|
125
110
|
end
|
|
126
111
|
|
|
127
|
-
# Convenience aliases for the most common bounded shapes. The
|
|
128
|
-
#
|
|
129
|
-
# human-facing output.
|
|
112
|
+
# Convenience aliases for the most common bounded shapes. The named alias survives roundtrip
|
|
113
|
+
# through `describe` for nicer human-facing output.
|
|
130
114
|
def positive_int
|
|
131
115
|
IntegerRange.new(1, IntegerRange::POS_INFINITY)
|
|
132
116
|
end
|
|
@@ -147,11 +131,9 @@ module Rigor
|
|
|
147
131
|
IntegerRange.new(IntegerRange::NEG_INFINITY, IntegerRange::POS_INFINITY)
|
|
148
132
|
end
|
|
149
133
|
|
|
150
|
-
# Point-removal refinement carrier (ADR-3 OQ3 Option C). Use
|
|
151
|
-
# `
|
|
152
|
-
# `
|
|
153
|
-
# `difference(base, removed)` for ad-hoc refinements an
|
|
154
|
-
# `RBS::Extended` annotation introduces.
|
|
134
|
+
# Point-removal refinement carrier (ADR-3 OQ3 Option C). Use `non_empty_string` / `non_zero_int` /
|
|
135
|
+
# `non_empty_array` / `non_empty_hash` for the imported built-in shapes; raw `difference(base,
|
|
136
|
+
# removed)` for ad-hoc refinements an `RBS::Extended` annotation introduces.
|
|
155
137
|
def difference(base, removed)
|
|
156
138
|
Difference.new(base, removed)
|
|
157
139
|
end
|
|
@@ -164,11 +146,9 @@ module Rigor
|
|
|
164
146
|
Difference.new(nominal_of("Integer"), constant_of(0))
|
|
165
147
|
end
|
|
166
148
|
|
|
167
|
-
# `non-empty-array[T]` requires the element type so the
|
|
168
|
-
#
|
|
169
|
-
#
|
|
170
|
-
# `Top` admits any array element when the caller does
|
|
171
|
-
# not have a more specific element type.
|
|
149
|
+
# `non-empty-array[T]` requires the element type so the `Nominal[Array, [T]]` projection through
|
|
150
|
+
# Array#first / #last keeps element precision intact. The default `Top` admits any array element
|
|
151
|
+
# when the caller does not have a more specific element type.
|
|
172
152
|
def non_empty_array(element = top)
|
|
173
153
|
Difference.new(
|
|
174
154
|
nominal_of("Array", type_args: [element]),
|
|
@@ -183,12 +163,10 @@ module Rigor
|
|
|
183
163
|
)
|
|
184
164
|
end
|
|
185
165
|
|
|
186
|
-
# Predicate-subset refinement carrier (ADR-3 OQ3 Option C,
|
|
187
|
-
#
|
|
188
|
-
# `
|
|
189
|
-
#
|
|
190
|
-
# ad-hoc refinements introduced by an `RBS::Extended`
|
|
191
|
-
# annotation or a plugin-contributed predicate.
|
|
166
|
+
# Predicate-subset refinement carrier (ADR-3 OQ3 Option C, second half). Use `lowercase_string` /
|
|
167
|
+
# `uppercase_string` / `numeric_string` for the imported built-in shapes; raw `refined(base,
|
|
168
|
+
# predicate_id)` for ad-hoc refinements introduced by an `RBS::Extended` annotation or a
|
|
169
|
+
# plugin-contributed predicate.
|
|
192
170
|
def refined(base, predicate_id)
|
|
193
171
|
Refined.new(base, predicate_id)
|
|
194
172
|
end
|
|
@@ -197,12 +175,10 @@ module Rigor
|
|
|
197
175
|
Refined.new(nominal_of("String"), :lowercase)
|
|
198
176
|
end
|
|
199
177
|
|
|
200
|
-
# Complement of `lowercase-string`: a `String` with at least
|
|
201
|
-
#
|
|
202
|
-
#
|
|
203
|
-
#
|
|
204
|
-
# `~lowercase-string` narrows to this carrier instead of
|
|
205
|
-
# falling back to `Difference[String, lowercase-string]`.
|
|
178
|
+
# Complement of `lowercase-string`: a `String` with at least one non-lowercase character (i.e. `v !=
|
|
179
|
+
# v.downcase`). Registered as the paired complement of `:lowercase` in {Refined::COMPLEMENT_PAIRS}
|
|
180
|
+
# so `~lowercase-string` narrows to this carrier instead of falling back to `Difference[String,
|
|
181
|
+
# lowercase-string]`.
|
|
206
182
|
def non_lowercase_string
|
|
207
183
|
Refined.new(nominal_of("String"), :not_lowercase)
|
|
208
184
|
end
|
|
@@ -211,9 +187,8 @@ module Rigor
|
|
|
211
187
|
Refined.new(nominal_of("String"), :uppercase)
|
|
212
188
|
end
|
|
213
189
|
|
|
214
|
-
# Complement of `uppercase-string`: a `String` with at least
|
|
215
|
-
#
|
|
216
|
-
# {Refined::COMPLEMENT_PAIRS}.
|
|
190
|
+
# Complement of `uppercase-string`: a `String` with at least one non-uppercase character. Paired
|
|
191
|
+
# with `:uppercase` in {Refined::COMPLEMENT_PAIRS}.
|
|
217
192
|
def non_uppercase_string
|
|
218
193
|
Refined.new(nominal_of("String"), :not_uppercase)
|
|
219
194
|
end
|
|
@@ -222,11 +197,9 @@ module Rigor
|
|
|
222
197
|
Refined.new(nominal_of("String"), :numeric)
|
|
223
198
|
end
|
|
224
199
|
|
|
225
|
-
# Complement of `numeric-string`: a `String` that is not
|
|
226
|
-
#
|
|
227
|
-
#
|
|
228
|
-
# form, etc.). Paired with `:numeric` in
|
|
229
|
-
# {Refined::COMPLEMENT_PAIRS}.
|
|
200
|
+
# Complement of `numeric-string`: a `String` that is not accepted by Rigor's Ruby numeric-string
|
|
201
|
+
# predicate (contains at least one non-digit, has a malformed numeric form, etc.). Paired with
|
|
202
|
+
# `:numeric` in {Refined::COMPLEMENT_PAIRS}.
|
|
230
203
|
def non_numeric_string
|
|
231
204
|
Refined.new(nominal_of("String"), :not_numeric)
|
|
232
205
|
end
|
|
@@ -243,36 +216,27 @@ module Rigor
|
|
|
243
216
|
Refined.new(nominal_of("String"), :hex_int)
|
|
244
217
|
end
|
|
245
218
|
|
|
246
|
-
# `literal-string` — a `String` that is statically known to
|
|
247
|
-
#
|
|
248
|
-
#
|
|
249
|
-
#
|
|
250
|
-
#
|
|
251
|
-
# literal-
|
|
252
|
-
# where the concrete value is unknown but literal-ness has
|
|
253
|
-
# been established (an RBS::Extended `return: literal-string`
|
|
254
|
-
# annotation, or interpolation over literal-bearing parts).
|
|
219
|
+
# `literal-string` — a `String` that is statically known to come from a source-code literal (or a
|
|
220
|
+
# composition of literals). v0.0.9 tracks this flow through interpolation `"#{...}"`, leaving
|
|
221
|
+
# propagation through `+` / `<<` to a later slice. Every `Constant<String>` is implicitly
|
|
222
|
+
# literal-string-compatible; the carrier exists for cases where the concrete value is unknown but
|
|
223
|
+
# literal-ness has been established (an RBS::Extended `return: literal-string` annotation, or
|
|
224
|
+
# interpolation over literal-bearing parts).
|
|
255
225
|
def literal_string
|
|
256
226
|
Refined.new(nominal_of("String"), :literal_string)
|
|
257
227
|
end
|
|
258
228
|
|
|
259
|
-
# `non-empty-literal-string` = `non-empty-string ∩ literal-string`.
|
|
260
|
-
#
|
|
261
|
-
# with the predicate-subset half. Both members erase to
|
|
262
|
-
# `String`.
|
|
229
|
+
# `non-empty-literal-string` = `non-empty-string ∩ literal-string`. Composes the point-removal half
|
|
230
|
+
# (`Difference[String, ""]`) with the predicate-subset half. Both members erase to `String`.
|
|
263
231
|
def non_empty_literal_string
|
|
264
232
|
intersection(non_empty_string, literal_string)
|
|
265
233
|
end
|
|
266
234
|
|
|
267
|
-
# Recognises the carriers that participate in literal-string
|
|
268
|
-
#
|
|
269
|
-
#
|
|
270
|
-
# `
|
|
271
|
-
#
|
|
272
|
-
# `ExpressionTyper#type_of_interpolated_string` and the
|
|
273
|
-
# `LiteralStringFolding` dispatcher tier so propagation
|
|
274
|
-
# through interpolation and `+`/`*` composition stays
|
|
275
|
-
# consistent.
|
|
235
|
+
# Recognises the carriers that participate in literal-string flow tracking: any `Constant<String>`
|
|
236
|
+
# (constants are literal by construction), the `literal-string` Refined carrier, an `Intersection`
|
|
237
|
+
# containing `literal-string`, or a `Union` whose every member qualifies. Used by
|
|
238
|
+
# `ExpressionTyper#type_of_interpolated_string` and the `LiteralStringFolding` dispatcher tier so
|
|
239
|
+
# propagation through interpolation and `+`/`*` composition stays consistent.
|
|
276
240
|
def literal_string_compatible?(type)
|
|
277
241
|
case type
|
|
278
242
|
when Constant then type.value.is_a?(String)
|
|
@@ -289,14 +253,12 @@ module Rigor
|
|
|
289
253
|
refined.base.class_name == "String"
|
|
290
254
|
end
|
|
291
255
|
|
|
292
|
-
# Returns true when `type` is statically known to be a
|
|
293
|
-
#
|
|
294
|
-
#
|
|
295
|
-
# the non-empty guarantee through `+` and `*`.
|
|
256
|
+
# Returns true when `type` is statically known to be a non-empty String — i.e. its value can never
|
|
257
|
+
# be `""`. Used at String binary-operator dispatch sites to propagate the non-empty guarantee
|
|
258
|
+
# through `+` and `*`.
|
|
296
259
|
#
|
|
297
260
|
# - `Constant[s]` where `s != ""` — a concrete non-empty literal.
|
|
298
|
-
# - `Difference[Nominal[String], Constant[""]]` — the canonical
|
|
299
|
-
# `non-empty-string` carrier.
|
|
261
|
+
# - `Difference[Nominal[String], Constant[""]]` — the canonical `non-empty-string` carrier.
|
|
300
262
|
# - `Intersection[…]` — any member suffices (set-theoretic subset).
|
|
301
263
|
# - `Union[…]` — all members must qualify (the join may include "").
|
|
302
264
|
def non_empty_string_compatible?(type)
|
|
@@ -316,16 +278,14 @@ module Rigor
|
|
|
316
278
|
diff.removed.value == ""
|
|
317
279
|
end
|
|
318
280
|
|
|
319
|
-
# Returns true when `type` is statically known to be a
|
|
320
|
-
#
|
|
321
|
-
#
|
|
322
|
-
# the non-zero guarantee through `*` and identity methods.
|
|
281
|
+
# Returns true when `type` is statically known to be a non-zero Integer — i.e. its value can never
|
|
282
|
+
# be `0`. Used at Integer arithmetic dispatch sites to propagate the non-zero guarantee through `*`
|
|
283
|
+
# and identity methods.
|
|
323
284
|
#
|
|
324
285
|
# - `Constant[n]` where `n != 0` — a concrete non-zero literal.
|
|
325
|
-
# - `Difference[Nominal[Integer], Constant[0]]` — the canonical
|
|
326
|
-
#
|
|
327
|
-
#
|
|
328
|
-
# ([1,+∞)) and `negative-int` ([-∞,-1]) qualify.
|
|
286
|
+
# - `Difference[Nominal[Integer], Constant[0]]` — the canonical `non-zero-int` carrier.
|
|
287
|
+
# - `IntegerRange` that does not cover 0 — both `positive-int` ([1,+∞)) and `negative-int` ([-∞,-1])
|
|
288
|
+
# qualify.
|
|
329
289
|
# - `Intersection[…]` — any member suffices.
|
|
330
290
|
# - `Union[…]` — all members must qualify.
|
|
331
291
|
def non_zero_int_compatible?(type)
|
|
@@ -346,23 +306,18 @@ module Rigor
|
|
|
346
306
|
diff.removed.value.zero?
|
|
347
307
|
end
|
|
348
308
|
|
|
349
|
-
# Normalised intersection. Flattens nested Intersections,
|
|
350
|
-
#
|
|
351
|
-
# `
|
|
352
|
-
#
|
|
353
|
-
# results so a degenerate intersection never reaches the
|
|
354
|
-
# carrier. See ADR-3 OQ3 for the rationale; the lattice
|
|
355
|
-
# algebra is in
|
|
309
|
+
# Normalised intersection. Flattens nested Intersections, drops `Top` members, collapses to `Bot` if
|
|
310
|
+
# any member is `Bot`, deduplicates structurally-equal members, sorts the survivors by
|
|
311
|
+
# `describe(:short)`, and collapses 0-/1-member results so a degenerate intersection never reaches
|
|
312
|
+
# the carrier. See ADR-3 OQ3 for the rationale; the lattice algebra is in
|
|
356
313
|
# [`value-lattice.md`](docs/type-specification/value-lattice.md).
|
|
357
314
|
def intersection(*members)
|
|
358
315
|
collapse_intersection(normalised_intersection_members(members))
|
|
359
316
|
end
|
|
360
317
|
|
|
361
|
-
# `non-empty-lowercase-string` = non-empty-string ∩
|
|
362
|
-
#
|
|
363
|
-
#
|
|
364
|
-
# (`Refined[String, :lowercase]`). Both members erase to
|
|
365
|
-
# `String` so the carrier's RBS erasure is unambiguous.
|
|
318
|
+
# `non-empty-lowercase-string` = non-empty-string ∩ lowercase-string. Composes the point-removal
|
|
319
|
+
# half (`Difference[String, ""]`) with the predicate-subset half (`Refined[String, :lowercase]`).
|
|
320
|
+
# Both members erase to `String` so the carrier's RBS erasure is unambiguous.
|
|
366
321
|
def non_empty_lowercase_string
|
|
367
322
|
intersection(non_empty_string, lowercase_string)
|
|
368
323
|
end
|
|
@@ -371,16 +326,14 @@ module Rigor
|
|
|
371
326
|
intersection(non_empty_string, uppercase_string)
|
|
372
327
|
end
|
|
373
328
|
|
|
374
|
-
# Constructs a heterogeneous, fixed-arity Tuple from positional
|
|
375
|
-
#
|
|
376
|
-
# which is structurally distinct from the raw `Nominal[Array]`.
|
|
329
|
+
# Constructs a heterogeneous, fixed-arity Tuple from positional element types. `tuple_of()`
|
|
330
|
+
# produces the empty tuple `Tuple[]`, which is structurally distinct from the raw `Nominal[Array]`.
|
|
377
331
|
def tuple_of(*elements)
|
|
378
332
|
Tuple.new(elements)
|
|
379
333
|
end
|
|
380
334
|
|
|
381
|
-
# Constructs a HashShape from an ordered (Symbol|String) -> type
|
|
382
|
-
#
|
|
383
|
-
# MUST NOT rely on later mutation.
|
|
335
|
+
# Constructs a HashShape from an ordered (Symbol|String) -> type map. The argument is duped and
|
|
336
|
+
# frozen by the carrier; callers MUST NOT rely on later mutation.
|
|
384
337
|
def hash_shape_of(pairs = nil, **options)
|
|
385
338
|
if pairs.nil?
|
|
386
339
|
pairs = options
|
|
@@ -390,64 +343,57 @@ module Rigor
|
|
|
390
343
|
HashShape.new(pairs, **options)
|
|
391
344
|
end
|
|
392
345
|
|
|
393
|
-
# ADR-48 — the class object produced by `Data.define(:x, :y)`.
|
|
394
|
-
# `
|
|
395
|
-
#
|
|
396
|
-
# for the anonymous `Data.define(...)` result.
|
|
346
|
+
# ADR-48 — the class object produced by `Data.define(:x, :y)`. `members` is the ordered Symbol
|
|
347
|
+
# member-name list; `class_name` tags the class when known (the named-subclass form) and is nil for
|
|
348
|
+
# the anonymous `Data.define(...)` result.
|
|
397
349
|
def data_class_of(members:, class_name: nil)
|
|
398
350
|
DataClass.new(members, class_name)
|
|
399
351
|
end
|
|
400
352
|
|
|
401
|
-
# ADR-48 — a `Data.define` value instance. `members` is the ordered
|
|
402
|
-
#
|
|
403
|
-
# class when known.
|
|
353
|
+
# ADR-48 — a `Data.define` value instance. `members` is the ordered member-name -> value-type map;
|
|
354
|
+
# `class_name` tags the instance's class when known.
|
|
404
355
|
def data_instance_of(members:, class_name: nil)
|
|
405
356
|
DataInstance.new(members, class_name)
|
|
406
357
|
end
|
|
407
358
|
|
|
408
|
-
# ADR-48 Struct follow-up — the class object produced by
|
|
409
|
-
#
|
|
410
|
-
# list; `keyword_init` records the `keyword_init:` flag; `class_name`
|
|
359
|
+
# ADR-48 Struct follow-up — the class object produced by `Struct.new(:x, :y)`. `members` is the
|
|
360
|
+
# ordered Symbol member-name list; `keyword_init` records the `keyword_init:` flag; `class_name`
|
|
411
361
|
# tags the class when known (the named-subclass form).
|
|
412
362
|
def struct_class_of(members:, class_name: nil, keyword_init: false)
|
|
413
363
|
StructClass.new(members, class_name, keyword_init: keyword_init)
|
|
414
364
|
end
|
|
415
365
|
|
|
416
|
-
# ADR-48 Struct follow-up — a `Struct.new` value instance. `members`
|
|
417
|
-
#
|
|
418
|
-
# instance's class when known.
|
|
366
|
+
# ADR-48 Struct follow-up — a `Struct.new` value instance. `members` is the ordered member-name ->
|
|
367
|
+
# value-type map; `class_name` tags the instance's class when known.
|
|
419
368
|
def struct_instance_of(members:, class_name: nil)
|
|
420
369
|
StructInstance.new(members, class_name)
|
|
421
370
|
end
|
|
422
371
|
|
|
423
|
-
# Normalized union. Flattens nested Unions, deduplicates structurally
|
|
424
|
-
#
|
|
372
|
+
# Normalized union. Flattens nested Unions, deduplicates structurally equal members, drops Bot, and
|
|
373
|
+
# collapses 0/1-member results.
|
|
425
374
|
def union(*types)
|
|
426
375
|
result = collapse_union(normalized_union_members(types))
|
|
427
376
|
if Inference::BudgetTrace.enabled? && result.is_a?(Union)
|
|
428
377
|
Inference::BudgetTrace.observe(Inference::BudgetTrace::UNION_ARITY, result.members.size)
|
|
429
378
|
end
|
|
430
|
-
# `rigor trace` — degenerate merges (`1 | 1 → 1`, Dynamic
|
|
431
|
-
#
|
|
432
|
-
# teachable moment.
|
|
379
|
+
# `rigor trace` — degenerate merges (`1 | 1 → 1`, Dynamic absorption) are recorded too; the
|
|
380
|
+
# collapse itself is the teachable moment.
|
|
433
381
|
Inference::FlowTracer.union(types, result) if Inference::FlowTracer.active? && types.size >= 2
|
|
434
382
|
result
|
|
435
383
|
end
|
|
436
384
|
|
|
437
|
-
# `key_of[T]` type function — projects the type-level
|
|
438
|
-
# union of `T`'s known keys. Recognised shapes:
|
|
385
|
+
# `key_of[T]` type function — projects the type-level union of `T`'s known keys. Recognised shapes:
|
|
439
386
|
#
|
|
440
387
|
# - `Type::HashShape{a: A, b: B}` → `Constant<:a> | Constant<:b>`.
|
|
441
388
|
# - `Type::Tuple[A, B, C]` → `Constant<0> | Constant<1> | Constant<2>`.
|
|
442
389
|
# - `Type::Nominal["Hash", [K, V]]` → `K` (untyped if absent).
|
|
443
390
|
# - `Type::Nominal["Array", [E]]` → `non-negative-int`.
|
|
444
|
-
# - `Type::Constant` whose value is a Hash / Array / Range —
|
|
445
|
-
#
|
|
391
|
+
# - `Type::Constant` whose value is a Hash / Array / Range — project through the literal's
|
|
392
|
+
# per-element keys.
|
|
446
393
|
#
|
|
447
|
-
# Other inputs (`Top`, `Dynamic`, untyped Nominals, `Union`,
|
|
448
|
-
# `
|
|
449
|
-
#
|
|
450
|
-
# narrow further when they know more.
|
|
394
|
+
# Other inputs (`Top`, `Dynamic`, untyped Nominals, `Union`, `Refined`, `Difference`,
|
|
395
|
+
# `Intersection`) project to `top` so the type function always returns a value — callers may narrow
|
|
396
|
+
# further when they know more.
|
|
451
397
|
def key_of(type)
|
|
452
398
|
case type
|
|
453
399
|
when HashShape then hash_shape_keys(type)
|
|
@@ -458,15 +404,15 @@ module Rigor
|
|
|
458
404
|
end
|
|
459
405
|
end
|
|
460
406
|
|
|
461
|
-
# `value_of[T]` type function — projects the type-level
|
|
462
|
-
#
|
|
407
|
+
# `value_of[T]` type function — projects the type-level union of `T`'s known values. Mirror of
|
|
408
|
+
# `key_of`:
|
|
463
409
|
#
|
|
464
410
|
# - `Type::HashShape{a: A, b: B}` → `A | B`.
|
|
465
411
|
# - `Type::Tuple[A, B, C]` → `A | B | C`.
|
|
466
412
|
# - `Type::Nominal["Hash", [K, V]]` → `V` (untyped if absent).
|
|
467
413
|
# - `Type::Nominal["Array", [E]]` → `E` (untyped if absent).
|
|
468
|
-
# - `Type::Constant` whose value is a Hash / Array / Range —
|
|
469
|
-
#
|
|
414
|
+
# - `Type::Constant` whose value is a Hash / Array / Range — union of `Constant<…>` for each
|
|
415
|
+
# element.
|
|
470
416
|
def value_of(type)
|
|
471
417
|
case type
|
|
472
418
|
when HashShape then hash_shape_values(type)
|
|
@@ -477,15 +423,11 @@ module Rigor
|
|
|
477
423
|
end
|
|
478
424
|
end
|
|
479
425
|
|
|
480
|
-
# `int_mask[1, 2, 4]` type function — every Integer
|
|
481
|
-
#
|
|
482
|
-
#
|
|
483
|
-
# `
|
|
484
|
-
#
|
|
485
|
-
# the cardinality exceeds `INT_MASK_UNION_LIMIT`. Returns
|
|
486
|
-
# `nil` when the input is malformed (non-integer flag,
|
|
487
|
-
# negative flag, or too many flags to compute the closure
|
|
488
|
-
# cheaply).
|
|
426
|
+
# `int_mask[1, 2, 4]` type function — every Integer representable by a bitwise OR over the listed
|
|
427
|
+
# flags, including 0. The closure of `[1, 2, 4]` is `{0, 1, 2, 3, 4, 5, 6, 7}`. Returns a
|
|
428
|
+
# `Union[Constant…]` for small closures and a covering `IntegerRange` once the cardinality exceeds
|
|
429
|
+
# `INT_MASK_UNION_LIMIT`. Returns `nil` when the input is malformed (non-integer flag, negative
|
|
430
|
+
# flag, or too many flags to compute the closure cheaply).
|
|
489
431
|
INT_MASK_FLAG_LIMIT = 6
|
|
490
432
|
INT_MASK_UNION_LIMIT = 16
|
|
491
433
|
private_constant :INT_MASK_FLAG_LIMIT, :INT_MASK_UNION_LIMIT
|
|
@@ -505,12 +447,9 @@ module Rigor
|
|
|
505
447
|
end
|
|
506
448
|
end
|
|
507
449
|
|
|
508
|
-
# `int_mask_of[T]` — derives the int_mask closure from
|
|
509
|
-
# a
|
|
510
|
-
#
|
|
511
|
-
# member must be a `Constant<Integer>`). Returns nil for
|
|
512
|
-
# incompatible inputs (Top, Dynamic, IntegerRange, mixed
|
|
513
|
-
# member shapes).
|
|
450
|
+
# `int_mask_of[T]` — derives the int_mask closure from a finite integer-literal type: `Constant<n>`
|
|
451
|
+
# (single flag), `Union[Constant…]` (every member must be a `Constant<Integer>`). Returns nil for
|
|
452
|
+
# incompatible inputs (Top, Dynamic, IntegerRange, mixed member shapes).
|
|
514
453
|
def int_mask_of(type)
|
|
515
454
|
flags = extract_constant_int_set(type)
|
|
516
455
|
return nil if flags.nil?
|
|
@@ -518,20 +457,16 @@ module Rigor
|
|
|
518
457
|
int_mask(flags)
|
|
519
458
|
end
|
|
520
459
|
|
|
521
|
-
# `T[K]` indexed-access type operator — extracts the type
|
|
522
|
-
# at index / key `K` from a structured `T`:
|
|
460
|
+
# `T[K]` indexed-access type operator — extracts the type at index / key `K` from a structured `T`:
|
|
523
461
|
#
|
|
524
|
-
# - `Tuple[A, B, C][Constant<i>]` → `A` / `B` / `C` (out-of-
|
|
525
|
-
# range indices return `Top` for safety).
|
|
462
|
+
# - `Tuple[A, B, C][Constant<i>]` → `A` / `B` / `C` (out-of-range indices return `Top` for safety).
|
|
526
463
|
# - `HashShape{a: A, b: B}[Constant<:a>]` → `A`.
|
|
527
464
|
# - `Nominal[Hash, [K, V]][_]` → `V` (untyped if absent).
|
|
528
465
|
# - `Nominal[Array, [E]][_]` → `E` (untyped if absent).
|
|
529
466
|
#
|
|
530
|
-
# Other shapes (`Top`, `Dynamic`, untyped Nominals,
|
|
531
|
-
# `
|
|
532
|
-
#
|
|
533
|
-
# `Type::t`; only `Type::Constant` keys produce a precise
|
|
534
|
-
# answer.
|
|
467
|
+
# Other shapes (`Top`, `Dynamic`, untyped Nominals, `Union`, `Refined`, `Difference`,
|
|
468
|
+
# `Intersection`) project to `Top`. The key argument is itself a `Type::t`; only `Type::Constant`
|
|
469
|
+
# keys produce a precise answer.
|
|
535
470
|
def indexed_access(type, key)
|
|
536
471
|
case type
|
|
537
472
|
when Tuple then tuple_indexed_access(type, key)
|
|
@@ -542,18 +477,14 @@ module Rigor
|
|
|
542
477
|
end
|
|
543
478
|
end
|
|
544
479
|
|
|
545
|
-
# `pick_of[T, K]` shape-projection — keeps only the entries
|
|
546
|
-
#
|
|
547
|
-
# `K`. ADR-13 § "Shape projection / Restriction and removal".
|
|
480
|
+
# `pick_of[T, K]` shape-projection — keeps only the entries of `T` whose key is in the literal-key
|
|
481
|
+
# set extracted from `K`. ADR-13 § "Shape projection / Restriction and removal".
|
|
548
482
|
#
|
|
549
|
-
# Phase A handles `Type::HashShape` (literal-key K).
|
|
550
|
-
#
|
|
551
|
-
#
|
|
552
|
-
# `
|
|
553
|
-
#
|
|
554
|
-
# `dynamic.shape.lossy-projection` diagnostic that flags
|
|
555
|
-
# the boundary lands when caller-side diagnostic threading
|
|
556
|
-
# arrives).
|
|
483
|
+
# Phase A handles `Type::HashShape` (literal-key K). Phase B (slice 5) extends to `Type::Tuple`
|
|
484
|
+
# (integer-index K) — `pick_of[Tuple[A, B, C], 0 | 2]` evaluates to `Tuple[A, C]`. Non-shape inputs
|
|
485
|
+
# (`Type::Nominal`, etc.) return `type` unchanged ("lossy degradation"; the
|
|
486
|
+
# `dynamic.shape.lossy-projection` diagnostic that flags the boundary lands when caller-side
|
|
487
|
+
# diagnostic threading arrives).
|
|
557
488
|
def pick_of(type, keys)
|
|
558
489
|
case type
|
|
559
490
|
when HashShape then hash_shape_pick(type, keys)
|
|
@@ -562,9 +493,8 @@ module Rigor
|
|
|
562
493
|
end
|
|
563
494
|
end
|
|
564
495
|
|
|
565
|
-
# `omit_of[T, K]` shape-projection — dual of {pick_of}.
|
|
566
|
-
#
|
|
567
|
-
# `Tuple`) is in the literal-key set extracted from `K`.
|
|
496
|
+
# `omit_of[T, K]` shape-projection — dual of {pick_of}. Drops the entries / positions whose key (or
|
|
497
|
+
# index, for a `Tuple`) is in the literal-key set extracted from `K`.
|
|
568
498
|
def omit_of(type, keys)
|
|
569
499
|
case type
|
|
570
500
|
when HashShape then hash_shape_omit(type, keys)
|
|
@@ -573,11 +503,9 @@ module Rigor
|
|
|
573
503
|
end
|
|
574
504
|
end
|
|
575
505
|
|
|
576
|
-
# `partial_of[T]` shape-projection — flips every required
|
|
577
|
-
#
|
|
578
|
-
#
|
|
579
|
-
# distinguishes "key absent" from "key present with nil
|
|
580
|
-
# value", so flipping required-ness is sufficient.
|
|
506
|
+
# `partial_of[T]` shape-projection — flips every required entry of `T` to optional. ADR-13 §
|
|
507
|
+
# "Required-ness flips". Does NOT add `nil` to value types — Rigor's HashShape distinguishes "key
|
|
508
|
+
# absent" from "key present with nil value", so flipping required-ness is sufficient.
|
|
581
509
|
def partial_of(type)
|
|
582
510
|
return type unless type.is_a?(HashShape)
|
|
583
511
|
|
|
@@ -590,8 +518,8 @@ module Rigor
|
|
|
590
518
|
)
|
|
591
519
|
end
|
|
592
520
|
|
|
593
|
-
# `required_of[T]` shape-projection — inverse of
|
|
594
|
-
#
|
|
521
|
+
# `required_of[T]` shape-projection — inverse of {partial_of}; flips every optional entry to
|
|
522
|
+
# required.
|
|
595
523
|
def required_of(type)
|
|
596
524
|
return type unless type.is_a?(HashShape)
|
|
597
525
|
|
|
@@ -604,9 +532,8 @@ module Rigor
|
|
|
604
532
|
)
|
|
605
533
|
end
|
|
606
534
|
|
|
607
|
-
# `readonly_of[T]` shape-projection — marks every entry of
|
|
608
|
-
#
|
|
609
|
-
# does NOT prove the underlying Ruby Hash is frozen.
|
|
535
|
+
# `readonly_of[T]` shape-projection — marks every entry of `T` as read-only in the current view.
|
|
536
|
+
# View-level only — does NOT prove the underlying Ruby Hash is frozen.
|
|
610
537
|
def readonly_of(type)
|
|
611
538
|
return type unless type.is_a?(HashShape)
|
|
612
539
|
|
|
@@ -619,18 +546,14 @@ module Rigor
|
|
|
619
546
|
)
|
|
620
547
|
end
|
|
621
548
|
|
|
622
|
-
# Predicate that a shape-projection (`pick_of`, `omit_of`,
|
|
623
|
-
# `
|
|
624
|
-
#
|
|
625
|
-
#
|
|
626
|
-
# `dynamic.shape.lossy-projection` diagnostic at the site
|
|
627
|
-
# where the projection was authored.
|
|
549
|
+
# Predicate that a shape-projection (`pick_of`, `omit_of`, `partial_of`, `required_of`,
|
|
550
|
+
# `readonly_of`) would degrade to "input unchanged" on this carrier. Callers consult this BEFORE
|
|
551
|
+
# invoking the projection so they can emit a `dynamic.shape.lossy-projection` diagnostic at the
|
|
552
|
+
# site where the projection was authored.
|
|
628
553
|
#
|
|
629
|
-
# `HashShape` and `Tuple` carry shape-level information
|
|
630
|
-
#
|
|
631
|
-
#
|
|
632
|
-
# / parser callers; this predicate stands alone in slice 5
|
|
633
|
-
# for unit-test coverage and future composition.
|
|
554
|
+
# `HashShape` and `Tuple` carry shape-level information the projections honour; every other carrier
|
|
555
|
+
# is lossy. Slice 5b wires diagnostic emission through `RbsExtended` / parser callers; this
|
|
556
|
+
# predicate stands alone in slice 5 for unit-test coverage and future composition.
|
|
634
557
|
def shape_projection_lossy?(type)
|
|
635
558
|
!type.is_a?(HashShape) && !type.is_a?(Tuple)
|
|
636
559
|
end
|
|
@@ -684,20 +607,12 @@ module Rigor
|
|
|
684
607
|
end
|
|
685
608
|
end
|
|
686
609
|
|
|
687
|
-
# `Type::Constant` only carries scalar literals (Integer
|
|
688
|
-
# / Float / String / Symbol / Range / Rational / Complex
|
|
689
|
-
# / true / false / nil); Array and Hash literals become
|
|
690
|
-
# Tuple / HashShape carriers earlier in the typer. Range
|
|
691
|
-
# is the only scalar with meaningful key/value
|
|
692
|
-
# projections.
|
|
693
610
|
def compute_int_mask_closure(flags)
|
|
694
611
|
unique = flags.uniq
|
|
695
612
|
return [0] if unique.empty?
|
|
696
613
|
|
|
697
|
-
# Closure under bitwise OR over a set of non-negative
|
|
698
|
-
#
|
|
699
|
-
# are bit-disjoint; otherwise it's a strict subset.
|
|
700
|
-
# Enumerate every subset's OR.
|
|
614
|
+
# Closure under bitwise OR over a set of non-negative integers is `0..(max_or_value)` only when
|
|
615
|
+
# the flags are bit-disjoint; otherwise it's a strict subset. Enumerate every subset's OR.
|
|
701
616
|
closure = Set.new([0])
|
|
702
617
|
unique.each do |flag|
|
|
703
618
|
closure |= closure.map { |c| c | flag }
|
|
@@ -714,13 +629,10 @@ module Rigor
|
|
|
714
629
|
end
|
|
715
630
|
end
|
|
716
631
|
|
|
717
|
-
# Literal-key set extraction for {pick_of} / {omit_of}.
|
|
718
|
-
#
|
|
719
|
-
#
|
|
720
|
-
#
|
|
721
|
-
# (untyped, Top, Difference, Refined, mixed-kind union,
|
|
722
|
-
# etc.) — callers degrade to "input unchanged" per
|
|
723
|
-
# ADR-13's lossy-projection rule.
|
|
632
|
+
# Literal-key set extraction for {pick_of} / {omit_of}. Accepts `Constant<Symbol|String>` or
|
|
633
|
+
# `Union[Constant…]` where every member is such a Constant. Returns `nil` when the shape can't be
|
|
634
|
+
# reduced to a finite key set (untyped, Top, Difference, Refined, mixed-kind union, etc.) —
|
|
635
|
+
# callers degrade to "input unchanged" per ADR-13's lossy-projection rule.
|
|
724
636
|
def extract_constant_key_set(type)
|
|
725
637
|
case type
|
|
726
638
|
when Constant then constant_key_set(type)
|
|
@@ -743,12 +655,10 @@ module Rigor
|
|
|
743
655
|
value.is_a?(Symbol) || value.is_a?(String)
|
|
744
656
|
end
|
|
745
657
|
|
|
746
|
-
# Rebuild a {HashShape} from the subset of `keys` the
|
|
747
|
-
#
|
|
748
|
-
#
|
|
749
|
-
#
|
|
750
|
-
# drop from each policy list. Used by both {pick_of}
|
|
751
|
-
# (intersection with K) and {omit_of} (set difference).
|
|
658
|
+
# Rebuild a {HashShape} from the subset of `keys` the caller decided to keep. Preserves required /
|
|
659
|
+
# optional / read-only classification AND the extra-keys policy of the source shape; entries
|
|
660
|
+
# dropped from `pairs` also drop from each policy list. Used by both {pick_of} (intersection with
|
|
661
|
+
# K) and {omit_of} (set difference).
|
|
752
662
|
def rebuild_hash_shape_with_keys(shape, kept_keys)
|
|
753
663
|
HashShape.new(
|
|
754
664
|
shape.pairs.slice(*kept_keys),
|
|
@@ -773,12 +683,10 @@ module Rigor
|
|
|
773
683
|
rebuild_hash_shape_with_keys(type, type.pairs.keys - key_set)
|
|
774
684
|
end
|
|
775
685
|
|
|
776
|
-
# ADR-13 slice 5 — Tuple support. `K` MUST be a
|
|
777
|
-
#
|
|
778
|
-
#
|
|
779
|
-
#
|
|
780
|
-
# indices are dropped silently per slice 5's permissive
|
|
781
|
-
# take — surface diagnostics are slice 5b material.
|
|
686
|
+
# ADR-13 slice 5 — Tuple support. `K` MUST be a `Constant<Integer>` or `Union[Constant<Integer>,
|
|
687
|
+
# …]`; other K shapes (or non-integer Constants in a Union) return the input unchanged. Negative
|
|
688
|
+
# or out-of-range indices are dropped silently per slice 5's permissive take — surface
|
|
689
|
+
# diagnostics are slice 5b material.
|
|
782
690
|
def tuple_pick(type, keys)
|
|
783
691
|
index_set = extract_tuple_index_set(keys, type.elements.size)
|
|
784
692
|
return type if index_set.nil?
|
|
@@ -794,12 +702,10 @@ module Rigor
|
|
|
794
702
|
Tuple.new(type.elements.each_with_index.reject { |_, i| dropped.include?(i) }.map(&:first))
|
|
795
703
|
end
|
|
796
704
|
|
|
797
|
-
# Extracts a sorted, deduplicated set of in-range integer
|
|
798
|
-
#
|
|
799
|
-
#
|
|
800
|
-
#
|
|
801
|
-
# "lossy projection" (current pick / omit just produce an
|
|
802
|
-
# empty Tuple).
|
|
705
|
+
# Extracts a sorted, deduplicated set of in-range integer indices from a `Constant<Integer>` /
|
|
706
|
+
# `Union[Constant<Integer>, …]` carrier. Out-of-range indices are dropped silently; the caller
|
|
707
|
+
# decides whether an empty result still means "lossy projection" (current pick / omit just
|
|
708
|
+
# produce an empty Tuple).
|
|
803
709
|
def extract_tuple_index_set(type, size)
|
|
804
710
|
flags = extract_constant_int_set(type)
|
|
805
711
|
return nil if flags.nil?
|
|
@@ -841,6 +747,9 @@ module Rigor
|
|
|
841
747
|
top
|
|
842
748
|
end
|
|
843
749
|
|
|
750
|
+
# `Type::Constant` only carries scalar literals (Integer / Float / String / Symbol / Range /
|
|
751
|
+
# Rational / Complex / true / false / nil); Array and Hash literals become Tuple / HashShape
|
|
752
|
+
# carriers earlier in the typer. Range is the only scalar with meaningful key/value projections.
|
|
844
753
|
def constant_keys(value)
|
|
845
754
|
return non_negative_int if value.is_a?(Range) && value.begin.is_a?(Integer)
|
|
846
755
|
|
|
@@ -888,10 +797,9 @@ module Rigor
|
|
|
888
797
|
end
|
|
889
798
|
end
|
|
890
799
|
|
|
891
|
-
# Symmetric counterparts to the Union normalisers. The
|
|
892
|
-
#
|
|
893
|
-
#
|
|
894
|
-
# (intersecting with the universal type is a no-op).
|
|
800
|
+
# Symmetric counterparts to the Union normalisers. The absorbing element is `Bot` (anything
|
|
801
|
+
# intersected with nothing is nothing) and the identity element is `Top` (intersecting with the
|
|
802
|
+
# universal type is a no-op).
|
|
895
803
|
def normalised_intersection_members(types)
|
|
896
804
|
flattened = []
|
|
897
805
|
types.each { |t| flatten_intersection_into(flattened, t) }
|