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
|
@@ -7,62 +7,45 @@ require_relative "multi_target_binder"
|
|
|
7
7
|
|
|
8
8
|
module Rigor
|
|
9
9
|
module Inference
|
|
10
|
-
# Builds the entry scope of a block body by translating the block's
|
|
11
|
-
#
|
|
10
|
+
# Builds the entry scope of a block body by translating the block's parameter list into a `name ->
|
|
11
|
+
# Rigor::Type` map.
|
|
12
12
|
#
|
|
13
|
-
# The binder is the symmetric counterpart of {MethodParameterBinder}
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
# (
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
# into the scope (a block body whose `Local x` reads return
|
|
22
|
-
# `Dynamic[Top]` instead of falling through to the unbound-local
|
|
23
|
-
# `Dynamic[Top]` event is the same observable type, but the
|
|
24
|
-
# binding presence is what later slices need to attach narrowing
|
|
25
|
-
# facts to).
|
|
13
|
+
# The binder is the symmetric counterpart of {MethodParameterBinder} for `Prism::BlockNode`. The
|
|
14
|
+
# expected parameter types come from the receiving method's RBS signature
|
|
15
|
+
# ({Rigor::Inference::MethodDispatcher.expected_block_param_types}); parameters that the signature does
|
|
16
|
+
# not cover (or that the binder cannot match by position) default to `Dynamic[Top]`. The default is the
|
|
17
|
+
# Slice 1 fail-soft answer for unknown values, so a block whose receiving method has no signature still
|
|
18
|
+
# binds every name into the scope (a block body whose `Local x` reads return `Dynamic[Top]` instead of
|
|
19
|
+
# falling through to the unbound-local `Dynamic[Top]` event is the same observable type, but the binding
|
|
20
|
+
# presence is what later slices need to attach narrowing facts to).
|
|
26
21
|
#
|
|
27
|
-
# MultiTargetNode parameters (`|(a, b), c|`) are bound by
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
# (`_1`, `_2`, ...) are bound from `Prism::NumberedParametersNode`
|
|
33
|
-
# using the same per-position `expected_param_types:` array, so
|
|
34
|
-
# `[1, 2, 3].each { _1 + _2 }` sees `_1`/`_2` typed identically
|
|
35
|
-
# to their explicit `|x, y|` counterparts.
|
|
22
|
+
# MultiTargetNode parameters (`|(a, b), c|`) are bound by delegating each destructuring slot to
|
|
23
|
+
# {Rigor::Inference::MultiTargetBinder}, so a Tuple-shaped expected element type projects element-wise
|
|
24
|
+
# into the inner locals (Slice 6 phase C sub-phase 2). Numbered parameters (`_1`, `_2`, ...) are bound
|
|
25
|
+
# from `Prism::NumberedParametersNode` using the same per-position `expected_param_types:` array, so
|
|
26
|
+
# `[1, 2, 3].each { _1 + _2 }` sees `_1`/`_2` typed identically to their explicit `|x, y|` counterparts.
|
|
36
27
|
#
|
|
37
|
-
# The `it` implicit parameter (Ruby 3.4+) is bound from
|
|
38
|
-
#
|
|
39
|
-
# `
|
|
40
|
-
# the body's `Prism::ItLocalVariableReadNode` lookup sees the same
|
|
41
|
-
# type as the explicit `|x|` form would.
|
|
28
|
+
# The `it` implicit parameter (Ruby 3.4+) is bound from `Prism::ItParametersNode`. It is the
|
|
29
|
+
# single-argument cousin of `_1`: the binder produces `{ it: expected_param_types[0] }` so the body's
|
|
30
|
+
# `Prism::ItLocalVariableReadNode` lookup sees the same type as the explicit `|x|` form would.
|
|
42
31
|
#
|
|
43
|
-
# Block-local declarations after `;` (e.g., `|x; y, z|`) are
|
|
44
|
-
#
|
|
45
|
-
# scope MUST NOT observe them and the binder leaves them unbound.
|
|
32
|
+
# Block-local declarations after `;` (e.g., `|x; y, z|`) are still skipped — they are explicitly
|
|
33
|
+
# block-local, so the outer scope MUST NOT observe them and the binder leaves them unbound.
|
|
46
34
|
#
|
|
47
35
|
# See docs/internal-spec/inference-engine.md for the binding contract.
|
|
48
36
|
class BlockParameterBinder
|
|
49
|
-
# @param expected_param_types [Array<Rigor::Type>] positional block
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
# list, or because the slot is a kind we do not pull from the
|
|
53
|
-
# array) default to `Dynamic[Top]`.
|
|
37
|
+
# @param expected_param_types [Array<Rigor::Type>] positional block parameter types in order. Indices
|
|
38
|
+
# the binder cannot fill from this array (because the array is shorter than the parameter list, or
|
|
39
|
+
# because the slot is a kind we do not pull from the array) default to `Dynamic[Top]`.
|
|
54
40
|
def initialize(expected_param_types: [])
|
|
55
41
|
@expected_param_types = expected_param_types
|
|
56
42
|
end
|
|
57
43
|
|
|
58
44
|
# @param block_node [Prism::BlockNode]
|
|
59
|
-
# @return [Hash{Symbol => Rigor::Type}] ordered map from parameter
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
# declaration order. Numbered-parameter forms (`_1`, `_2`,
|
|
64
|
-
# ...) bind `:_1`, `:_2`, ... up to the maximum the block
|
|
65
|
-
# body refers to.
|
|
45
|
+
# @return [Hash{Symbol => Rigor::Type}] ordered map from parameter name to bound type. Anonymous
|
|
46
|
+
# parameters are skipped; MultiTargetNode destructuring slots delegate to {MultiTargetBinder} and
|
|
47
|
+
# contribute every named local in declaration order. Numbered-parameter forms (`_1`, `_2`, ...) bind
|
|
48
|
+
# `:_1`, `:_2`, ... up to the maximum the block body refers to.
|
|
66
49
|
def bind(block_node)
|
|
67
50
|
params_root = block_node.parameters
|
|
68
51
|
return {} if params_root.nil?
|
|
@@ -81,12 +64,10 @@ module Rigor
|
|
|
81
64
|
|
|
82
65
|
private
|
|
83
66
|
|
|
84
|
-
# `|_1, _2|` numbered-parameter form. Prism exposes the
|
|
85
|
-
#
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
# `LocalVariableReadNode` lookups see the same types as the
|
|
89
|
-
# equivalent explicit `|x, y|` form would.
|
|
67
|
+
# `|_1, _2|` numbered-parameter form. Prism exposes the implicit count through
|
|
68
|
+
# `NumberedParametersNode#maximum` (the highest `_N` referenced in the body); we materialise bindings
|
|
69
|
+
# for `:_1` through `:_maximum` so the block body's `LocalVariableReadNode` lookups see the same types
|
|
70
|
+
# as the equivalent explicit `|x, y|` form would.
|
|
90
71
|
def bind_numbered_parameters(numbered_node)
|
|
91
72
|
bindings = {}
|
|
92
73
|
numbered_node.maximum.times do |i|
|
|
@@ -95,9 +76,8 @@ module Rigor
|
|
|
95
76
|
bindings
|
|
96
77
|
end
|
|
97
78
|
|
|
98
|
-
# `{ it.foo }` — Ruby 3.4 `it` is a single-argument implicit
|
|
99
|
-
#
|
|
100
|
-
# in the body looks the binding up by name.
|
|
79
|
+
# `{ it.foo }` — Ruby 3.4 `it` is a single-argument implicit parameter. Always binds the symbol `:it`;
|
|
80
|
+
# `ItLocalVariableReadNode` in the body looks the binding up by name.
|
|
101
81
|
def bind_it_parameter
|
|
102
82
|
{ it: positional_type_at(0) }
|
|
103
83
|
end
|
|
@@ -117,27 +97,21 @@ module Rigor
|
|
|
117
97
|
bindings
|
|
118
98
|
end
|
|
119
99
|
|
|
120
|
-
# Ruby blocks (NOT lambdas) auto-splat a single yielded
|
|
121
|
-
#
|
|
122
|
-
# required positional parameter:
|
|
100
|
+
# Ruby blocks (NOT lambdas) auto-splat a single yielded Tuple-shaped value when the block declares more
|
|
101
|
+
# than one required positional parameter:
|
|
123
102
|
#
|
|
124
103
|
# { a: 1 }.each { |k, v| ... }
|
|
125
104
|
#
|
|
126
|
-
# yields `[key, value]` as a single arg, but the two-param
|
|
127
|
-
# block
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
# `k = Tuple[K, V]` and `v = Dynamic[Top]`, and any call
|
|
131
|
-
# on `k.<method-not-on-Tuple>` would false-fire.
|
|
105
|
+
# yields `[key, value]` as a single arg, but the two-param block sees `k = key, v = value`. RBS /
|
|
106
|
+
# IteratorDispatch encode this as the block taking ONE `[K, V]` Tuple parameter; without this fix-up
|
|
107
|
+
# the binder would assign `k = Tuple[K, V]` and `v = Dynamic[Top]`, and any call on
|
|
108
|
+
# `k.<method-not-on-Tuple>` would false-fire.
|
|
132
109
|
#
|
|
133
|
-
# The rule fires only when (a) the receiver yields exactly
|
|
134
|
-
#
|
|
135
|
-
#
|
|
136
|
-
#
|
|
137
|
-
#
|
|
138
|
-
# NOT auto-splatted — matching Ruby semantics where a
|
|
139
|
-
# multi-arg yield to a `|a, b, c|` block fills the extra
|
|
140
|
-
# slot with nil rather than splatting any element.
|
|
110
|
+
# The rule fires only when (a) the receiver yields exactly one value (`expected_param_types.size ==
|
|
111
|
+
# 1`), (b) the block declares more than one positional slot, and (c) that single expected element is a
|
|
112
|
+
# Tuple. Multi-arg yields (e.g. `each_with_index`'s `(element, index)` pair) are NOT auto-splatted —
|
|
113
|
+
# matching Ruby semantics where a multi-arg yield to a `|a, b, c|` block fills the extra slot with nil
|
|
114
|
+
# rather than splatting any element.
|
|
141
115
|
def apply_auto_splat(params_node)
|
|
142
116
|
return unless @expected_param_types.size == 1
|
|
143
117
|
|
|
@@ -180,11 +154,9 @@ module Rigor
|
|
|
180
154
|
cursor
|
|
181
155
|
end
|
|
182
156
|
|
|
183
|
-
# `|*rest|` binds an Array of the leftover positional arguments.
|
|
184
|
-
#
|
|
185
|
-
#
|
|
186
|
-
# default to `Array[Dynamic[Top]]`. Element-type precision for
|
|
187
|
-
# rest parameters is deferred (demand-gated).
|
|
157
|
+
# `|*rest|` binds an Array of the leftover positional arguments. The expected-types array is
|
|
158
|
+
# per-position, not per-rest; we cannot reliably pick a single element type for rest, so we default to
|
|
159
|
+
# `Array[Dynamic[Top]]`. Element-type precision for rest parameters is deferred (demand-gated).
|
|
188
160
|
def bind_rest(params_node, bindings)
|
|
189
161
|
rest = params_node.rest
|
|
190
162
|
return unless rest.respond_to?(:name) && rest&.name
|
|
@@ -219,13 +191,11 @@ module Rigor
|
|
|
219
191
|
bindings[block.name] = Type::Combinator.nominal_of(Proc)
|
|
220
192
|
end
|
|
221
193
|
|
|
222
|
-
# Required parameters in a block list can be either a plain
|
|
223
|
-
# `
|
|
224
|
-
#
|
|
225
|
-
#
|
|
226
|
-
#
|
|
227
|
-
# inner local. Other shapes (anonymous required parameters,
|
|
228
|
-
# forward arguments) are silently skipped.
|
|
194
|
+
# Required parameters in a block list can be either a plain `RequiredParameterNode` (named) or a
|
|
195
|
+
# `MultiTargetNode` (the `|(a, b), c|` destructuring form). Slice 6 phase C sub-phase 2 delegates the
|
|
196
|
+
# latter to {MultiTargetBinder}, which decomposes the slot's expected Tuple element-wise and binds
|
|
197
|
+
# every named inner local. Other shapes (anonymous required parameters, forward arguments) are
|
|
198
|
+
# silently skipped.
|
|
229
199
|
def bind_required_param(param, cursor, bindings)
|
|
230
200
|
case param
|
|
231
201
|
when Prism::RequiredParameterNode
|
|
@@ -5,25 +5,21 @@ require_relative "budget_trace"
|
|
|
5
5
|
|
|
6
6
|
module Rigor
|
|
7
7
|
module Inference
|
|
8
|
-
# ADR-56 WD3 — the single capped-fixpoint mechanism shared by the
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# across an unknown number (0..N) of iterations.
|
|
8
|
+
# ADR-56 WD3 — the single capped-fixpoint mechanism shared by the non-escaping block captured-local
|
|
9
|
+
# write-back (slice A) and the loop-body fixpoint (slice B). Computes the continuation binding of a set of
|
|
10
|
+
# locals that a body (a block body or a loop body) may rebind across an unknown number (0..N) of
|
|
11
|
+
# iterations.
|
|
13
12
|
#
|
|
14
|
-
# The body may run zero times, so the seed (pre-state) binding is kept
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# collapsed to `Dynamic[top]` — the established escaping-block floor —
|
|
20
|
-
# and a {BudgetTrace::BLOCK_WRITEBACK_CAP} hit is recorded.
|
|
13
|
+
# The body may run zero times, so the seed (pre-state) binding is kept as a join constituent throughout; it
|
|
14
|
+
# may compound (`a = [a]`), so the join is iterated to a fixed point under a hard cap (ADR-41 WD4). On the
|
|
15
|
+
# final permitted iteration value-pinned constituents are widened to their nominal base to force
|
|
16
|
+
# convergence; a local that still moves is collapsed to `Dynamic[top]` — the established escaping-block
|
|
17
|
+
# floor — and a {BudgetTrace::BLOCK_WRITEBACK_CAP} hit is recorded.
|
|
21
18
|
#
|
|
22
|
-
# The mechanism is parameterized over an `evaluate_body` callable so
|
|
23
|
-
#
|
|
24
|
-
# the
|
|
25
|
-
#
|
|
26
|
-
# not appear in the returned hash).
|
|
19
|
+
# The mechanism is parameterized over an `evaluate_body` callable so slice B can reuse it: given the
|
|
20
|
+
# current per-name bindings it returns the per-name exit bindings produced by one body evaluation from
|
|
21
|
+
# those bindings (names the body leaves unwritten in a given pass simply do not appear in the returned
|
|
22
|
+
# hash).
|
|
27
23
|
module BodyFixpoint
|
|
28
24
|
# One body evaluation per iteration; ADR-55's shape (cap 3).
|
|
29
25
|
CAP = 3
|
|
@@ -33,15 +29,14 @@ module Rigor
|
|
|
33
29
|
# @param names [Array<Symbol>] the outer locals the body can rebind.
|
|
34
30
|
# @param seed_bindings [Hash{Symbol=>Type}] pre-state binding per name.
|
|
35
31
|
# @param widen [#call] value-pinned widener (Constant -> Nominal).
|
|
36
|
-
# @param evaluate_body [#call] `bindings -> exit_bindings` — evaluates
|
|
37
|
-
# the
|
|
38
|
-
# returns the per-name exit binding it produced.
|
|
32
|
+
# @param evaluate_body [#call] `bindings -> exit_bindings` — evaluates the body once from `bindings`
|
|
33
|
+
# (the per-name current assumption) and returns the per-name exit binding it produced.
|
|
39
34
|
# @return [Hash{Symbol=>Type}] the continuation binding per name.
|
|
40
35
|
def converge(names:, seed_bindings:, widen:, evaluate_body:)
|
|
41
36
|
return {} if names.empty?
|
|
42
37
|
|
|
43
|
-
# Running assumption per name; seeded with the pre-state binding,
|
|
44
|
-
#
|
|
38
|
+
# Running assumption per name; seeded with the pre-state binding, which stays a join constituent
|
|
39
|
+
# throughout (0-iteration soundness).
|
|
45
40
|
assumption = seed_bindings.dup
|
|
46
41
|
|
|
47
42
|
(0...CAP).each do |iteration|
|
|
@@ -54,14 +49,11 @@ module Rigor
|
|
|
54
49
|
next if exit_type.nil? # body did not write it this pass
|
|
55
50
|
|
|
56
51
|
if last_iteration
|
|
57
|
-
# On the final permitted iteration widen BOTH the running
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
# (
|
|
62
|
-
# converge without collapsing both sides first. If the join
|
|
63
|
-
# is still wider than the widened assumption (structural
|
|
64
|
-
# compounding, `a = [a]`), the local floors to `Dynamic[top]`.
|
|
52
|
+
# On the final permitted iteration widen BOTH the running assumption and the fresh exit type to
|
|
53
|
+
# their nominal bases before joining: Rigor's `union` keeps `Constant[1]` and `Nominal[Integer]`
|
|
54
|
+
# as distinct members, so an accumulator (`+=`/`*=`) producing a fresh constant per pass would
|
|
55
|
+
# never converge without collapsing both sides first. If the join is still wider than the
|
|
56
|
+
# widened assumption (structural compounding, `a = [a]`), the local floors to `Dynamic[top]`.
|
|
65
57
|
base = widen.call(assumption[name])
|
|
66
58
|
joined = Type::Combinator.union(base, widen.call(exit_type))
|
|
67
59
|
if joined == base
|
|
@@ -2,61 +2,46 @@
|
|
|
2
2
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module Inference
|
|
5
|
-
# Opt-in counters for the hard-coded inference cutoffs — the
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# `budgets:` table in docs/type-specification/inference-budgets.md
|
|
10
|
-
# is not yet wired); counting how often each fires on a real
|
|
5
|
+
# Opt-in counters for the hard-coded inference cutoffs — the "budget" guards that silently return
|
|
6
|
+
# `Dynamic[top]` / `nil` / a fallback bound rather than emitting a diagnostic. These are the *operative*
|
|
7
|
+
# cutoffs in the engine today (the configurable `budgets:` table in
|
|
8
|
+
# docs/type-specification/inference-budgets.md is not yet wired); counting how often each fires on a real
|
|
11
9
|
# project is the only way to see where inference actually stops.
|
|
12
10
|
#
|
|
13
11
|
# Three categories, one per guard site:
|
|
14
12
|
#
|
|
15
|
-
# - {RECURSION_GUARD} — `ExpressionTyper#infer_user_method_return`
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# - {
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
# (ADR-55 slice 1) exhausted its per-entry fuel and fell back to
|
|
25
|
-
# the plain `(receiver, method)` guard (in-cycle call → `Dynamic[top]`).
|
|
26
|
-
# - {RECURSION_FIXPOINT_CAP} — the fixpoint return-summary iteration
|
|
27
|
-
# (ADR-55 slice 2) hit its 3-evaluation cap without converging and
|
|
28
|
-
# collapsed the summary to `untyped` (today's behaviour).
|
|
13
|
+
# - {RECURSION_GUARD} — `ExpressionTyper#infer_user_method_return` detected a `(receiver, method)` cycle
|
|
14
|
+
# and returned `Dynamic[top]` (the de-facto recursion-depth budget, effective depth 1).
|
|
15
|
+
# - {ANCESTOR_WALK_LIMIT} — `resolve_user_def_through_ancestors` hit the 100-node BFS cap and gave up
|
|
16
|
+
# resolving the self-call.
|
|
17
|
+
# - {HKT_FUEL_EXHAUSTED} — `HktReducer` ran out of its reduction fuel budget and unwound to `app.bound`.
|
|
18
|
+
# - {RECURSION_UNROLL_FUEL} — the constant-arg recursion unroll (ADR-55 slice 1) exhausted its per-entry
|
|
19
|
+
# fuel and fell back to the plain `(receiver, method)` guard (in-cycle call → `Dynamic[top]`).
|
|
20
|
+
# - {RECURSION_FIXPOINT_CAP} — the fixpoint return-summary iteration (ADR-55 slice 2) hit its 3-evaluation
|
|
21
|
+
# cap without converging and collapsed the summary to `untyped` (today's behaviour).
|
|
29
22
|
#
|
|
30
|
-
# Enabled only when `RIGOR_BUDGET_TRACE` is set (to any non-empty
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
# immediately, so normal runs pay nothing.
|
|
23
|
+
# Enabled only when `RIGOR_BUDGET_TRACE` is set (to any non-empty value) in the environment, or via
|
|
24
|
+
# {enable!} in tests. When disabled, {hit} is a single boolean check and returns immediately, so normal
|
|
25
|
+
# runs pay nothing.
|
|
34
26
|
#
|
|
35
|
-
# Counters are process-global (Mutex-guarded) so they aggregate
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
# when collecting a trace.
|
|
27
|
+
# Counters are process-global (Mutex-guarded) so they aggregate across threads, but they do NOT cross
|
|
28
|
+
# `fork` boundaries — run `rigor check --workers 0` to keep all inference in one process when collecting a
|
|
29
|
+
# trace.
|
|
39
30
|
module BudgetTrace
|
|
40
31
|
RECURSION_GUARD = :recursion_guard
|
|
41
32
|
ANCESTOR_WALK_LIMIT = :ancestor_walk_limit
|
|
42
33
|
HKT_FUEL_EXHAUSTED = :hkt_fuel_exhausted
|
|
43
|
-
# `ExpressionTyper#infer_user_method_return` exhausted its
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
# call widened to `Dynamic[top]` exactly as it does without the
|
|
47
|
-
# unroll.
|
|
34
|
+
# `ExpressionTyper#infer_user_method_return` exhausted its constant-arg unroll fuel (ADR-55 slice 1) and
|
|
35
|
+
# fell back to the plain `(receiver, method)` recursion guard — i.e. the in-cycle call widened to
|
|
36
|
+
# `Dynamic[top]` exactly as it does without the unroll.
|
|
48
37
|
RECURSION_UNROLL_FUEL = :recursion_unroll_fuel
|
|
49
|
-
# `ExpressionTyper#infer_user_method_return` ran the fixpoint
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
# `untyped` — the in-cycle result widens to `Dynamic[top]` exactly
|
|
53
|
-
# as it does without the fixpoint.
|
|
38
|
+
# `ExpressionTyper#infer_user_method_return` ran the fixpoint return-summary iteration (ADR-55 slice 2)
|
|
39
|
+
# to its 3-evaluation cap without reaching convergence and collapsed the summary to `untyped` — the
|
|
40
|
+
# in-cycle result widens to `Dynamic[top]` exactly as it does without the fixpoint.
|
|
54
41
|
RECURSION_FIXPOINT_CAP = :recursion_fixpoint_cap
|
|
55
|
-
# `BodyFixpoint#converge` (ADR-56 slice A — non-escaping block
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
# `Dynamic[top]` (the escaping-block floor). Shared by slice B's
|
|
59
|
-
# loop-body fixpoint.
|
|
42
|
+
# `BodyFixpoint#converge` (ADR-56 slice A — non-escaping block captured-local write-back) ran its
|
|
43
|
+
# 3-evaluation cap without the written local's join converging and collapsed that local to
|
|
44
|
+
# `Dynamic[top]` (the escaping-block floor). Shared by slice B's loop-body fixpoint.
|
|
60
45
|
BLOCK_WRITEBACK_CAP = :block_writeback_cap
|
|
61
46
|
|
|
62
47
|
CATEGORIES = [
|
|
@@ -64,12 +49,10 @@ module Rigor
|
|
|
64
49
|
RECURSION_FIXPOINT_CAP, BLOCK_WRITEBACK_CAP
|
|
65
50
|
].freeze
|
|
66
51
|
|
|
67
|
-
# Distribution (histogram) categories — read-only observations of
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
# of every `Type::Union` that `Combinator.union` produces — the
|
|
72
|
-
# distribution the `union_size` budget default should be set from.
|
|
52
|
+
# Distribution (histogram) categories — read-only observations of a value's size at a site, used to
|
|
53
|
+
# choose budget defaults from an observed tail rather than a guess (ADR-41 WD3 / Slice 2a). No cap is
|
|
54
|
+
# enforced; these only record. `UNION_ARITY` is the member count of every `Type::Union` that
|
|
55
|
+
# `Combinator.union` produces — the distribution the `union_size` budget default should be set from.
|
|
73
56
|
UNION_ARITY = :union_arity
|
|
74
57
|
|
|
75
58
|
DISTRIBUTION_CATEGORIES = [UNION_ARITY].freeze
|
|
@@ -85,8 +68,8 @@ module Rigor
|
|
|
85
68
|
@enabled
|
|
86
69
|
end
|
|
87
70
|
|
|
88
|
-
# Test / programmatic toggles. Production enablement is the
|
|
89
|
-
#
|
|
71
|
+
# Test / programmatic toggles. Production enablement is the `RIGOR_BUDGET_TRACE` env var read once at
|
|
72
|
+
# load time.
|
|
90
73
|
def enable!
|
|
91
74
|
@enabled = true
|
|
92
75
|
end
|
|
@@ -95,24 +78,23 @@ module Rigor
|
|
|
95
78
|
@enabled = false
|
|
96
79
|
end
|
|
97
80
|
|
|
98
|
-
# Records one firing of `category`. No-op (one boolean check)
|
|
99
|
-
# when tracing is disabled.
|
|
81
|
+
# Records one firing of `category`. No-op (one boolean check) when tracing is disabled.
|
|
100
82
|
def hit(category)
|
|
101
83
|
return unless @enabled
|
|
102
84
|
|
|
103
85
|
@mutex.synchronize { @counts[category] += 1 }
|
|
104
86
|
end
|
|
105
87
|
|
|
106
|
-
# Frozen snapshot of the current counts, every known category
|
|
107
|
-
#
|
|
88
|
+
# Frozen snapshot of the current counts, every known category present (zero-filled) so consumers can
|
|
89
|
+
# render a stable table.
|
|
108
90
|
def snapshot
|
|
109
91
|
@mutex.synchronize do
|
|
110
92
|
CATEGORIES.to_h { |category| [category, @counts[category]] }.freeze
|
|
111
93
|
end
|
|
112
94
|
end
|
|
113
95
|
|
|
114
|
-
# Records one observation of `value` (an Integer size) into
|
|
115
|
-
#
|
|
96
|
+
# Records one observation of `value` (an Integer size) into `category`'s histogram. No-op (one boolean
|
|
97
|
+
# check) when disabled.
|
|
116
98
|
def observe(category, value)
|
|
117
99
|
return unless @enabled
|
|
118
100
|
|
|
@@ -124,10 +106,9 @@ module Rigor
|
|
|
124
106
|
@mutex.synchronize { @distributions[category].dup.freeze }
|
|
125
107
|
end
|
|
126
108
|
|
|
127
|
-
# Summary of a distribution category: total observation count, max
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
# nearest-rank method over the expanded sample.
|
|
109
|
+
# Summary of a distribution category: total observation count, max observed value, selected percentiles,
|
|
110
|
+
# and how many observations met or exceeded each threshold in `over`. Percentiles use the nearest-rank
|
|
111
|
+
# method over the expanded sample.
|
|
131
112
|
def summarize(category, over: [])
|
|
132
113
|
hist = distribution(category)
|
|
133
114
|
total = hist.values.sum
|
|
@@ -141,8 +122,7 @@ module Rigor
|
|
|
141
122
|
over: over.to_h { |t| [t, hist.sum { |value, n| value >= t ? n : 0 }] } }
|
|
142
123
|
end
|
|
143
124
|
|
|
144
|
-
# Nearest-rank percentile over a `{value => count}` histogram
|
|
145
|
-
# without materialising the full sample.
|
|
125
|
+
# Nearest-rank percentile over a `{value => count}` histogram without materialising the full sample.
|
|
146
126
|
def percentile(hist, total, fraction)
|
|
147
127
|
rank = (fraction * total).ceil
|
|
148
128
|
cumulative = 0
|
|
@@ -7,12 +7,10 @@ module Rigor
|
|
|
7
7
|
module Builtins
|
|
8
8
|
# `Array` catalog. Singleton — load once, consult during dispatch.
|
|
9
9
|
#
|
|
10
|
-
# Array has more mutation surface than String: every method that
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# recognise. The blocklist captures the methods we have
|
|
15
|
-
# specifically observed flowing as `:leaf` despite mutating.
|
|
10
|
+
# Array has more mutation surface than String: every method that logically reshapes the array tends
|
|
11
|
+
# to call `rb_ary_modify` or an internal helper (`ary_replace`, `ary_resize`, `ary_pop`,
|
|
12
|
+
# `ary_push_internal`, …) that the classifier does not yet recognise. The blocklist captures the
|
|
13
|
+
# methods we have specifically observed flowing as `:leaf` despite mutating.
|
|
16
14
|
ARRAY_CATALOG = MethodCatalog.for_topic(
|
|
17
15
|
"array",
|
|
18
16
|
mutating_selectors: {
|
|
@@ -7,12 +7,10 @@ module Rigor
|
|
|
7
7
|
module Builtins
|
|
8
8
|
# `Comparable` module catalog. Singleton — load once.
|
|
9
9
|
#
|
|
10
|
-
# `Comparable` is a Ruby module, not a class, so the
|
|
11
|
-
#
|
|
12
|
-
# `MethodDispatcher::ConstantFolding
|
|
13
|
-
# (
|
|
14
|
-
# The data is wired into `MODULE_CATALOGS` in
|
|
15
|
-
# `MethodDispatcher::ConstantFolding` (ancestor-chain lookup).
|
|
10
|
+
# `Comparable` is a Ruby module, not a class, so the catalog is NOT routed through
|
|
11
|
+
# `MethodDispatcher::ConstantFolding::CATALOG_BY_CLASS` (which dispatches on the receiver's concrete
|
|
12
|
+
# class). The data is wired into `MODULE_CATALOGS` in `MethodDispatcher::ConstantFolding`
|
|
13
|
+
# (ancestor-chain lookup).
|
|
16
14
|
COMPARABLE_CATALOG = MethodCatalog.for_topic(
|
|
17
15
|
"comparable",
|
|
18
16
|
mutating_selectors: {
|
|
@@ -5,30 +5,24 @@ require_relative "method_catalog"
|
|
|
5
5
|
module Rigor
|
|
6
6
|
module Inference
|
|
7
7
|
module Builtins
|
|
8
|
-
# `Complex` catalog. Singleton — load once, consult during
|
|
9
|
-
# dispatch.
|
|
8
|
+
# `Complex` catalog. Singleton — load once, consult during dispatch.
|
|
10
9
|
#
|
|
11
|
-
# `Complex` is a fully-immutable value type in Ruby: once a
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
# the conventional `:initialize_copy` defence-in-depth entry
|
|
21
|
-
# so a hypothetical future `Constant<Complex>` carrier cannot
|
|
22
|
-
# fold an aliasing copy through the catalog (mirrors
|
|
23
|
-
# `range_catalog.rb`, `time_catalog.rb`, `date_catalog.rb`).
|
|
10
|
+
# `Complex` is a fully-immutable value type in Ruby: once a complex number is constructed (via
|
|
11
|
+
# `Complex(real, imag)` or `Complex.rect` / `Complex.polar`) its `real` and `imag` slots are never
|
|
12
|
+
# rewritten. Every public instance method either returns `self` unchanged or builds a fresh
|
|
13
|
+
# `Complex` / `Numeric`. The C-body classifier already correctly flags the four `:dispatch` methods
|
|
14
|
+
# (`<=>`, `to_s`, `inspect`, `rationalize`) so there are no false-positive `:leaf` entries to
|
|
15
|
+
# override. The blocklist therefore carries only the conventional `:initialize_copy`
|
|
16
|
+
# defence-in-depth entry so a hypothetical future `Constant<Complex>` carrier cannot fold an
|
|
17
|
+
# aliasing copy through the catalog (mirrors `range_catalog.rb`, `time_catalog.rb`,
|
|
18
|
+
# `date_catalog.rb`).
|
|
24
19
|
COMPLEX_CATALOG = MethodCatalog.for_topic(
|
|
25
20
|
"complex",
|
|
26
21
|
mutating_selectors: {
|
|
27
22
|
"Complex" => Set[
|
|
28
|
-
# Defence in depth: `Complex` does not currently expose
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
# CRuby additions cannot leak a copy-mutator through.
|
|
23
|
+
# Defence in depth: `Complex` does not currently expose a public `initialize_copy`, but
|
|
24
|
+
# blocking it keeps the convention identical to every other catalog so future CRuby additions
|
|
25
|
+
# cannot leak a copy-mutator through.
|
|
32
26
|
:initialize_copy
|
|
33
27
|
]
|
|
34
28
|
}
|