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
|
@@ -4,17 +4,15 @@ require_relative "../type"
|
|
|
4
4
|
|
|
5
5
|
module Rigor
|
|
6
6
|
module Inference
|
|
7
|
-
# Widens a local- or instance-variable binding after a call
|
|
8
|
-
#
|
|
9
|
-
# in-place mutator.
|
|
7
|
+
# Widens a local- or instance-variable binding after a call whose receiver is that variable
|
|
8
|
+
# AND whose method is a known in-place mutator.
|
|
10
9
|
#
|
|
11
10
|
# Closes the **G1 / G2** flow-folding gaps documented at
|
|
12
|
-
# `docs/notes/20260521-mastodon-cluster4-flow-folding-triage.md`
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# body or sibling method that mutates `arr` / `@arr` in place.
|
|
11
|
+
# `docs/notes/20260521-mastodon-cluster4-flow-folding-triage.md` and queued in
|
|
12
|
+
# [`docs/CURRENT_WORK.md`](../../../docs/CURRENT_WORK.md) § "Flow-folding". The user-visible
|
|
13
|
+
# symptom they shared was a spurious `flow.always-truthy-condition` on a `arr.size == N` /
|
|
14
|
+
# `arr.empty?` / `@arr.empty?` check that follows a loop body or sibling method that mutates
|
|
15
|
+
# `arr` / `@arr` in place.
|
|
18
16
|
#
|
|
19
17
|
# **The mechanism.** When source like
|
|
20
18
|
#
|
|
@@ -24,30 +22,23 @@ module Rigor
|
|
|
24
22
|
# end
|
|
25
23
|
# return arms.first if arms.size == 1
|
|
26
24
|
#
|
|
27
|
-
# runs through inference today, the literal `[first]` writes
|
|
28
|
-
# `
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
# `arms.size == 1` constant-folds to `true` and the user sees
|
|
33
|
-
# a false `flow.always-truthy-condition`.
|
|
25
|
+
# runs through inference today, the literal `[first]` writes `arms` as `Tuple[T]`. The shape
|
|
26
|
+
# carrier's `size` folds to `Constant[1]`. The body's `arms << next_arm` returns a type for
|
|
27
|
+
# the call expression but does NOT rebind `arms`, so after the loop `arms` still carries the
|
|
28
|
+
# `Tuple[T]` binding — `arms.size == 1` constant-folds to `true` and the user sees a false
|
|
29
|
+
# `flow.always-truthy-condition`.
|
|
34
30
|
#
|
|
35
|
-
# The narrowest correct fix is to **widen the receiver binding
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
# `
|
|
41
|
-
# name); the resulting union loses size precision, so the
|
|
42
|
-
# `arms.size` fold returns `Integer` (not `Constant[1]`) and
|
|
43
|
-
# the diagnostic correctly stays silent.
|
|
31
|
+
# The narrowest correct fix is to **widen the receiver binding at the mutator call site**:
|
|
32
|
+
# replace `arms`'s binding with `Nominal[Array, [union(elements)]]` so the carrier no longer
|
|
33
|
+
# carries the literal arity. Inside a loop body, the post-call body scope then joins with the
|
|
34
|
+
# pre-loop scope through `join_with_nil_injection` → `Scope#join` (which unions per name); the
|
|
35
|
+
# resulting union loses size precision, so the `arms.size` fold returns `Integer` (not
|
|
36
|
+
# `Constant[1]`) and the diagnostic correctly stays silent.
|
|
44
37
|
#
|
|
45
|
-
# The widening is **always type-safe**: it never introduces a
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
# nominal stays exact (`Array` / `Hash`) and element types
|
|
50
|
-
# stay as a union of what was there.
|
|
38
|
+
# The widening is **always type-safe**: it never introduces a new fact, only forgets a
|
|
39
|
+
# literal-shape fact that is no longer justified once mutation occurred. It costs only the
|
|
40
|
+
# precise arity / pair-set the shape carrier was tracking; the underlying nominal stays exact
|
|
41
|
+
# (`Array` / `Hash`) and element types stay as a union of what was there.
|
|
51
42
|
#
|
|
52
43
|
# **Scope.** This slice addresses:
|
|
53
44
|
#
|
|
@@ -56,32 +47,25 @@ module Rigor
|
|
|
56
47
|
#
|
|
57
48
|
# Out of scope (left for a separate cycle):
|
|
58
49
|
#
|
|
59
|
-
# - **`retry` flow edge** (e.g. `tries += 1; retry`). The
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
# (e.g. `if @performed; perform!; if @performed`). The
|
|
64
|
-
# intra-procedural call effect on ivars is a separate
|
|
50
|
+
# - **`retry` flow edge** (e.g. `tries += 1; retry`). The `tries` rebind across `retry` is a
|
|
51
|
+
# flow-edge issue, not a call-site mutation issue.
|
|
52
|
+
# - **Intervening method call invalidates the ivar binding** (e.g. `if @performed;
|
|
53
|
+
# perform!; if @performed`). The intra-procedural call effect on ivars is a separate
|
|
65
54
|
# mutation-effect feature.
|
|
66
|
-
# - **Read-before-write nil** (e.g. `unless @warning_issued;
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
# - **
|
|
70
|
-
#
|
|
71
|
-
# ADR-56 slice A (`widen_after_block`). **Ivar mutations
|
|
72
|
-
# inside a block ARE also handled** (ivars live in the
|
|
73
|
-
# method-body scope, not the block-local scope).
|
|
55
|
+
# - **Read-before-write nil** (e.g. `unless @warning_issued; ...; @warning_issued = true`).
|
|
56
|
+
# Requires tracking the first-write position; flow-sensitive but orthogonal.
|
|
57
|
+
# - **Local-variable mutation inside a block body** (e.g. `arr = []; xs.each { |x| arr <<
|
|
58
|
+
# x }`) — landed as ADR-56 slice A (`widen_after_block`). **Ivar mutations inside a block
|
|
59
|
+
# ARE also handled** (ivars live in the method-body scope, not the block-local scope).
|
|
74
60
|
#
|
|
75
61
|
# The remaining three items above are demand-gated; see ADR-56.
|
|
76
62
|
module MutationWidening
|
|
77
|
-
# Array mutators that change either the size or the element
|
|
78
|
-
#
|
|
79
|
-
# methods only — non-mutating siblings (`map` vs `map!`,
|
|
63
|
+
# Array mutators that change either the size or the element set of a literal-shape carrier
|
|
64
|
+
# (Tuple). Receiver-mutating methods only — non-mutating siblings (`map` vs `map!`,
|
|
80
65
|
# `select` vs `select!`) stay precise.
|
|
81
66
|
#
|
|
82
|
-
# `<<` and `[]=` are the dominant survey cases; the bang
|
|
83
|
-
#
|
|
84
|
-
# Mastodon cluster-4 G1 catalogue.
|
|
67
|
+
# `<<` and `[]=` are the dominant survey cases; the bang variants and the size-mutators
|
|
68
|
+
# cover the rest of the Mastodon cluster-4 G1 catalogue.
|
|
85
69
|
ARRAY_MUTATORS = %i[
|
|
86
70
|
<< push append prepend unshift concat insert
|
|
87
71
|
pop shift
|
|
@@ -92,9 +76,8 @@ module Rigor
|
|
|
92
76
|
flatten! sort! sort_by! reverse! rotate! shuffle! slice!
|
|
93
77
|
].to_set.freeze
|
|
94
78
|
|
|
95
|
-
# Hash mutators that invalidate a `HashShape` carrier. Same
|
|
96
|
-
#
|
|
97
|
-
# methods are listed.
|
|
79
|
+
# Hash mutators that invalidate a `HashShape` carrier. Same principle as `ARRAY_MUTATORS`:
|
|
80
|
+
# only the receiver-mutating methods are listed.
|
|
98
81
|
HASH_MUTATORS = %i[
|
|
99
82
|
[]= store
|
|
100
83
|
delete delete_if reject! select! filter! keep_if
|
|
@@ -102,12 +85,10 @@ module Rigor
|
|
|
102
85
|
replace
|
|
103
86
|
].to_set.freeze
|
|
104
87
|
|
|
105
|
-
# Methods that return the receiver (or a shallow copy) and
|
|
106
|
-
#
|
|
107
|
-
#
|
|
108
|
-
#
|
|
109
|
-
# whose return value is the receiver itself (or a copy that
|
|
110
|
-
# leaves the original untouched).
|
|
88
|
+
# Methods that return the receiver (or a shallow copy) and cannot mutate it. They must not
|
|
89
|
+
# trigger widening or any other receiver-fact invalidation. The list is intentionally
|
|
90
|
+
# narrow — only methods whose purity is unconditional and whose return value is the
|
|
91
|
+
# receiver itself (or a copy that leaves the original untouched).
|
|
111
92
|
PURE_SELF_RETURNERS = %i[freeze dup clone itself].freeze
|
|
112
93
|
|
|
113
94
|
module_function
|
|
@@ -118,11 +99,10 @@ module Rigor
|
|
|
118
99
|
PURE_SELF_RETURNERS.include?(method_name)
|
|
119
100
|
end
|
|
120
101
|
|
|
121
|
-
# Returns a scope with the call's receiver widened, when the
|
|
122
|
-
#
|
|
123
|
-
#
|
|
124
|
-
#
|
|
125
|
-
# shape. Returns `current_scope` unchanged otherwise.
|
|
102
|
+
# Returns a scope with the call's receiver widened, when the receiver is a
|
|
103
|
+
# local-/instance-variable read whose current binding is a literal-shape carrier
|
|
104
|
+
# (`Tuple` / `HashShape`) AND the call name is a known in-place mutator for that shape.
|
|
105
|
+
# Returns `current_scope` unchanged otherwise.
|
|
126
106
|
#
|
|
127
107
|
# @param call_node [Prism::CallNode]
|
|
128
108
|
# @param current_scope [Rigor::Scope]
|
|
@@ -143,29 +123,22 @@ module Rigor
|
|
|
143
123
|
end
|
|
144
124
|
end
|
|
145
125
|
|
|
146
|
-
# Propagate block-body mutations of outer-scope variables
|
|
147
|
-
#
|
|
148
|
-
#
|
|
149
|
-
#
|
|
150
|
-
# outer scope (ivars are handled correctly already because
|
|
151
|
-
# they live in the method-body scope, not the block-local
|
|
152
|
-
# scope).
|
|
126
|
+
# Propagate block-body mutations of outer-scope variables back into `outer_scope`. Block
|
|
127
|
+
# bodies live in a child scope; mutations the block body performs on captured outer
|
|
128
|
+
# LOCALS are otherwise invisible to the post-call outer scope (ivars are handled correctly
|
|
129
|
+
# already because they live in the method-body scope, not the block-local scope).
|
|
153
130
|
#
|
|
154
|
-
# Walks the block AST for `<receiver>.<method>(...)` calls
|
|
155
|
-
#
|
|
156
|
-
# `depth
|
|
157
|
-
#
|
|
158
|
-
#
|
|
159
|
-
#
|
|
160
|
-
#
|
|
161
|
-
# — it can only LOSE precision — so blindly propagating is
|
|
162
|
-
# sound regardless of whether the block actually runs.
|
|
131
|
+
# Walks the block AST for `<receiver>.<method>(...)` calls whose receiver is either a
|
|
132
|
+
# `LocalVariableReadNode` with `depth > 0` (a captured outer local — Prism's `depth`
|
|
133
|
+
# counts scope hops outward; `depth == 0` means a block-local) or an
|
|
134
|
+
# `InstanceVariableReadNode` (always method-scope), and applies `widen_after_call` for
|
|
135
|
+
# each one against the outer scope. The widening is always safe — it can only LOSE
|
|
136
|
+
# precision — so blindly propagating is sound regardless of whether the block actually
|
|
137
|
+
# runs.
|
|
163
138
|
#
|
|
164
|
-
# Recurses into nested expression nodes so chained / nested
|
|
165
|
-
#
|
|
166
|
-
#
|
|
167
|
-
# `Prism::BlockNode`s — each block is processed by its own
|
|
168
|
-
# `eval_call`.
|
|
139
|
+
# Recurses into nested expression nodes so chained / nested forms (`arr << f(x); arr <<
|
|
140
|
+
# g(y)`, `arr.push(x) if cond`) are all caught. Does NOT recurse into nested
|
|
141
|
+
# `Prism::BlockNode`s — each block is processed by its own `eval_call`.
|
|
169
142
|
def widen_after_block(call_node:, outer_scope:)
|
|
170
143
|
block = call_node.block
|
|
171
144
|
return outer_scope unless block.is_a?(Prism::BlockNode)
|
|
@@ -181,15 +154,12 @@ module Rigor
|
|
|
181
154
|
|
|
182
155
|
scope = widen_for_outer_receiver(node, scope) if node.is_a?(Prism::CallNode)
|
|
183
156
|
|
|
184
|
-
# Descend into every child, including nested blocks. The
|
|
185
|
-
# `
|
|
186
|
-
# `
|
|
187
|
-
#
|
|
188
|
-
#
|
|
189
|
-
#
|
|
190
|
-
# blocks is safe and necessary for the hkt_registry-shape
|
|
191
|
-
# case (an outer collection mutated inside an iterator
|
|
192
|
-
# block whose body is itself inside another block).
|
|
157
|
+
# Descend into every child, including nested blocks. The `LocalVariableReadNode#depth`
|
|
158
|
+
# check inside `widen_for_outer_receiver` keeps nested-block-locals from being widened
|
|
159
|
+
# in the outer scope — only references with `depth >= 1` (true captures of the outer
|
|
160
|
+
# scope's locals) trigger widening, so descending into nested blocks is safe and
|
|
161
|
+
# necessary for the hkt_registry-shape case (an outer collection mutated inside an
|
|
162
|
+
# iterator block whose body is itself inside another block).
|
|
193
163
|
node.compact_child_nodes.each do |child|
|
|
194
164
|
scope = walk_for_outer_mutations(child, scope)
|
|
195
165
|
end
|
|
@@ -230,11 +200,10 @@ module Rigor
|
|
|
230
200
|
current_scope.with_ivar(var_name, widened)
|
|
231
201
|
end
|
|
232
202
|
|
|
233
|
-
# Returns the widened type for a binding whose receiver is
|
|
234
|
-
#
|
|
235
|
-
#
|
|
236
|
-
#
|
|
237
|
-
# binding is already a nominal — no precision to lose).
|
|
203
|
+
# Returns the widened type for a binding whose receiver is about to be mutated by
|
|
204
|
+
# `method_name`, or `nil` when no widening applies (binding is not a literal-shape
|
|
205
|
+
# carrier, OR the method is not a mutator for that shape, OR the binding is already a
|
|
206
|
+
# nominal — no precision to lose).
|
|
238
207
|
def widen_for_mutator(type, method_name)
|
|
239
208
|
return nil if type.nil?
|
|
240
209
|
|
|
@@ -250,9 +219,8 @@ module Rigor
|
|
|
250
219
|
end
|
|
251
220
|
end
|
|
252
221
|
|
|
253
|
-
# `Tuple[A, B, C]` → `Nominal[Array, [union(A, B, C)]]`.
|
|
254
|
-
#
|
|
255
|
-
# form carries `untyped` element bound — matches the
|
|
222
|
+
# `Tuple[A, B, C]` → `Nominal[Array, [union(A, B, C)]]`. An empty tuple has no element
|
|
223
|
+
# evidence, so the widened form carries `untyped` element bound — matches the
|
|
256
224
|
# `tuple_to_array` widening already used by `BlockFolding`.
|
|
257
225
|
def widen_tuple(tuple)
|
|
258
226
|
element_type =
|
|
@@ -266,9 +234,8 @@ module Rigor
|
|
|
266
234
|
Type::Combinator.nominal_of("Array", type_args: [element_type])
|
|
267
235
|
end
|
|
268
236
|
|
|
269
|
-
# `HashShape` (closed or open) → `Nominal[Hash, [Kunion,
|
|
270
|
-
#
|
|
271
|
-
# fully-untyped Hash.
|
|
237
|
+
# `HashShape` (closed or open) → `Nominal[Hash, [Kunion, Vunion]]`. Empty / extra-keys-only
|
|
238
|
+
# shapes degrade to a fully-untyped Hash.
|
|
272
239
|
def widen_hash_shape(shape)
|
|
273
240
|
if shape.pairs.empty?
|
|
274
241
|
return Type::Combinator.nominal_of("Hash",
|
|
@@ -281,12 +248,10 @@ module Rigor
|
|
|
281
248
|
Type::Combinator.nominal_of("Hash", type_args: [key_type, value_type])
|
|
282
249
|
end
|
|
283
250
|
|
|
284
|
-
# Maps the literal Ruby key set (`Symbol` / `String`) to a
|
|
285
|
-
#
|
|
286
|
-
#
|
|
287
|
-
#
|
|
288
|
-
# widening contract; the goal here is to LOSE precision, not
|
|
289
|
-
# to record a new fact set.
|
|
251
|
+
# Maps the literal Ruby key set (`Symbol` / `String`) to a union of the corresponding type
|
|
252
|
+
# carriers. We deliberately do NOT fold to a `Constant<:k1> | Constant<:k2>` union — that
|
|
253
|
+
# would be a precision improvement that complicates the widening contract; the goal here
|
|
254
|
+
# is to LOSE precision, not to record a new fact set.
|
|
290
255
|
def key_union_for(keys)
|
|
291
256
|
kinds = keys.map { |k| k.is_a?(Symbol) ? "Symbol" : "String" }.uniq
|
|
292
257
|
carriers = kinds.map { |name| Type::Combinator.nominal_of(name) }
|
|
@@ -296,45 +261,40 @@ module Rigor
|
|
|
296
261
|
# ----------------------------------------------------------------
|
|
297
262
|
# ADR-56 slice C — receiver-content element-type JOIN.
|
|
298
263
|
#
|
|
299
|
-
# `widen_after_block` above forgets a literal-shape carrier's arity
|
|
300
|
-
#
|
|
301
|
-
#
|
|
302
|
-
#
|
|
303
|
-
#
|
|
304
|
-
#
|
|
305
|
-
# key/value) types INTO the continuation collection's parameter, so
|
|
306
|
-
# the result is `Array[0 | Integer]` rather than `Array[0]`.
|
|
264
|
+
# `widen_after_block` above forgets a literal-shape carrier's arity when a captured local
|
|
265
|
+
# is content-mutated inside a block, but it keeps only the SEED's element types — an
|
|
266
|
+
# unsound under-approximation for a non-empty seed (`out = [0]; arr.each { |x| out << x
|
|
267
|
+
# }` types `Array[0]` while the runtime array is `[0, 1, 2, 3]`). Slice C joins the
|
|
268
|
+
# appended/stored element (and key/value) types INTO the continuation collection's
|
|
269
|
+
# parameter, so the result is `Array[0 | Integer]` rather than `Array[0]`.
|
|
307
270
|
#
|
|
308
|
-
# Array content-mutators that append/store ELEMENTS. The appended
|
|
309
|
-
#
|
|
310
|
-
#
|
|
311
|
-
#
|
|
312
|
-
# new element evidence and are already covered by the arity-forget).
|
|
271
|
+
# Array content-mutators that append/store ELEMENTS. The appended element type is the
|
|
272
|
+
# call's argument type(s); `[]=`'s value is its LAST argument (the keys precede it).
|
|
273
|
+
# Subset of `ARRAY_MUTATORS`: only the element-INTRODUCING methods (removers / reorderers
|
|
274
|
+
# add no new element evidence and are already covered by the arity-forget).
|
|
313
275
|
ARRAY_CONTENT_ADDERS = %i[
|
|
314
276
|
<< push append prepend unshift concat insert []= fill replace
|
|
315
277
|
].to_set.freeze
|
|
316
278
|
|
|
317
|
-
# Hash content-mutators that store a key→value pair. For `[]=` /
|
|
318
|
-
#
|
|
279
|
+
# Hash content-mutators that store a key→value pair. For `[]=` / `store` the key is the
|
|
280
|
+
# first argument and the value the last.
|
|
319
281
|
HASH_CONTENT_ADDERS = %i[[]= store].to_set.freeze
|
|
320
282
|
|
|
321
|
-
# String content-mutators that append to the buffer. String carries
|
|
322
|
-
#
|
|
323
|
-
#
|
|
324
|
-
#
|
|
325
|
-
# join helpers below short-circuit on a non-collection pre-state.
|
|
283
|
+
# String content-mutators that append to the buffer. String carries no element parameter,
|
|
284
|
+
# so these contribute nothing to a join — they are listed so the orchestrator recognises
|
|
285
|
+
# them as content mutators (the binding already widens to `String` via normal typing);
|
|
286
|
+
# the join helpers below short-circuit on a non-collection pre-state.
|
|
326
287
|
STRING_CONTENT_ADDERS = %i[<< concat prepend insert replace].to_set.freeze
|
|
327
288
|
|
|
328
|
-
# Every method name that mutates a captured local's CONTENT — the
|
|
329
|
-
#
|
|
289
|
+
# Every method name that mutates a captured local's CONTENT — the union the orchestrator
|
|
290
|
+
# scans the block body for.
|
|
330
291
|
CONTENT_ADDERS = (ARRAY_CONTENT_ADDERS | HASH_CONTENT_ADDERS | STRING_CONTENT_ADDERS).freeze
|
|
331
292
|
|
|
332
|
-
# The element types a single content-mutator call introduces into an
|
|
333
|
-
#
|
|
334
|
-
#
|
|
335
|
-
#
|
|
336
|
-
#
|
|
337
|
-
# element evidence (e.g. a `<<` with no resolvable arg).
|
|
293
|
+
# The element types a single content-mutator call introduces into an Array, given the
|
|
294
|
+
# per-argument types (already typed in the block body scope). `concat`/`replace` take
|
|
295
|
+
# collection arguments, so their element evidence is the arguments' OWN element types
|
|
296
|
+
# unioned; the rest append the argument values directly. Returns `[]` when no element
|
|
297
|
+
# evidence (e.g. a `<<` with no resolvable arg).
|
|
338
298
|
def array_added_elements(method_name, arg_types)
|
|
339
299
|
return [] if arg_types.empty?
|
|
340
300
|
|
|
@@ -357,17 +317,15 @@ module Rigor
|
|
|
357
317
|
end
|
|
358
318
|
end
|
|
359
319
|
|
|
360
|
-
# Builds the continuation Array type from the pre-state binding and
|
|
361
|
-
#
|
|
362
|
-
#
|
|
363
|
-
# at all.
|
|
320
|
+
# Builds the continuation Array type from the pre-state binding and the appended element
|
|
321
|
+
# types. The floor is `Array[Dynamic[top]]` (the sound empty-seed behaviour) when there is
|
|
322
|
+
# no element evidence at all.
|
|
364
323
|
def join_array_content(pre_state, added_elements)
|
|
365
324
|
seed_elements = collection_element_types(pre_state)
|
|
366
325
|
added = added_elements.compact
|
|
367
|
-
# The empty-seed floor element is `Dynamic[top]` (no element
|
|
368
|
-
#
|
|
369
|
-
#
|
|
370
|
-
# reads `Array[Integer]`, not `Array[Integer | Dynamic[top]]`.
|
|
326
|
+
# The empty-seed floor element is `Dynamic[top]` (no element evidence). When real
|
|
327
|
+
# appended evidence exists that floor carries nothing, so drop it — an empty accumulator
|
|
328
|
+
# built by `out << x*2` reads `Array[Integer]`, not `Array[Integer | Dynamic[top]]`.
|
|
371
329
|
seed_elements = drop_dynamic(seed_elements) unless added.empty?
|
|
372
330
|
elements = seed_elements + added
|
|
373
331
|
return Type::Combinator.nominal_of("Array", type_args: [Type::Combinator.untyped]) if elements.empty?
|
|
@@ -375,8 +333,8 @@ module Rigor
|
|
|
375
333
|
Type::Combinator.nominal_of("Array", type_args: [Type::Combinator.union(*elements)])
|
|
376
334
|
end
|
|
377
335
|
|
|
378
|
-
# Builds the continuation Hash type from the pre-state binding and a
|
|
379
|
-
#
|
|
336
|
+
# Builds the continuation Hash type from the pre-state binding and a list of `[key_type,
|
|
337
|
+
# value_type]` pairs stored by `[]=` / `store`.
|
|
380
338
|
def join_hash_content(pre_state, added_pairs)
|
|
381
339
|
seed_keys, seed_values = hash_shape_key_values(pre_state)
|
|
382
340
|
added_keys = added_pairs.map(&:first).compact
|
|
@@ -395,9 +353,9 @@ module Rigor
|
|
|
395
353
|
types.grep_v(Type::Dynamic)
|
|
396
354
|
end
|
|
397
355
|
|
|
398
|
-
# Element types carried by a collection binding, regardless of which
|
|
399
|
-
#
|
|
400
|
-
#
|
|
356
|
+
# Element types carried by a collection binding, regardless of which carrier holds them: a
|
|
357
|
+
# `Tuple` lists them, a `Nominal[Array, [E]]` has one element param, a bare `Array` /
|
|
358
|
+
# anything else yields none.
|
|
401
359
|
def collection_element_types(type)
|
|
402
360
|
case type
|
|
403
361
|
when Type::Tuple
|
|
@@ -405,17 +363,16 @@ module Rigor
|
|
|
405
363
|
when Type::Nominal
|
|
406
364
|
type.class_name == "Array" ? type.type_args : []
|
|
407
365
|
when Type::Union
|
|
408
|
-
# A loop's single-pass join can union the widened collection with
|
|
409
|
-
#
|
|
410
|
-
# evidence from every Array-ish member.
|
|
366
|
+
# A loop's single-pass join can union the widened collection with its un-widened
|
|
367
|
+
# literal seed (`Array[0] | [0]`); pull element evidence from every Array-ish member.
|
|
411
368
|
type.members.flat_map { |m| collection_element_types(m) }
|
|
412
369
|
else
|
|
413
370
|
[]
|
|
414
371
|
end
|
|
415
372
|
end
|
|
416
373
|
|
|
417
|
-
# `[keys, values]` evidence from a Hash-ish pre-state binding —
|
|
418
|
-
#
|
|
374
|
+
# `[keys, values]` evidence from a Hash-ish pre-state binding — a `HashShape` (literal
|
|
375
|
+
# pairs) or a `Nominal[Hash, [K, V]]`.
|
|
419
376
|
def hash_shape_key_values(type)
|
|
420
377
|
case type
|
|
421
378
|
when Type::HashShape
|