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
data/lib/rigor/type/refined.rb
CHANGED
|
@@ -8,35 +8,26 @@ require_relative "acceptance_router"
|
|
|
8
8
|
|
|
9
9
|
module Rigor
|
|
10
10
|
module Type
|
|
11
|
-
# `Refined[base, predicate_id]` — predicate-subset half of
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# Decision Option C). Sibling of `Type::Difference`, which
|
|
15
|
-
# carries the point-removal half.
|
|
11
|
+
# `Refined[base, predicate_id]` — predicate-subset half of the OQ3 refinement-carrier strategy
|
|
12
|
+
# ([ADR-3](docs/adr/3-type-representation.md), Working Decision Option C). Sibling of
|
|
13
|
+
# `Type::Difference`, which carries the point-removal half.
|
|
16
14
|
#
|
|
17
15
|
# lowercase-string = Refined[Nominal[String], :lowercase]
|
|
18
16
|
# uppercase-string = Refined[Nominal[String], :uppercase]
|
|
19
17
|
# numeric-string = Refined[Nominal[String], :numeric]
|
|
20
18
|
#
|
|
21
|
-
# The carrier wraps a base type and a `predicate_id` Symbol
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
# value; for non-Constant receivers the carrier is a marker
|
|
25
|
-
# the catalog tier consults to project `String#downcase` /
|
|
19
|
+
# The carrier wraps a base type and a `predicate_id` Symbol drawn from {PREDICATES}. The recogniser
|
|
20
|
+
# is invoked at constant-fold and acceptance time over a `Constant<base>` value; for non-Constant
|
|
21
|
+
# receivers the carrier is a marker the catalog tier consults to project `String#downcase` /
|
|
26
22
|
# `String#upcase` (etc.) into the matching refinement.
|
|
27
23
|
#
|
|
28
|
-
# Display routes through {CANONICAL_NAMES}: registered
|
|
29
|
-
# `
|
|
30
|
-
#
|
|
31
|
-
# combinations fall back to the `base & predicate?` operator
|
|
32
|
-
# form per
|
|
33
|
-
# [`type-operators.md`](docs/type-specification/type-operators.md).
|
|
24
|
+
# Display routes through {CANONICAL_NAMES}: registered `(base_class_name, predicate_id)` pairs print
|
|
25
|
+
# in their kebab-case spelling (`lowercase-string`); unregistered combinations fall back to the
|
|
26
|
+
# `base & predicate?` operator form per [`type-operators.md`](docs/type-specification/type-operators.md).
|
|
34
27
|
#
|
|
35
|
-
# Construction MUST go through `Type::Combinator.refined` /
|
|
36
|
-
#
|
|
37
|
-
# `
|
|
38
|
-
# Direct `.new` is an internal escape hatch for tests and
|
|
39
|
-
# combinator's own implementation.
|
|
28
|
+
# Construction MUST go through `Type::Combinator.refined` / the per-name factories
|
|
29
|
+
# (`Combinator.lowercase_string`, `Combinator.uppercase_string`, `Combinator.numeric_string`). Direct
|
|
30
|
+
# `.new` is an internal escape hatch for tests and combinator's own implementation.
|
|
40
31
|
class Refined
|
|
41
32
|
attr_reader :base, :predicate_id
|
|
42
33
|
|
|
@@ -55,8 +46,8 @@ module Rigor
|
|
|
55
46
|
"#{base.describe(verbosity)} & #{predicate_id}?"
|
|
56
47
|
end
|
|
57
48
|
|
|
58
|
-
# Erases to the base nominal: every refinement MUST erase
|
|
59
|
-
#
|
|
49
|
+
# Erases to the base nominal: every refinement MUST erase to its base per
|
|
50
|
+
# [`rbs-erasure.md`](docs/type-specification/rbs-erasure.md).
|
|
60
51
|
def erase_to_rbs
|
|
61
52
|
base.erase_to_rbs
|
|
62
53
|
end
|
|
@@ -83,12 +74,9 @@ module Rigor
|
|
|
83
74
|
"#<Rigor::Type::Refined #{describe(:short)}>"
|
|
84
75
|
end
|
|
85
76
|
|
|
86
|
-
# Recognises a Ruby value against this carrier's
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
# when the predicate is unknown to the registry, so
|
|
90
|
-
# callers (today {Inference::Acceptance}) can fall
|
|
91
|
-
# through to gradual-mode `:maybe`.
|
|
77
|
+
# Recognises a Ruby value against this carrier's predicate. The trinary return is intentional:
|
|
78
|
+
# `true` / `false` when the predicate registry decides, `nil` when the predicate is unknown to the
|
|
79
|
+
# registry, so callers (today {Inference::Acceptance}) can fall through to gradual-mode `:maybe`.
|
|
92
80
|
# rubocop:disable Style/ReturnNilInPredicateMethodDefinition
|
|
93
81
|
def matches?(value)
|
|
94
82
|
recogniser = PREDICATES[predicate_id]
|
|
@@ -98,60 +86,42 @@ module Rigor
|
|
|
98
86
|
end
|
|
99
87
|
# rubocop:enable Style/ReturnNilInPredicateMethodDefinition
|
|
100
88
|
|
|
101
|
-
# `predicate_id => recogniser` table. The recogniser is
|
|
102
|
-
#
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
# (return false rather than raise) over arbitrary input,
|
|
106
|
-
# so callers can pass any `Constant#value` without a
|
|
107
|
-
# type-prefilter.
|
|
89
|
+
# `predicate_id => recogniser` table. The recogniser is called with a Ruby value (typically the
|
|
90
|
+
# inner `value` of a `Constant`) and returns truthy when the value satisfies the predicate. The
|
|
91
|
+
# recogniser MUST be total (return false rather than raise) over arbitrary input, so callers can
|
|
92
|
+
# pass any `Constant#value` without a type-prefilter.
|
|
108
93
|
#
|
|
109
|
-
# Plugin-contributed predicates are not yet wired; today
|
|
110
|
-
# the table covers the built-in catalogue.
|
|
94
|
+
# Plugin-contributed predicates are not yet wired; today the table covers the built-in catalogue.
|
|
111
95
|
#
|
|
112
96
|
# Recogniser policy:
|
|
113
97
|
#
|
|
114
|
-
# - `:numeric` recognises a string that is a *single Ruby
|
|
115
|
-
#
|
|
116
|
-
#
|
|
117
|
-
# `
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
# `
|
|
121
|
-
# `
|
|
122
|
-
# (`
|
|
123
|
-
#
|
|
124
|
-
#
|
|
125
|
-
#
|
|
126
|
-
#
|
|
127
|
-
#
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
#
|
|
131
|
-
#
|
|
132
|
-
# a numeric literal, so those are `CallNode`s too. The
|
|
133
|
-
# stricter base-N predicates below remain proper subsets.
|
|
134
|
-
# - `:decimal_int` is "what `Integer(s, 10)` would parse
|
|
135
|
-
# without remainder" — one or more decimal digits,
|
|
136
|
-
# optional leading sign, no whitespace, no fractional
|
|
137
|
-
# tail.
|
|
138
|
-
# - `:octal_int` and `:hex_int` REQUIRE their conventional
|
|
139
|
-
# prefix (`0o` / `0O` / leading `0` for octal; `0x` /
|
|
140
|
-
# `0X` for hex) so the predicate is disjoint from
|
|
141
|
-
# `:decimal_int`. A bare `"755"` is decimal-int-string,
|
|
142
|
-
# not octal-int-string. This matches the typical user
|
|
143
|
-
# intent — a refinement marks a string that "looks like
|
|
144
|
-
# octal", not "happens to be base-8 valid".
|
|
98
|
+
# - `:numeric` recognises a string that is a *single Ruby numeric literal* — exactly the syntax
|
|
99
|
+
# that, written in Ruby source, evaluates to an `Integer` / `Float` / `Rational` / `Complex`. The
|
|
100
|
+
# recogniser delegates to the real Ruby parser ({Refined.ruby_numeric_literal?} via Prism), so it
|
|
101
|
+
# tracks Ruby's grammar precisely: decimal / `0x` hex / `0o` (or leading-zero) octal / `0b`
|
|
102
|
+
# binary / `0d` decimal integers, underscore digit separators (`1_000`), decimal fractions and
|
|
103
|
+
# scientific floats (`1.5`, `1E-5`), and the `r` rational / `i` imaginary suffixes (`1r`, `2i`,
|
|
104
|
+
# `0xffr`). A single leading sign is folded into the literal (`-1`, `+1.5`), but a doubled sign
|
|
105
|
+
# (`--1`, `++1`) parses as a unary-operator chain — a `CallNode`, not a literal — and is
|
|
106
|
+
# rejected, as are multi-dot junk (`1.2.3`), partial literals (`0x`, `1_`), whitespace-padded
|
|
107
|
+
# strings, and — crucially — non-ASCII "digits" (full-width `1`, superscript `²`, other Unicode
|
|
108
|
+
# number characters): Ruby's lexer only accepts `[0-9]` in a numeric literal, so those are
|
|
109
|
+
# `CallNode`s too. The stricter base-N predicates below remain proper subsets.
|
|
110
|
+
# - `:decimal_int` is "what `Integer(s, 10)` would parse without remainder" — one or more decimal
|
|
111
|
+
# digits, optional leading sign, no whitespace, no fractional tail.
|
|
112
|
+
# - `:octal_int` and `:hex_int` REQUIRE their conventional prefix (`0o` / `0O` / leading `0` for
|
|
113
|
+
# octal; `0x` / `0X` for hex) so the predicate is disjoint from `:decimal_int`. A bare `"755"` is
|
|
114
|
+
# decimal-int-string, not octal-int-string. This matches the typical user intent — a refinement
|
|
115
|
+
# marks a string that "looks like octal", not "happens to be base-8 valid".
|
|
145
116
|
DECIMAL_INT_STRING_PATTERN = /\A-?\d+\z/
|
|
146
117
|
OCTAL_INT_STRING_PATTERN = /\A-?(?:0[oO][0-7]+|0[0-7]+)\z/
|
|
147
118
|
HEX_INT_STRING_PATTERN = /\A-?0[xX][0-9a-fA-F]+\z/
|
|
148
119
|
private_constant :DECIMAL_INT_STRING_PATTERN,
|
|
149
120
|
:OCTAL_INT_STRING_PATTERN, :HEX_INT_STRING_PATTERN
|
|
150
121
|
|
|
151
|
-
# Prism node classes that represent a numeric literal. A
|
|
152
|
-
#
|
|
153
|
-
# the
|
|
154
|
-
# is already folded into the literal by the parser).
|
|
122
|
+
# Prism node classes that represent a numeric literal. A string is a numeric-string exactly when
|
|
123
|
+
# the parser reduces the whole input to a single one of these (the leading sign is already folded
|
|
124
|
+
# into the literal by the parser).
|
|
155
125
|
NUMERIC_LITERAL_NODES = [
|
|
156
126
|
Prism::IntegerNode,
|
|
157
127
|
Prism::FloatNode,
|
|
@@ -160,11 +130,9 @@ module Rigor
|
|
|
160
130
|
].freeze
|
|
161
131
|
private_constant :NUMERIC_LITERAL_NODES
|
|
162
132
|
|
|
163
|
-
# Cheap pre-filter applied before invoking the parser: every
|
|
164
|
-
#
|
|
165
|
-
#
|
|
166
|
-
# reach Prism (the common non-numeric case stays allocation-
|
|
167
|
-
# and parse-free).
|
|
133
|
+
# Cheap pre-filter applied before invoking the parser: every Ruby numeric literal starts with an
|
|
134
|
+
# ASCII digit, optionally preceded by exactly one sign. Strings that fail this never reach Prism
|
|
135
|
+
# (the common non-numeric case stays allocation- and parse-free).
|
|
168
136
|
NUMERIC_LITERAL_PREFIX = /\A[+-]?\d/
|
|
169
137
|
private_constant :NUMERIC_LITERAL_PREFIX
|
|
170
138
|
|
|
@@ -176,10 +144,8 @@ module Rigor
|
|
|
176
144
|
def self.ruby_numeric_literal?(value)
|
|
177
145
|
return false unless value.is_a?(String)
|
|
178
146
|
return false if value.empty?
|
|
179
|
-
# A numeric literal carries no whitespace; reject any
|
|
180
|
-
#
|
|
181
|
-
# must be the literal (Prism would otherwise accept a
|
|
182
|
-
# trailing-space `"1 "`).
|
|
147
|
+
# A numeric literal carries no whitespace; reject any leading / trailing / interior space so the
|
|
148
|
+
# *whole* string must be the literal (Prism would otherwise accept a trailing-space `"1 "`).
|
|
183
149
|
return false if value.match?(/\s/)
|
|
184
150
|
return false unless value.match?(NUMERIC_LITERAL_PREFIX)
|
|
185
151
|
|
|
@@ -203,28 +169,21 @@ module Rigor
|
|
|
203
169
|
decimal_int: ->(v) { v.is_a?(String) && DECIMAL_INT_STRING_PATTERN.match?(v) },
|
|
204
170
|
octal_int: ->(v) { v.is_a?(String) && OCTAL_INT_STRING_PATTERN.match?(v) },
|
|
205
171
|
hex_int: ->(v) { v.is_a?(String) && HEX_INT_STRING_PATTERN.match?(v) },
|
|
206
|
-
# `literal-string` is a flow-tracked predicate, not a value-
|
|
207
|
-
#
|
|
208
|
-
#
|
|
209
|
-
#
|
|
210
|
-
#
|
|
211
|
-
#
|
|
212
|
-
# really tracked in the flow analysis (interpolation,
|
|
213
|
-
# concatenation, RBS::Extended `return: literal-string`)
|
|
214
|
-
# rather than recovered by inspecting an arbitrary string.
|
|
172
|
+
# `literal-string` is a flow-tracked predicate, not a value-level predicate: a String is
|
|
173
|
+
# literal-string when it is known to come from a source-code literal (or composition of
|
|
174
|
+
# literals). Every concrete `Constant<String>` is already literal by construction, so the
|
|
175
|
+
# inspection recogniser returns true for any String — the property is really tracked in the flow
|
|
176
|
+
# analysis (interpolation, concatenation, RBS::Extended `return: literal-string`) rather than
|
|
177
|
+
# recovered by inspecting an arbitrary string.
|
|
215
178
|
literal_string: ->(v) { v.is_a?(String) }
|
|
216
179
|
}.freeze
|
|
217
180
|
|
|
218
|
-
# Maps `[base_class_name, predicate_id]` pairs to their
|
|
219
|
-
#
|
|
220
|
-
# through `describe`; unregistered combinations fall back
|
|
221
|
-
# to the operator form.
|
|
181
|
+
# Maps `[base_class_name, predicate_id]` pairs to their kebab-case canonical name. Registered
|
|
182
|
+
# shapes print through `describe`; unregistered combinations fall back to the operator form.
|
|
222
183
|
#
|
|
223
|
-
# ADR-15 Phase 4b.x — `Ractor.make_shareable` (not `.freeze`)
|
|
224
|
-
#
|
|
225
|
-
#
|
|
226
|
-
# worker Ractor reading `CANONICAL_NAMES[[base, predicate]]`
|
|
227
|
-
# would trip `Ractor::IsolationError`.
|
|
184
|
+
# ADR-15 Phase 4b.x — `Ractor.make_shareable` (not `.freeze`) because the keys are nested
|
|
185
|
+
# two-element Arrays. Plain `.freeze` would leave the inner arrays mutable, so a worker Ractor
|
|
186
|
+
# reading `CANONICAL_NAMES[[base, predicate]]` would trip `Ractor::IsolationError`.
|
|
228
187
|
CANONICAL_NAMES = Ractor.make_shareable({
|
|
229
188
|
["String", :lowercase] => "lowercase-string",
|
|
230
189
|
["String", :not_lowercase] => "non-lowercase-string",
|
|
@@ -239,21 +198,15 @@ module Rigor
|
|
|
239
198
|
})
|
|
240
199
|
private_constant :CANONICAL_NAMES
|
|
241
200
|
|
|
242
|
-
# Bidirectional `predicate_id ↔ complement_predicate_id`
|
|
243
|
-
#
|
|
244
|
-
#
|
|
245
|
-
#
|
|
246
|
-
# under the `~T` operator. Predicates without a registered
|
|
247
|
-
# complement fall back to the imprecise but sound
|
|
248
|
-
# `Difference[part, refined]` carrier from the existing
|
|
249
|
-
# narrowing rule.
|
|
201
|
+
# Bidirectional `predicate_id ↔ complement_predicate_id` registry. `~Refined[base, p]` narrows to
|
|
202
|
+
# `Refined[base, COMPLEMENT_PAIRS[p]]` when the part is the refinement's base — the precise carrier
|
|
203
|
+
# the spec promises under the `~T` operator. Predicates without a registered complement fall back
|
|
204
|
+
# to the imprecise but sound `Difference[part, refined]` carrier from the existing narrowing rule.
|
|
250
205
|
#
|
|
251
|
-
# Adding a new pair here is an additive change: register the
|
|
252
|
-
#
|
|
253
|
-
#
|
|
254
|
-
#
|
|
255
|
-
# `complement_refined` consults this map and routes through
|
|
256
|
-
# the registered complement automatically.
|
|
206
|
+
# Adding a new pair here is an additive change: register the complement predicate in {PREDICATES},
|
|
207
|
+
# give it a kebab-case canonical name in {CANONICAL_NAMES}, and add the bidirectional entry below.
|
|
208
|
+
# No call site needs to know about the new pair — `complement_refined` consults this map and
|
|
209
|
+
# routes through the registered complement automatically.
|
|
257
210
|
COMPLEMENT_PAIRS = {
|
|
258
211
|
lowercase: :not_lowercase,
|
|
259
212
|
not_lowercase: :lowercase,
|
data/lib/rigor/type/singleton.rb
CHANGED
|
@@ -7,13 +7,11 @@ require_relative "plain_lattice"
|
|
|
7
7
|
|
|
8
8
|
module Rigor
|
|
9
9
|
module Type
|
|
10
|
-
# The singleton type for a Ruby class or module. Inhabitants are the
|
|
11
|
-
#
|
|
12
|
-
# In RBS this corresponds to `singleton(Foo)`.
|
|
10
|
+
# The singleton type for a Ruby class or module. Inhabitants are the class object itself (e.g. the
|
|
11
|
+
# constant `Foo`), not its instances. In RBS this corresponds to `singleton(Foo)`.
|
|
13
12
|
#
|
|
14
|
-
# `Singleton[Foo]` and `Nominal[Foo]` share the same `class_name` but
|
|
15
|
-
#
|
|
16
|
-
# instances of the class).
|
|
13
|
+
# `Singleton[Foo]` and `Nominal[Foo]` share the same `class_name` but are NEVER equal; they describe
|
|
14
|
+
# disjoint values (the class object vs. instances of the class).
|
|
17
15
|
#
|
|
18
16
|
# See docs/type-specification/rbs-compatible-types.md (singleton(T)).
|
|
19
17
|
class Singleton
|
|
@@ -7,25 +7,20 @@ require_relative "plain_lattice"
|
|
|
7
7
|
|
|
8
8
|
module Rigor
|
|
9
9
|
module Type
|
|
10
|
-
# The class object produced by `Struct.new(:x, :y)` (ADR-48 Struct
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# the ordered member-name list so `Point.new(...)` can materialise a
|
|
15
|
-
# {StructInstance}.
|
|
10
|
+
# The class object produced by `Struct.new(:x, :y)` (ADR-48 Struct follow-up). The mutable sibling of
|
|
11
|
+
# {DataClass}: it models the *class* (the value bound to `Point` in `Point = Struct.new(:x, :y)`, or
|
|
12
|
+
# the anonymous superclass in `class Point < Struct.new(:x, :y)`), carrying the ordered member-name
|
|
13
|
+
# list so `Point.new(...)` can materialise a {StructInstance}.
|
|
16
14
|
#
|
|
17
|
-
# `keyword_init` records the `Struct.new(..., keyword_init: true)` flag
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
# shape and must degrade rather than fold a wrong member map.
|
|
15
|
+
# `keyword_init` records the `Struct.new(..., keyword_init: true)` flag so `.new` only materialises a
|
|
16
|
+
# precise instance for the matching call form — a positional `.new(1, 2)` on a `keyword_init: true`
|
|
17
|
+
# struct, or a keyword `.new(x: 1)` on a positional struct, is a different runtime shape and must
|
|
18
|
+
# degrade rather than fold a wrong member map.
|
|
22
19
|
#
|
|
23
|
-
# `class_name` carries the binding name when known (the named-subclass
|
|
24
|
-
#
|
|
25
|
-
# before it is assigned to a constant.
|
|
20
|
+
# `class_name` carries the binding name when known (the named-subclass form) and is `nil` for the
|
|
21
|
+
# anonymous result of a bare `Struct.new(...)` before it is assigned to a constant.
|
|
26
22
|
#
|
|
27
|
-
# Equality and hashing are structural over the member list, the class
|
|
28
|
-
# name, and the keyword-init flag.
|
|
23
|
+
# Equality and hashing are structural over the member list, the class name, and the keyword-init flag.
|
|
29
24
|
#
|
|
30
25
|
# See docs/adr/48-data-struct-value-folding.md § "Struct follow-up".
|
|
31
26
|
class StructClass
|
|
@@ -7,28 +7,23 @@ require_relative "plain_lattice"
|
|
|
7
7
|
|
|
8
8
|
module Rigor
|
|
9
9
|
module Type
|
|
10
|
-
# A `Struct.new` value instance (ADR-48 Struct follow-up) —
|
|
11
|
-
#
|
|
12
|
-
# total, class-tagged member map (member name -> value type),
|
|
10
|
+
# A `Struct.new` value instance (ADR-48 Struct follow-up) — `Point.new(1, 2)`. The mutable sibling of
|
|
11
|
+
# {DataInstance}: a closed, total, class-tagged member map (member name -> value type),
|
|
13
12
|
# HashShape-shaped but nominal.
|
|
14
13
|
#
|
|
15
|
-
# Unlike {DataInstance}, a `Struct` instance is **mutable** — `s.x = v`,
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
# rather than fold a possibly-stale member value. Promoting the
|
|
22
|
-
# fold to mutation-free bound locals is the deferred slice 3 (see ADR).
|
|
14
|
+
# Unlike {DataInstance}, a `Struct` instance is **mutable** — `s.x = v`, `s[:x] = v`, and escape can
|
|
15
|
+
# invalidate the member map. The folding tier therefore only projects member reads off a **fresh**
|
|
16
|
+
# instance (the transient receiver of a `.new(...).x` / `.with(...).x` chain, which provably cannot
|
|
17
|
+
# have been mutated between materialisation and the read); a read off a *stored* binding degrades to
|
|
18
|
+
# `Dynamic[top]` rather than fold a possibly-stale member value. Promoting the fold to mutation-free
|
|
19
|
+
# bound locals is the deferred slice 3 (see ADR).
|
|
23
20
|
#
|
|
24
|
-
# That mutability-gating lives in the dispatch tier (`StructFolding`),
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
# non-member calls resolve without mis-firing undefined-method.
|
|
21
|
+
# That mutability-gating lives in the dispatch tier (`StructFolding`), not the carrier: the carrier
|
|
22
|
+
# itself just records the member map. Like {DataInstance}, non-folded methods project to the
|
|
23
|
+
# `Struct` nominal (or the tagged class) through {RbsDispatch}'s `receiver_descriptor`, so non-member
|
|
24
|
+
# calls resolve without mis-firing undefined-method.
|
|
29
25
|
#
|
|
30
|
-
# Equality and hashing are structural over the (member -> type) map and
|
|
31
|
-
# the class name.
|
|
26
|
+
# Equality and hashing are structural over the (member -> type) map and the class name.
|
|
32
27
|
#
|
|
33
28
|
# See docs/adr/48-data-struct-value-folding.md § "Struct follow-up".
|
|
34
29
|
class StructInstance
|
|
@@ -67,9 +62,8 @@ module Rigor
|
|
|
67
62
|
"#{class_name || 'Struct'}(#{rendered.join(', ')})"
|
|
68
63
|
end
|
|
69
64
|
|
|
70
|
-
# Erases to the tagging class nominal (conservative: the structural
|
|
71
|
-
#
|
|
72
|
-
# anonymous case erases to the `Struct` supertype.
|
|
65
|
+
# Erases to the tagging class nominal (conservative: the structural members are not RBS-expressible
|
|
66
|
+
# as a class instance). The anonymous case erases to the `Struct` supertype.
|
|
73
67
|
def erase_to_rbs
|
|
74
68
|
name = class_name
|
|
75
69
|
return "Struct" if name.nil?
|
data/lib/rigor/type/tuple.rb
CHANGED
|
@@ -7,30 +7,25 @@ require_relative "plain_lattice"
|
|
|
7
7
|
|
|
8
8
|
module Rigor
|
|
9
9
|
module Type
|
|
10
|
-
# A heterogeneous, fixed-arity array shape. Inhabitants are exactly
|
|
11
|
-
#
|
|
12
|
-
# and whose element at position `i` inhabits `elements[i]`.
|
|
10
|
+
# A heterogeneous, fixed-arity array shape. Inhabitants are exactly the Ruby `Array` instances whose
|
|
11
|
+
# length matches `elements.size` and whose element at position `i` inhabits `elements[i]`.
|
|
13
12
|
#
|
|
14
|
-
# In RBS this corresponds to the tuple form `[A, B, C]`. A tuple
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# while acceptance keeps the per-position precision.
|
|
13
|
+
# In RBS this corresponds to the tuple form `[A, B, C]`. A tuple is always a subtype of
|
|
14
|
+
# `Array[union(elements)]`; method dispatch therefore degrades to the underlying `Nominal[Array,
|
|
15
|
+
# [union]]` while acceptance keeps the per-position precision.
|
|
18
16
|
#
|
|
19
|
-
# Slice 5 phase 1 introduces the carrier and surfaces it from the
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
# above {Rigor::Inference::MethodDispatcher::RbsDispatch}.
|
|
17
|
+
# Slice 5 phase 1 introduces the carrier and surfaces it from the `ArrayNode` literal handler when
|
|
18
|
+
# every element is a non-splat value. Tuple-aware refinements (`tuple[0]`, `tuple.first`,
|
|
19
|
+
# destructuring) are implemented in `ShapeDispatch`, which runs above
|
|
20
|
+
# {Rigor::Inference::MethodDispatcher::RbsDispatch}.
|
|
24
21
|
#
|
|
25
|
-
# Equality and hashing are structural across an ordered, frozen
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
# `Tuple[]` directly when the zero-arity discipline is intended.
|
|
22
|
+
# Equality and hashing are structural across an ordered, frozen element list. The empty Tuple
|
|
23
|
+
# `Tuple[]` is permitted; the array literal handler keeps `[]` as raw `Nominal[Array]` (no element
|
|
24
|
+
# evidence to lock the arity), but external constructors MAY build `Tuple[]` directly when the
|
|
25
|
+
# zero-arity discipline is intended.
|
|
30
26
|
#
|
|
31
27
|
# See docs/type-specification/rbs-compatible-types.md (tuple) and
|
|
32
|
-
# docs/type-specification/rigor-extensions.md (hash-shape and
|
|
33
|
-
# tuple kin).
|
|
28
|
+
# docs/type-specification/rigor-extensions.md (hash-shape and tuple kin).
|
|
34
29
|
class Tuple
|
|
35
30
|
attr_reader :elements
|
|
36
31
|
|
data/lib/rigor/type/union.rb
CHANGED
|
@@ -6,12 +6,11 @@ require_relative "acceptance_router"
|
|
|
6
6
|
|
|
7
7
|
module Rigor
|
|
8
8
|
module Type
|
|
9
|
-
# A normalized non-empty union of two or more distinct types. Unions are
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# an already-normalized members array.
|
|
9
|
+
# A normalized non-empty union of two or more distinct types. Unions are constructed exclusively
|
|
10
|
+
# through Rigor::Type::Combinator.union, which flattens nested unions, deduplicates
|
|
11
|
+
# structurally-equal members, and collapses single-member or empty results to the appropriate scalar
|
|
12
|
+
# type. Direct calls to .new are an internal contract: callers MUST pass an already-normalized
|
|
13
|
+
# members array.
|
|
15
14
|
#
|
|
16
15
|
# See docs/type-specification/normalization.md.
|
|
17
16
|
class Union
|
|
@@ -26,22 +25,19 @@ module Rigor
|
|
|
26
25
|
freeze
|
|
27
26
|
end
|
|
28
27
|
|
|
29
|
-
# Display-only adoption of two concise RBS spellings for the
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
# — only the human-facing rendering reads like the RBS the user wrote.
|
|
28
|
+
# Display-only adoption of two concise RBS spellings for the union (see
|
|
29
|
+
# docs/type-specification/normalization.md § "Interaction with display" and rbs-compatible-types.md
|
|
30
|
+
# § "Optionals"). Both are purely cosmetic: `@members` keeps every carrier verbatim, so the
|
|
31
|
+
# underlying type identity, RBS erasure, and round-trip are unchanged — only the human-facing
|
|
32
|
+
# rendering reads like the RBS the user wrote.
|
|
35
33
|
#
|
|
36
|
-
# * `true | false` → `bool` (the RBS boolean alias). The
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
# * `T | nil` → `T?` (the RBS optional sugar). Only
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
# `Integer | String
|
|
43
|
-
# parenthesised `(Integer | String)?`. The two collapses compose,
|
|
44
|
-
# so `false | true | nil` reads as `bool?`.
|
|
34
|
+
# * `true | false` → `bool` (the RBS boolean alias). The `bool` token leads the
|
|
35
|
+
# rendering, so `false | Foo | true` reads as `bool | Foo` rather than burying the pair
|
|
36
|
+
# mid-list.
|
|
37
|
+
# * `T | nil` → `T?` (the RBS optional sugar). Only applied when exactly one
|
|
38
|
+
# *logical* member remains beside `nil`, matching the rbs gem's own `to_s`: a multi-member
|
|
39
|
+
# union such as `Integer | String | nil` stays explicit rather than gaining a parenthesised
|
|
40
|
+
# `(Integer | String)?`. The two collapses compose, so `false | true | nil` reads as `bool?`.
|
|
45
41
|
def describe(verbosity = :short)
|
|
46
42
|
return "#{optional_inner(verbosity)}?" if optional?
|
|
47
43
|
|
|
@@ -53,22 +49,16 @@ module Rigor
|
|
|
53
49
|
end
|
|
54
50
|
end
|
|
55
51
|
|
|
56
|
-
# ADR-1 § "RBS round-trip is lossless" + the value-lattice
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
# the RBS surface has no carrier for "Dynamic-origin
|
|
61
|
-
# alongside a static facet", and the gradual-consistency
|
|
62
|
-
# contract guarantees the substitution is sound at every
|
|
52
|
+
# ADR-1 § "RBS round-trip is lossless" + the value-lattice rule `untyped | T = untyped` (every `T`
|
|
53
|
+
# is gradually consistent with `untyped`). When any union member erases to `"untyped"`, the whole
|
|
54
|
+
# union erases to `"untyped"` — the RBS surface has no carrier for "Dynamic-origin alongside a
|
|
55
|
+
# static facet", and the gradual-consistency contract guarantees the substitution is sound at every
|
|
63
56
|
# call site.
|
|
64
57
|
#
|
|
65
|
-
# Post-erasure dedupe removes `String | String` artefacts
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
# are already structurally deduped at construction by
|
|
70
|
-
# `Type::Combinator.union`, but the post-erase strings
|
|
71
|
-
# can collide.
|
|
58
|
+
# Post-erasure dedupe removes `String | String` artefacts that arise when two structurally-distinct
|
|
59
|
+
# `Constant` carriers (e.g. `Constant<"Alice">` / `Constant<"Bob">`) share an RBS-erased envelope.
|
|
60
|
+
# The members themselves are already structurally deduped at construction by
|
|
61
|
+
# `Type::Combinator.union`, but the post-erase strings can collide.
|
|
72
62
|
def erase_to_rbs
|
|
73
63
|
erased = members.map(&:erase_to_rbs)
|
|
74
64
|
return "untyped" if erased.include?("untyped")
|
|
@@ -100,9 +90,8 @@ module Rigor
|
|
|
100
90
|
|
|
101
91
|
private
|
|
102
92
|
|
|
103
|
-
# Both `true` and `false` literals are present, so the pair can
|
|
104
|
-
#
|
|
105
|
-
# plain literal (`true` / `false`) — that asymmetry is meaningful.
|
|
93
|
+
# Both `true` and `false` literals are present, so the pair can render as `bool`. A union carrying
|
|
94
|
+
# only one of them stays a plain literal (`true` / `false`) — that asymmetry is meaningful.
|
|
106
95
|
def boolean_pair?
|
|
107
96
|
members.any? { |m| boolean_literal?(m, true) } &&
|
|
108
97
|
members.any? { |m| boolean_literal?(m, false) }
|
|
@@ -121,10 +110,9 @@ module Rigor
|
|
|
121
110
|
member.is_a?(Constant) && member.value.nil?
|
|
122
111
|
end
|
|
123
112
|
|
|
124
|
-
# `nil` is present and, once the `bool` pair is treated as a
|
|
125
|
-
#
|
|
126
|
-
#
|
|
127
|
-
# one is what lets `false | true | nil` reach `bool?`.
|
|
113
|
+
# `nil` is present and, once the `bool` pair is treated as a single logical member, exactly one
|
|
114
|
+
# non-`nil` member remains — so the whole union renders as `T?`. Counting the bool pair as one is
|
|
115
|
+
# what lets `false | true | nil` reach `bool?`.
|
|
128
116
|
def optional?
|
|
129
117
|
return false unless members.any? { |m| nil_literal?(m) }
|
|
130
118
|
|
|
@@ -9,20 +9,13 @@ module Rigor
|
|
|
9
9
|
# sequence of type-argument nodes already produced by the
|
|
10
10
|
# parser at one level of depth.
|
|
11
11
|
#
|
|
12
|
-
# Args are themselves {TypeNode::Identifier} or
|
|
13
|
-
#
|
|
14
|
-
# `Pick
|
|
15
|
-
# `Generic("Pick", [Identifier("Address"), Generic("Union", [...])])`
|
|
16
|
-
# — actually the union spelling depends on the parser's
|
|
17
|
-
# eventual convention (slice 3 pins it); for now the field
|
|
18
|
-
# set is the only public commitment.
|
|
12
|
+
# Args are themselves {TypeNode::Identifier} or {TypeNode::Generic}. Nested generics ride the same
|
|
13
|
+
# shape: `Pick<Address, "name" | "surname">` reaches the resolver as
|
|
14
|
+
# `Generic("Pick", [Identifier("Address"), Union([...])])`.
|
|
19
15
|
#
|
|
20
|
-
# The carrier is intentionally permissive about `args.size`.
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
# zero-arg Generic so plugins can synthesise nodes for
|
|
24
|
-
# diagnostic or testing purposes without the parser fighting
|
|
25
|
-
# back.
|
|
16
|
+
# The carrier is intentionally permissive about `args.size`. The grammar-level rule "no brackets ⇒
|
|
17
|
+
# Identifier; brackets ⇒ Generic" lives on the parser side; nothing here forbids a zero-arg Generic
|
|
18
|
+
# so plugins can synthesise nodes for diagnostic or testing purposes without the parser fighting back.
|
|
26
19
|
class Generic < Data.define(:head, :args)
|
|
27
20
|
def initialize(head:, args:)
|
|
28
21
|
unless head.is_a?(String) && !head.empty?
|
|
@@ -38,10 +31,9 @@ module Rigor
|
|
|
38
31
|
"TypeNode::IntegerLiteral, got #{args.inspect}"
|
|
39
32
|
end
|
|
40
33
|
|
|
41
|
-
# Freeze the String head + Array args so the Data
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
# freezing the wrapping Array is sufficient.
|
|
34
|
+
# Freeze the String head + Array args so the Data object is `Ractor.shareable?`. Each `a` is
|
|
35
|
+
# already a shareable TypeNode value object (checked above), so freezing the wrapping Array
|
|
36
|
+
# is sufficient.
|
|
45
37
|
frozen_head = head.frozen? ? head : head.dup.freeze
|
|
46
38
|
frozen_args = args.frozen? ? args : args.dup.freeze
|
|
47
39
|
super(head: frozen_head, args: frozen_args)
|
|
@@ -49,15 +41,11 @@ module Rigor
|
|
|
49
41
|
|
|
50
42
|
private
|
|
51
43
|
|
|
52
|
-
# ADR-13 slice 3 expanded the accepted set to include
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
# / {Union} so `Pick[T, :a | "b"]` carries through to the
|
|
58
|
-
# resolver as a uniform AST. Slice 1 originally accepted
|
|
59
|
-
# only `Identifier` / `Generic`; every later addition stays
|
|
60
|
-
# additive — every slice-1-shape Generic remains valid.
|
|
44
|
+
# ADR-13 slice 3 expanded the accepted set to include {IntegerLiteral} so the parser can emit a
|
|
45
|
+
# uniform AST for `int<5, 10>` (angle bounds) and `int_mask[1, 2, 4]` (square-bracketed bitflag
|
|
46
|
+
# union). The follow-up further admits {SymbolLiteral} / {StringLiteral} / {IndexedAccess} /
|
|
47
|
+
# {Union} so `Pick[T, :a | "b"]` carries through to the resolver as a uniform AST. Every addition
|
|
48
|
+
# stays additive — every earlier-shape Generic remains valid.
|
|
61
49
|
def valid_arg?(arg)
|
|
62
50
|
arg.is_a?(Identifier) || arg.is_a?(Generic) || arg.is_a?(IntegerLiteral) ||
|
|
63
51
|
arg.is_a?(SymbolLiteral) || arg.is_a?(StringLiteral) ||
|