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
|
@@ -9,49 +9,42 @@ require_relative "rbs_type_translator"
|
|
|
9
9
|
|
|
10
10
|
module Rigor
|
|
11
11
|
module Inference
|
|
12
|
-
# Builds the entry scope of a method body by translating the method's
|
|
13
|
-
#
|
|
12
|
+
# Builds the entry scope of a method body by translating the method's parameter list into a
|
|
13
|
+
# `name -> Rigor::Type` map.
|
|
14
14
|
#
|
|
15
|
-
# Parameter types come from the surrounding class's RBS signature
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
# the
|
|
21
|
-
#
|
|
22
|
-
# `Dynamic[Top]` event is the same observable type, but the
|
|
23
|
-
# binding presence is what later slices need to attach narrowing
|
|
24
|
-
# facts to).
|
|
15
|
+
# Parameter types come from the surrounding class's RBS signature when one is available;
|
|
16
|
+
# otherwise every parameter defaults to `Dynamic[Top]`. The default is the Slice 1 fail-soft
|
|
17
|
+
# answer for unknown values, so a method whose RBS signature is missing or whose parameters
|
|
18
|
+
# cannot be matched still binds every name into the scope (a method body whose `Local x`
|
|
19
|
+
# reads return `Dynamic[Top]` instead of falling through to the unbound-local
|
|
20
|
+
# `Dynamic[Top]` event is the same observable type, but the binding presence is what later
|
|
21
|
+
# slices need to attach narrowing facts to).
|
|
25
22
|
#
|
|
26
|
-
# The class context (`class_path:`) and `singleton:` flag are
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
# `Rigor::Environment::RbsLoader#instance_method` /
|
|
32
|
-
# `#singleton_method`.
|
|
23
|
+
# The class context (`class_path:`) and `singleton:` flag are supplied by the caller (the
|
|
24
|
+
# StatementEvaluator) which threads them as the lexical class scope. The binder makes no
|
|
25
|
+
# assumption about how that context was computed; it only uses it to build the
|
|
26
|
+
# `(class_name, method_name)` lookup key for `Rigor::Environment::RbsLoader#instance_method`
|
|
27
|
+
# / `#singleton_method`.
|
|
33
28
|
#
|
|
34
29
|
# See docs/internal-spec/inference-engine.md for the binding contract.
|
|
35
|
-
# Leaf-name extraction for a destructured positional parameter
|
|
36
|
-
#
|
|
37
|
-
# {MethodParameterBinder} so the binder's class length stays in
|
|
30
|
+
# Leaf-name extraction for a destructured positional parameter (`Prism::MultiTargetNode`).
|
|
31
|
+
# Stateless; lifted out of {MethodParameterBinder} so the binder's class length stays in
|
|
38
32
|
# budget.
|
|
39
33
|
module Destructure
|
|
40
34
|
module_function
|
|
41
35
|
|
|
42
|
-
# Collect every leaf local name a `MultiTargetNode` binds,
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
# position) are skipped — there is no local to bind.
|
|
36
|
+
# Collect every leaf local name a `MultiTargetNode` binds, recursing through nested
|
|
37
|
+
# destructures (`((a, b), c)`) and the splat slot (`(a, *rest)`). Targets without a
|
|
38
|
+
# `#name` (an index/call write target, vanishingly rare in a parameter position) are
|
|
39
|
+
# skipped — there is no local to bind.
|
|
47
40
|
def target_names(multi_target)
|
|
48
41
|
entries = multi_target.lefts + [multi_target.rest, *multi_target.rights].compact
|
|
49
42
|
entries.flat_map { |entry| names_for_entry(entry) }
|
|
50
43
|
end
|
|
51
44
|
|
|
52
45
|
def names_for_entry(entry)
|
|
53
|
-
# A splat sub-target (`*rest` inside the destructure) wraps its
|
|
54
|
-
#
|
|
46
|
+
# A splat sub-target (`*rest` inside the destructure) wraps its real target in a
|
|
47
|
+
# `SplatNode#expression`; unwrap it.
|
|
55
48
|
entry = entry.expression if entry.is_a?(Prism::SplatNode) && entry.expression
|
|
56
49
|
return [] if entry.nil?
|
|
57
50
|
return target_names(entry) if entry.is_a?(Prism::MultiTargetNode)
|
|
@@ -63,19 +56,15 @@ module Rigor
|
|
|
63
56
|
|
|
64
57
|
class MethodParameterBinder
|
|
65
58
|
# @param environment [Rigor::Environment]
|
|
66
|
-
# @param class_path [String, nil] the qualified name of the class
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
# `
|
|
71
|
-
# @param singleton [Boolean] `true` when the def is a singleton
|
|
72
|
-
# method (either `def self.foo` or a `def foo` inside
|
|
73
|
-
# `class << self`); routes the lookup through
|
|
59
|
+
# @param class_path [String, nil] the qualified name of the class the method is defined in
|
|
60
|
+
# (e.g., `"Foo::Bar"`), or `nil` for a top-level `def` outside any class. When `nil` (or
|
|
61
|
+
# when the class is unknown to RBS), every parameter falls back to `Dynamic[Top]`.
|
|
62
|
+
# @param singleton [Boolean] `true` when the def is a singleton method (either `def
|
|
63
|
+
# self.foo` or a `def foo` inside `class << self`); routes the lookup through
|
|
74
64
|
# `RbsLoader#singleton_method`.
|
|
75
|
-
# @param source_path [String, nil] the project-relative path of
|
|
76
|
-
#
|
|
77
|
-
#
|
|
78
|
-
# synthetic / probe scopes) disables the contract tier.
|
|
65
|
+
# @param source_path [String, nil] the project-relative path of the file the method is
|
|
66
|
+
# defined in. Used to match ADR-28 path-scoped protocol contracts; `nil` (the default
|
|
67
|
+
# for synthetic / probe scopes) disables the contract tier.
|
|
79
68
|
def initialize(environment:, class_path:, singleton:, source_path: nil)
|
|
80
69
|
@environment = environment
|
|
81
70
|
@class_path = class_path
|
|
@@ -84,9 +73,8 @@ module Rigor
|
|
|
84
73
|
end
|
|
85
74
|
|
|
86
75
|
# @param def_node [Prism::DefNode]
|
|
87
|
-
# @return [Hash{Symbol => Rigor::Type}] ordered map from parameter
|
|
88
|
-
#
|
|
89
|
-
# a name) are skipped.
|
|
76
|
+
# @return [Hash{Symbol => Rigor::Type}] ordered map from parameter name to bound type.
|
|
77
|
+
# Anonymous parameters (`*` and `**` without a name) are skipped.
|
|
90
78
|
def bind(def_node)
|
|
91
79
|
slots = collect_slots(def_node.parameters)
|
|
92
80
|
types = default_types_for(slots)
|
|
@@ -94,19 +82,15 @@ module Rigor
|
|
|
94
82
|
rbs_method = lookup_rbs_method(def_node)
|
|
95
83
|
if rbs_method
|
|
96
84
|
apply_rbs_overloads(types, slots, rbs_method.method_types) unless rbs_method.method_types.empty?
|
|
97
|
-
# `rigor:v1:param: <name> <refinement>` annotations
|
|
98
|
-
#
|
|
99
|
-
#
|
|
100
|
-
# authoritative answer regardless of what the RBS
|
|
101
|
-
# signature declared.
|
|
85
|
+
# `rigor:v1:param: <name> <refinement>` annotations tighten the bound type for matching
|
|
86
|
+
# slots. Applied after the RBS-overload pass so the override is the authoritative
|
|
87
|
+
# answer regardless of what the RBS signature declared.
|
|
102
88
|
apply_param_overrides(types, slots, rbs_method)
|
|
103
89
|
end
|
|
104
|
-
# ADR-28 — a path-scoped protocol contract supplies the
|
|
105
|
-
#
|
|
106
|
-
#
|
|
107
|
-
#
|
|
108
|
-
# like — typically have no RBS signature at all, so this
|
|
109
|
-
# tier must run even when `rbs_method` is nil.
|
|
90
|
+
# ADR-28 — a path-scoped protocol contract supplies the parameter type for a matching
|
|
91
|
+
# `def`. Applied last (most authoritative) and regardless of RBS presence: the methods a
|
|
92
|
+
# contract targets — controller actions and the like — typically have no RBS signature
|
|
93
|
+
# at all, so this tier must run even when `rbs_method` is nil.
|
|
110
94
|
apply_protocol_contract(types, slots, def_node)
|
|
111
95
|
types
|
|
112
96
|
end
|
|
@@ -120,14 +104,11 @@ module Rigor
|
|
|
120
104
|
POSITIONAL_KINDS = %i[required_positional optional_positional].freeze
|
|
121
105
|
private_constant :POSITIONAL_KINDS
|
|
122
106
|
|
|
123
|
-
# Walk the Prism `ParametersNode` and emit one slot per named
|
|
124
|
-
#
|
|
125
|
-
#
|
|
126
|
-
#
|
|
127
|
-
#
|
|
128
|
-
# up the matching RBS function param) and is `nil` for the
|
|
129
|
-
# singleton kinds (`:rest_positional`, `:rest_keyword`,
|
|
130
|
-
# `:block`).
|
|
107
|
+
# Walk the Prism `ParametersNode` and emit one slot per named parameter, in declaration
|
|
108
|
+
# order. Anonymous slots (rest / keyword-rest with no name) are skipped because we have no
|
|
109
|
+
# local name to bind. The slot's `:index` is the positional index for
|
|
110
|
+
# required/optional/trailing positionals (used to look up the matching RBS function param)
|
|
111
|
+
# and is `nil` for the singleton kinds (`:rest_positional`, `:rest_keyword`, `:block`).
|
|
131
112
|
def collect_slots(params_node)
|
|
132
113
|
return [] if params_node.nil?
|
|
133
114
|
|
|
@@ -153,17 +134,14 @@ module Rigor
|
|
|
153
134
|
slots
|
|
154
135
|
end
|
|
155
136
|
|
|
156
|
-
# A destructured positional parameter — `def f((a, b))` — is a
|
|
157
|
-
#
|
|
158
|
-
#
|
|
159
|
-
#
|
|
160
|
-
#
|
|
161
|
-
#
|
|
162
|
-
#
|
|
163
|
-
#
|
|
164
|
-
# element types are not cheaply available from the parameter list
|
|
165
|
-
# alone (no RBS function param maps onto a destructured slot), so
|
|
166
|
-
# `Dynamic[Top]` is the sound default.
|
|
137
|
+
# A destructured positional parameter — `def f((a, b))` — is a `Prism::MultiTargetNode` in
|
|
138
|
+
# the `requireds`/`posts` list, not a `RequiredParameterNode`, so it has no `#name`. Bind
|
|
139
|
+
# each leaf sub-target local to `Dynamic[Top]` (a `:destructured_positional` slot with no
|
|
140
|
+
# RBS index) instead of crashing on a blind `.name`. Binding the names at all is what
|
|
141
|
+
# matters: it keeps the destructured locals present in the entry scope so the body's reads
|
|
142
|
+
# of them don't fall through to undefined-local noise. The element types are not cheaply
|
|
143
|
+
# available from the parameter list alone (no RBS function param maps onto a destructured
|
|
144
|
+
# slot), so `Dynamic[Top]` is the sound default.
|
|
167
145
|
def append_positional_slot(slots, kind, param, index)
|
|
168
146
|
if param.is_a?(Prism::MultiTargetNode)
|
|
169
147
|
Destructure.target_names(param).each do |name|
|
|
@@ -207,9 +185,8 @@ module Rigor
|
|
|
207
185
|
return nil if @class_path.nil?
|
|
208
186
|
|
|
209
187
|
method_name = def_node.name
|
|
210
|
-
# `def self.foo` always means a singleton method on the
|
|
211
|
-
#
|
|
212
|
-
# is also a singleton method (the StatementEvaluator threads
|
|
188
|
+
# `def self.foo` always means a singleton method on the immediate enclosing class. `def
|
|
189
|
+
# foo` inside `class << self` is also a singleton method (the StatementEvaluator threads
|
|
213
190
|
# the `singleton:` flag through this case).
|
|
214
191
|
if def_node.receiver.is_a?(Prism::SelfNode) || @singleton
|
|
215
192
|
Rigor::Reflection.singleton_method_definition(@class_path, method_name, environment: @environment)
|
|
@@ -218,13 +195,11 @@ module Rigor
|
|
|
218
195
|
end
|
|
219
196
|
end
|
|
220
197
|
|
|
221
|
-
# Bind each parameter slot to the union of the matching parameter
|
|
222
|
-
#
|
|
223
|
-
#
|
|
224
|
-
#
|
|
225
|
-
#
|
|
226
|
-
# the most informative type the RBS signature provides without
|
|
227
|
-
# having to know which overload the runtime will pick.
|
|
198
|
+
# Bind each parameter slot to the union of the matching parameter types across every
|
|
199
|
+
# overload that *has* that slot. Overloads that omit the slot (e.g., `Array#first` has
|
|
200
|
+
# both `()` and `(int)` overloads — only the second matches a `def first(n)` redefinition)
|
|
201
|
+
# are silently skipped, so the binder defaults to the most informative type the RBS
|
|
202
|
+
# signature provides without having to know which overload the runtime will pick.
|
|
228
203
|
def apply_rbs_overloads(types, slots, method_types)
|
|
229
204
|
slots.each do |slot|
|
|
230
205
|
next if slot.name.nil?
|
|
@@ -236,13 +211,11 @@ module Rigor
|
|
|
236
211
|
end
|
|
237
212
|
end
|
|
238
213
|
|
|
239
|
-
# Reads the override map off the method's annotations and
|
|
240
|
-
#
|
|
241
|
-
#
|
|
242
|
-
#
|
|
243
|
-
#
|
|
244
|
-
# `non-empty-array[Integer]` describe the parameter binding
|
|
245
|
-
# they actually want, not its element type.
|
|
214
|
+
# Reads the override map off the method's annotations and replaces the binding for any
|
|
215
|
+
# slot whose name appears in the map. Anonymous slots are skipped (no name to match). The
|
|
216
|
+
# override is used verbatim — no `:rest_*` re-wrapping — so authors who tighten a `*rest`
|
|
217
|
+
# parameter to e.g. `non-empty-array[Integer]` describe the parameter binding they
|
|
218
|
+
# actually want, not its element type.
|
|
246
219
|
def apply_param_overrides(types, slots, rbs_method)
|
|
247
220
|
override_map = RbsExtended.param_type_override_map(rbs_method, environment: @environment)
|
|
248
221
|
return if override_map.empty?
|
|
@@ -257,13 +230,11 @@ module Rigor
|
|
|
257
230
|
end
|
|
258
231
|
end
|
|
259
232
|
|
|
260
|
-
# ADR-28 — when a path-scoped protocol contract targets this
|
|
261
|
-
#
|
|
262
|
-
#
|
|
263
|
-
#
|
|
264
|
-
#
|
|
265
|
-
# an unresolvable name (the protocol's RBS not loaded) falls
|
|
266
|
-
# through to whatever the prior tiers bound, fail-soft.
|
|
233
|
+
# ADR-28 — when a path-scoped protocol contract targets this `def` (file path matches the
|
|
234
|
+
# contract's `path_glob`, method name + singleton-ness match), replace each contracted
|
|
235
|
+
# positional slot's binding with the contract's declared type. The type name resolves
|
|
236
|
+
# against the environment lazily here; an unresolvable name (the protocol's RBS not
|
|
237
|
+
# loaded) falls through to whatever the prior tiers bound, fail-soft.
|
|
267
238
|
def apply_protocol_contract(types, slots, def_node)
|
|
268
239
|
return if @source_path.nil?
|
|
269
240
|
|
|
@@ -311,17 +282,14 @@ module Rigor
|
|
|
311
282
|
wrap_for_kind(bound, kind)
|
|
312
283
|
end
|
|
313
284
|
|
|
314
|
-
# Dispatch table from slot kind to a small lambda that pulls the
|
|
315
|
-
#
|
|
316
|
-
#
|
|
317
|
-
#
|
|
318
|
-
#
|
|
319
|
-
#
|
|
320
|
-
#
|
|
321
|
-
#
|
|
322
|
-
# (`?by:`) while the Ruby `def` lists it as required (or vice
|
|
323
|
-
# versa); the binding is by-name regardless of which side
|
|
324
|
-
# defines it.
|
|
285
|
+
# Dispatch table from slot kind to a small lambda that pulls the matching RBS parameter
|
|
286
|
+
# type out of an `RBS::Types::Function`. The hash keeps `rbs_type_for_slot` linear (one
|
|
287
|
+
# lookup, one call) so the cyclomatic-complexity budget does not balloon as future slices
|
|
288
|
+
# add more parameter kinds (e.g., `**Symbol kw` is a candidate for a stricter route in
|
|
289
|
+
# Slice 5+).
|
|
290
|
+
# Match keyword parameters by name across both required and optional keyword maps. RBS may
|
|
291
|
+
# declare a keyword as optional (`?by:`) while the Ruby `def` lists it as required (or
|
|
292
|
+
# vice versa); the binding is by-name regardless of which side defines it.
|
|
325
293
|
KEYWORD_PROVIDER = Ractor.make_shareable(lambda do |fn, slot|
|
|
326
294
|
fn.required_keywords[slot.name]&.type || fn.optional_keywords[slot.name]&.type
|
|
327
295
|
end)
|
|
@@ -355,11 +323,10 @@ module Rigor
|
|
|
355
323
|
provider.call(function, slot)
|
|
356
324
|
end
|
|
357
325
|
|
|
358
|
-
# The variable bound to a `*rest` parameter is the *Array* of
|
|
359
|
-
#
|
|
360
|
-
#
|
|
361
|
-
#
|
|
362
|
-
# `Array[Integer]` rather than `Integer`.
|
|
326
|
+
# The variable bound to a `*rest` parameter is the *Array* of rest-positional arguments,
|
|
327
|
+
# not a single element. Likewise `**kw` is bound to a `Hash[Symbol, V]`. Wrap the
|
|
328
|
+
# translated element/value type accordingly so `rest` reads as `Array[Integer]` rather
|
|
329
|
+
# than `Integer`.
|
|
363
330
|
def wrap_for_kind(translated, kind)
|
|
364
331
|
case kind
|
|
365
332
|
when :rest_positional
|
|
@@ -8,57 +8,45 @@ module Rigor
|
|
|
8
8
|
module Inference
|
|
9
9
|
# Slice 5 phase 2 sub-phase 2 destructuring binder.
|
|
10
10
|
#
|
|
11
|
-
# `Rigor::Inference::MultiTargetBinder` decomposes a tuple-shaped
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# shared between two surfaces:
|
|
11
|
+
# `Rigor::Inference::MultiTargetBinder` decomposes a tuple-shaped right-hand side type
|
|
12
|
+
# against a Prism multi-target tree and produces a `name -> Rigor::Type` binding map. The
|
|
13
|
+
# binder is shared between two surfaces:
|
|
15
14
|
#
|
|
16
|
-
# 1. `Rigor::Inference::StatementEvaluator#eval_multi_write` for
|
|
17
|
-
#
|
|
18
|
-
# 2. `Rigor::Inference::BlockParameterBinder` for nested
|
|
19
|
-
#
|
|
20
|
-
# (`Prism::MultiTargetNode` under `BlockParametersNode#requireds`).
|
|
15
|
+
# 1. `Rigor::Inference::StatementEvaluator#eval_multi_write` for the statement-level `a, b =
|
|
16
|
+
# rhs` form (`Prism::MultiWriteNode`).
|
|
17
|
+
# 2. `Rigor::Inference::BlockParameterBinder` for nested destructuring inside block parameter
|
|
18
|
+
# lists (`Prism::MultiTargetNode` under `BlockParametersNode#requireds`).
|
|
21
19
|
#
|
|
22
|
-
# Both Prism nodes share the same `lefts` / `rest` (a
|
|
23
|
-
#
|
|
24
|
-
# uniformly. The binder is pure: it MUST NOT mutate its inputs and
|
|
20
|
+
# Both Prism nodes share the same `lefts` / `rest` (a `Prism::SplatNode`) / `rights` triple,
|
|
21
|
+
# so the binder treats them uniformly. The binder is pure: it MUST NOT mutate its inputs and
|
|
25
22
|
# MUST return a fresh `Hash` on every call.
|
|
26
23
|
#
|
|
27
|
-
# The binder threads `Type::Tuple` decompositions when the
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
# conservative on dynamic-arity right-hand sides until the
|
|
32
|
-
# narrower receiver-shape lattice lands.
|
|
24
|
+
# The binder threads `Type::Tuple` decompositions when the right-hand side carrier is a
|
|
25
|
+
# known-arity tuple. Other carriers (`Nominal[Array]`, `Dynamic[Top]`, `Top`, `Bot`, ...)
|
|
26
|
+
# collapse to `Dynamic[Top]` per slot — Slice 5 phase 2 sub-phase 2 stays conservative on
|
|
27
|
+
# dynamic-arity right-hand sides until the narrower receiver-shape lattice lands.
|
|
33
28
|
#
|
|
34
29
|
# Targets the binder recognises:
|
|
35
30
|
#
|
|
36
|
-
# - `Prism::LocalVariableTargetNode` — used by the statement-level
|
|
37
|
-
# `
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
# the new right-hand side.
|
|
48
|
-
# - `Prism::SplatNode` (used for `rest`) — its `expression` MUST
|
|
49
|
-
# be a `Prism::LocalVariableTargetNode` or a
|
|
50
|
-
# `Prism::RequiredParameterNode` to be observable; an anonymous
|
|
51
|
-
# `*` splat or a non-local target is skipped.
|
|
31
|
+
# - `Prism::LocalVariableTargetNode` — used by the statement-level `a, b = rhs` form. Binds
|
|
32
|
+
# `target.name` to its slice of the right-hand side.
|
|
33
|
+
# - `Prism::RequiredParameterNode` — used by block-parameter destructuring (`|(a, b), c|`).
|
|
34
|
+
# Prism encodes the inner names of a block-side `MultiTargetNode` as parameter nodes
|
|
35
|
+
# rather than target nodes; the binder treats them uniformly with their
|
|
36
|
+
# `LocalVariableTargetNode` cousins because they carry the same `name:` field and the
|
|
37
|
+
# same observable semantics (binding a fresh local in the block-entry scope).
|
|
38
|
+
# - `Prism::MultiTargetNode` — recurses with the slot's type as the new right-hand side.
|
|
39
|
+
# - `Prism::SplatNode` (used for `rest`) — its `expression` MUST be a
|
|
40
|
+
# `Prism::LocalVariableTargetNode` or a `Prism::RequiredParameterNode` to be observable;
|
|
41
|
+
# an anonymous `*` splat or a non-local target is skipped.
|
|
52
42
|
#
|
|
53
|
-
# Other target kinds (`InstanceVariableTargetNode`,
|
|
54
|
-
# `
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
# local-variable scope the StatementEvaluator threads.
|
|
43
|
+
# Other target kinds (`InstanceVariableTargetNode`, `ConstantTargetNode`,
|
|
44
|
+
# `IndexTargetNode`, `CallTargetNode`, `ConstantPathTargetNode`, `ImplicitRestNode`, ...)
|
|
45
|
+
# MUST be silently skipped: they have no observable contribution to the local-variable scope
|
|
46
|
+
# the StatementEvaluator threads.
|
|
58
47
|
#
|
|
59
|
-
# See docs/internal-spec/inference-engine.md for the binding
|
|
60
|
-
#
|
|
61
|
-
# rationale.
|
|
48
|
+
# See docs/internal-spec/inference-engine.md for the binding contract and
|
|
49
|
+
# docs/adr/4-type-inference-engine.md for the slice rationale.
|
|
62
50
|
module MultiTargetBinder
|
|
63
51
|
module_function
|
|
64
52
|
|
|
@@ -85,11 +73,10 @@ module Rigor
|
|
|
85
73
|
rights.each_with_index { |t, i| bind_target(t, backs[i], bindings) }
|
|
86
74
|
end
|
|
87
75
|
|
|
88
|
-
# Decomposes the right-hand side type into the per-slot
|
|
89
|
-
#
|
|
90
|
-
# `
|
|
91
|
-
# `
|
|
92
|
-
# `Rigor::Type` (when `rest_present:` is true) or `nil`.
|
|
76
|
+
# Decomposes the right-hand side type into the per-slot types. Returns a `[fronts,
|
|
77
|
+
# rest_type, backs]` triple, with `fronts` and `backs` each an ordered array of length
|
|
78
|
+
# `front_count`/`back_count`, and `rest_type` either a `Rigor::Type` (when
|
|
79
|
+
# `rest_present:` is true) or `nil`.
|
|
93
80
|
def decompose(rhs_type, front_count, back_count, rest_present:)
|
|
94
81
|
if rhs_type.is_a?(Type::Tuple)
|
|
95
82
|
decompose_tuple(rhs_type, front_count, back_count, rest_present: rest_present)
|
|
@@ -113,28 +100,24 @@ module Rigor
|
|
|
113
100
|
[fronts, rest_type, backs]
|
|
114
101
|
end
|
|
115
102
|
|
|
116
|
-
# The per-slot type for index `i` of a tuple decomposition, FP-safely
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
# `Tuple` whose optional slot was made optional by flow.
|
|
103
|
+
# The per-slot type for index `i` of a tuple decomposition, FP-safely softened: a
|
|
104
|
+
# missing slot is `nil` (the runtime value of an over-destructured positional), and a
|
|
105
|
+
# PRESENT but nil-bearing slot (`X | nil`) is softened to its non-`nil` part — for a
|
|
106
|
+
# heterogeneous `Tuple` whose optional slot was made optional by flow.
|
|
121
107
|
#
|
|
122
|
-
# Rationale (ADR-57 slice 3 work-item 2): a destructure of a tuple
|
|
123
|
-
#
|
|
124
|
-
#
|
|
125
|
-
#
|
|
126
|
-
#
|
|
127
|
-
#
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
#
|
|
131
|
-
#
|
|
132
|
-
#
|
|
133
|
-
#
|
|
134
|
-
#
|
|
135
|
-
# from a destructured slot and keep the non-`nil` constituent (a bare
|
|
136
|
-
# `nil` slot stays `nil` — there is nothing to soften). A pure non-
|
|
137
|
-
# optional element keeps its precise type unchanged.
|
|
108
|
+
# Rationale (ADR-57 slice 3 work-item 2): a destructure of a tuple element that flow
|
|
109
|
+
# typed as optional is almost always guarded by a CORRELATED invariant the flow engine
|
|
110
|
+
# cannot prove. The canonical case is haml's `parse_tag`, which returns `[...,
|
|
111
|
+
# last_line || @line.index + 1]` — a 9-tuple whose `last_line` slot widens to
|
|
112
|
+
# `Dynamic[top]?` through a loop-nested destructure; at the call site `..., last_line =
|
|
113
|
+
# parse_tag(text); raise(..., last_line - 1) if parse && value.empty?` the `last_line`
|
|
114
|
+
# is nil ONLY when an earlier element is too, and the guard short-circuits — but that
|
|
115
|
+
# correlation lives across slots, so per-slot flow sees `last_line` as nil-able and
|
|
116
|
+
# `last_line - 1` fires a spurious `possible nil receiver`. Manufacturing a `T?` for
|
|
117
|
+
# every destructured slot frightens working code; FP discipline (the program works)
|
|
118
|
+
# outranks the worst-case per-slot reading, so we drop the `nil` from a destructured slot
|
|
119
|
+
# and keep the non-`nil` constituent (a bare `nil` slot stays `nil` — there is nothing to
|
|
120
|
+
# soften). A pure non-optional element keeps its precise type unchanged.
|
|
138
121
|
def slot_type(elements, index)
|
|
139
122
|
element = elements[index]
|
|
140
123
|
return Type::Combinator.constant_of(nil) if element.nil?
|