rigortype 0.2.7 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/docs/manual/04-diagnostics.md +7 -4
- data/docs/manual/06-baseline.md +5 -2
- data/docs/manual/15-type-protection-coverage.md +6 -4
- data/lib/rigor/analysis/baseline.rb +56 -96
- data/lib/rigor/analysis/buffer_binding.rb +13 -20
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +29 -48
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +35 -54
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +23 -37
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +16 -23
- data/lib/rigor/analysis/check_rules/rule_walk.rb +48 -71
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +19 -28
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +48 -67
- data/lib/rigor/analysis/check_rules.rb +174 -274
- data/lib/rigor/analysis/dependency_recorder.rb +26 -38
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +13 -25
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +17 -30
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +15 -26
- data/lib/rigor/analysis/dependency_source_inference/index.rb +45 -76
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +23 -40
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +50 -83
- data/lib/rigor/analysis/dependency_source_inference.rb +6 -10
- data/lib/rigor/analysis/diagnostic.rb +48 -75
- data/lib/rigor/analysis/erb_template_detector.rb +13 -18
- data/lib/rigor/analysis/fact_store.rb +12 -22
- data/lib/rigor/analysis/incremental.rb +47 -63
- data/lib/rigor/analysis/incremental_session.rb +74 -100
- data/lib/rigor/analysis/project_scan.rb +12 -23
- data/lib/rigor/analysis/result.rb +3 -5
- data/lib/rigor/analysis/rule_catalog.rb +67 -95
- data/lib/rigor/analysis/run_stats.rb +38 -61
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +101 -160
- data/lib/rigor/analysis/runner/pool_coordinator.rb +121 -188
- data/lib/rigor/analysis/runner/project_pre_passes.rb +62 -96
- data/lib/rigor/analysis/runner/run_snapshots.rb +12 -18
- data/lib/rigor/analysis/runner.rb +220 -333
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +30 -43
- data/lib/rigor/analysis/worker_session.rb +64 -102
- data/lib/rigor/ast/type_node.rb +6 -8
- data/lib/rigor/ast.rb +6 -10
- data/lib/rigor/bleeding_edge.rb +27 -35
- data/lib/rigor/builtins/hkt_builtins.rb +7 -14
- data/lib/rigor/builtins/imported_refinements.rb +106 -149
- data/lib/rigor/builtins/predefined_constant_refinements.rb +51 -65
- data/lib/rigor/builtins/regex_refinement.rb +32 -43
- data/lib/rigor/builtins/static_return_refinements.rb +57 -81
- data/lib/rigor/cache/descriptor.rb +58 -94
- data/lib/rigor/cache/incremental_snapshot.rb +32 -48
- data/lib/rigor/cache/rbs_cache_producer.rb +10 -15
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +10 -16
- data/lib/rigor/cache/rbs_class_type_param_names.rb +10 -16
- data/lib/rigor/cache/rbs_constant_table.rb +8 -13
- data/lib/rigor/cache/rbs_descriptor.rb +8 -13
- data/lib/rigor/cache/rbs_environment.rb +11 -19
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +11 -19
- data/lib/rigor/cache/rbs_known_class_names.rb +8 -12
- data/lib/rigor/cache/store.rb +96 -151
- data/lib/rigor/cli/annotate_command.rb +51 -88
- data/lib/rigor/cli/baseline_command.rb +10 -17
- data/lib/rigor/cli/check_command.rb +121 -199
- data/lib/rigor/cli/check_runner_factory.rb +8 -14
- data/lib/rigor/cli/ci_detector.rb +9 -14
- data/lib/rigor/cli/command.rb +8 -14
- data/lib/rigor/cli/coverage_command.rb +19 -26
- data/lib/rigor/cli/coverage_mutation.rb +14 -19
- data/lib/rigor/cli/coverage_report.rb +0 -2
- data/lib/rigor/cli/coverage_scan.rb +7 -11
- data/lib/rigor/cli/diagnostic_formats.rb +43 -62
- data/lib/rigor/cli/diff_command.rb +10 -20
- data/lib/rigor/cli/docs_command.rb +26 -40
- data/lib/rigor/cli/doctor_command.rb +8 -9
- data/lib/rigor/cli/explain_command.rb +4 -7
- data/lib/rigor/cli/fused_protection_renderer.rb +4 -5
- data/lib/rigor/cli/fused_protection_report.rb +6 -8
- data/lib/rigor/cli/lsp_command.rb +11 -19
- data/lib/rigor/cli/mcp_command.rb +4 -6
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -4
- data/lib/rigor/cli/mutation_protection_report.rb +6 -9
- data/lib/rigor/cli/options.rb +11 -19
- data/lib/rigor/cli/plugin_command.rb +18 -30
- data/lib/rigor/cli/plugins_command.rb +29 -51
- data/lib/rigor/cli/plugins_renderer.rb +12 -20
- data/lib/rigor/cli/prism_colorizer.rb +13 -19
- data/lib/rigor/cli/protection_renderer.rb +5 -7
- data/lib/rigor/cli/protection_report.rb +27 -17
- data/lib/rigor/cli/renderable.rb +4 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +5 -8
- data/lib/rigor/cli/sig_gen_command.rb +14 -26
- data/lib/rigor/cli/skill_command.rb +30 -47
- data/lib/rigor/cli/skill_describe.rb +40 -64
- data/lib/rigor/cli/trace_command.rb +9 -16
- data/lib/rigor/cli/trace_renderer.rb +35 -51
- data/lib/rigor/cli/triage_command.rb +6 -10
- data/lib/rigor/cli/triage_renderer.rb +1 -2
- data/lib/rigor/cli/type_of_command.rb +14 -23
- data/lib/rigor/cli/type_of_renderer.rb +3 -5
- data/lib/rigor/cli/type_scan_command.rb +6 -10
- data/lib/rigor/cli/type_scan_renderer.rb +3 -5
- data/lib/rigor/cli/type_scan_report.rb +2 -3
- data/lib/rigor/cli/upgrade_command.rb +2 -3
- data/lib/rigor/cli.rb +16 -28
- data/lib/rigor/config_audit.rb +35 -44
- data/lib/rigor/configuration/dependencies.rb +41 -70
- data/lib/rigor/configuration/severity_profile.rb +28 -42
- data/lib/rigor/configuration.rb +150 -241
- data/lib/rigor/environment/bundle_sig_discovery.rb +74 -118
- data/lib/rigor/environment/class_registry.rb +19 -26
- data/lib/rigor/environment/constant_type_cache_holder.rb +14 -21
- data/lib/rigor/environment/hkt_registry_holder.rb +6 -12
- data/lib/rigor/environment/lockfile_resolver.rb +31 -47
- data/lib/rigor/environment/rbs_collection_discovery.rb +39 -63
- data/lib/rigor/environment/rbs_coverage_report.rb +24 -42
- data/lib/rigor/environment/rbs_loader.rb +420 -518
- data/lib/rigor/environment/reflection.rb +28 -53
- data/lib/rigor/environment/reporters.rb +13 -24
- data/lib/rigor/environment.rb +176 -291
- data/lib/rigor/flow_contribution/conflict.rb +16 -27
- data/lib/rigor/flow_contribution/element.rb +7 -12
- data/lib/rigor/flow_contribution/fact.rb +33 -57
- data/lib/rigor/flow_contribution/merge_result.rb +6 -9
- data/lib/rigor/flow_contribution/merger.rb +32 -47
- data/lib/rigor/flow_contribution.rb +37 -55
- data/lib/rigor/inference/acceptance.rb +133 -219
- data/lib/rigor/inference/block_parameter_binder.rb +52 -82
- data/lib/rigor/inference/body_fixpoint.rb +22 -30
- data/lib/rigor/inference/budget_trace.rb +43 -63
- data/lib/rigor/inference/builtins/array_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/comparable_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/complex_catalog.rb +13 -19
- data/lib/rigor/inference/builtins/date_catalog.rb +38 -67
- data/lib/rigor/inference/builtins/encoding_catalog.rb +22 -36
- data/lib/rigor/inference/builtins/enumerable_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/exception_catalog.rb +38 -57
- data/lib/rigor/inference/builtins/hash_catalog.rb +9 -14
- data/lib/rigor/inference/builtins/method_catalog.rb +29 -48
- data/lib/rigor/inference/builtins/numeric_catalog.rb +13 -18
- data/lib/rigor/inference/builtins/pathname_catalog.rb +8 -13
- data/lib/rigor/inference/builtins/proc_catalog.rb +38 -62
- data/lib/rigor/inference/builtins/random_catalog.rb +21 -32
- data/lib/rigor/inference/builtins/range_catalog.rb +12 -21
- data/lib/rigor/inference/builtins/rational_catalog.rb +9 -15
- data/lib/rigor/inference/builtins/re_catalog.rb +29 -48
- data/lib/rigor/inference/builtins/set_catalog.rb +18 -27
- data/lib/rigor/inference/builtins/string_catalog.rb +12 -19
- data/lib/rigor/inference/builtins/struct_catalog.rb +18 -30
- data/lib/rigor/inference/builtins/time_catalog.rb +28 -44
- data/lib/rigor/inference/closure_escape_analyzer.rb +44 -67
- data/lib/rigor/inference/coverage_scanner.rb +13 -18
- data/lib/rigor/inference/def_return_typer.rb +8 -14
- data/lib/rigor/inference/dynamic_origin.rb +23 -20
- data/lib/rigor/inference/expression_typer.rb +774 -1152
- data/lib/rigor/inference/fallback.rb +8 -12
- data/lib/rigor/inference/fallback_tracer.rb +4 -10
- data/lib/rigor/inference/flow_tracer.rb +25 -36
- data/lib/rigor/inference/hkt_body.rb +45 -68
- data/lib/rigor/inference/hkt_body_parser.rb +33 -52
- data/lib/rigor/inference/hkt_reducer.rb +38 -59
- data/lib/rigor/inference/hkt_registry.rb +50 -76
- data/lib/rigor/inference/indexed_narrowing.rb +55 -81
- data/lib/rigor/inference/macro_block_self_type.rb +21 -34
- data/lib/rigor/inference/method_dispatcher/array_to_h_folding.rb +11 -18
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +49 -83
- data/lib/rigor/inference/method_dispatcher/call_context.rb +16 -23
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +13 -22
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +307 -454
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +24 -34
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +22 -37
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +52 -81
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +51 -78
- data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +60 -97
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +23 -34
- data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +18 -26
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +37 -71
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +114 -182
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +122 -193
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +15 -24
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +64 -93
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +31 -45
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +6 -9
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +356 -511
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +22 -32
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +15 -21
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +66 -82
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +10 -15
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +9 -13
- data/lib/rigor/inference/method_dispatcher.rb +378 -524
- data/lib/rigor/inference/method_parameter_binder.rb +81 -114
- data/lib/rigor/inference/multi_target_binder.rb +51 -68
- data/lib/rigor/inference/mutation_widening.rb +115 -158
- data/lib/rigor/inference/narrowing.rb +509 -727
- data/lib/rigor/inference/origin_lookup.rb +38 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +100 -100
- data/lib/rigor/inference/precision_scanner.rb +26 -35
- data/lib/rigor/inference/project_patched_methods.rb +20 -32
- data/lib/rigor/inference/project_patched_scanner.rb +23 -37
- data/lib/rigor/inference/protection_scanner.rb +16 -18
- data/lib/rigor/inference/rbs_type_translator.rb +49 -66
- data/lib/rigor/inference/scope_indexer.rb +473 -817
- data/lib/rigor/inference/statement_evaluator.rb +706 -1111
- data/lib/rigor/inference/struct_fold_safety.rb +34 -48
- data/lib/rigor/inference/synthetic_method.rb +9 -16
- data/lib/rigor/inference/synthetic_method_index.rb +20 -35
- data/lib/rigor/inference/synthetic_method_scanner.rb +63 -103
- data/lib/rigor/language_server/buffer_resolution.rb +6 -9
- data/lib/rigor/language_server/buffer_table.rb +11 -18
- data/lib/rigor/language_server/completion_provider.rb +69 -116
- data/lib/rigor/language_server/debouncer.rb +16 -25
- data/lib/rigor/language_server/diagnostic_publisher.rb +26 -40
- data/lib/rigor/language_server/document_symbol_provider.rb +14 -19
- data/lib/rigor/language_server/folding_range_provider.rb +11 -19
- data/lib/rigor/language_server/hover_provider.rb +8 -11
- data/lib/rigor/language_server/hover_renderer.rb +56 -93
- data/lib/rigor/language_server/loop.rb +14 -19
- data/lib/rigor/language_server/project_context.rb +46 -74
- data/lib/rigor/language_server/selection_range_provider.rb +9 -12
- data/lib/rigor/language_server/server.rb +55 -83
- data/lib/rigor/language_server/signature_help_provider.rb +29 -46
- data/lib/rigor/language_server/synchronized_writer.rb +4 -7
- data/lib/rigor/language_server/uri.rb +8 -13
- data/lib/rigor/language_server.rb +4 -6
- data/lib/rigor/mcp/loop.rb +2 -3
- data/lib/rigor/mcp/server.rb +4 -7
- data/lib/rigor/mcp.rb +3 -6
- data/lib/rigor/plugin/access_denied_error.rb +5 -8
- data/lib/rigor/plugin/additional_initializer.rb +21 -31
- data/lib/rigor/plugin/base.rb +335 -518
- data/lib/rigor/plugin/blueprint.rb +14 -23
- data/lib/rigor/plugin/box.rb +18 -29
- data/lib/rigor/plugin/fact_store.rb +16 -26
- data/lib/rigor/plugin/inflector.rb +37 -53
- data/lib/rigor/plugin/io_boundary.rb +33 -56
- data/lib/rigor/plugin/isolation.rb +42 -55
- data/lib/rigor/plugin/load_error.rb +10 -15
- data/lib/rigor/plugin/loader.rb +30 -49
- data/lib/rigor/plugin/macro/block_as_method.rb +20 -32
- data/lib/rigor/plugin/macro/heredoc_template.rb +35 -58
- data/lib/rigor/plugin/macro/nested_class_template.rb +22 -36
- data/lib/rigor/plugin/macro/trait_registry.rb +34 -51
- data/lib/rigor/plugin/macro.rb +10 -15
- data/lib/rigor/plugin/manifest.rb +85 -144
- data/lib/rigor/plugin/node_context.rb +14 -22
- data/lib/rigor/plugin/node_rule_walk.rb +49 -74
- data/lib/rigor/plugin/protocol_contract.rb +25 -39
- data/lib/rigor/plugin/registry.rb +132 -205
- data/lib/rigor/plugin/services.rb +21 -33
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -19
- data/lib/rigor/plugin/trust_policy.rb +24 -38
- data/lib/rigor/plugin/type_node_resolver.rb +15 -24
- data/lib/rigor/protection/diagnostic_oracle.rb +11 -13
- data/lib/rigor/protection/mutation_scanner.rb +27 -35
- data/lib/rigor/protection/mutator.rb +50 -70
- data/lib/rigor/protection/test_suite_oracle.rb +20 -27
- data/lib/rigor/rbs_extended/conformance_checker.rb +48 -68
- data/lib/rigor/rbs_extended/hkt_directives.rb +28 -54
- data/lib/rigor/rbs_extended/reporter.rb +24 -40
- data/lib/rigor/rbs_extended.rb +107 -197
- data/lib/rigor/reflection.rb +68 -86
- data/lib/rigor/scope/discovery_index.rb +14 -19
- data/lib/rigor/scope.rb +255 -310
- data/lib/rigor/sig_gen/classification.rb +6 -10
- data/lib/rigor/sig_gen/generator.rb +197 -323
- data/lib/rigor/sig_gen/layout_index.rb +12 -20
- data/lib/rigor/sig_gen/method_candidate.rb +12 -17
- data/lib/rigor/sig_gen/observation_collector.rb +38 -70
- data/lib/rigor/sig_gen/observed_call.rb +13 -23
- data/lib/rigor/sig_gen/path_mapper.rb +17 -29
- data/lib/rigor/sig_gen/renderer.rb +7 -13
- data/lib/rigor/sig_gen/type_elaborator.rb +15 -28
- data/lib/rigor/sig_gen/write_result.rb +8 -16
- data/lib/rigor/sig_gen/writer.rb +95 -174
- data/lib/rigor/sig_gen.rb +3 -6
- data/lib/rigor/signature_path_audit.rb +24 -30
- data/lib/rigor/source/constant_path.rb +10 -14
- data/lib/rigor/source/literals.rb +31 -45
- data/lib/rigor/source/node_locator.rb +9 -11
- data/lib/rigor/source/node_walker.rb +9 -13
- data/lib/rigor/source.rb +3 -4
- data/lib/rigor/testing.rb +16 -20
- data/lib/rigor/triage/catalogue.rb +38 -62
- data/lib/rigor/triage.rb +31 -52
- data/lib/rigor/trinary.rb +9 -13
- data/lib/rigor/type/acceptance_router.rb +4 -6
- data/lib/rigor/type/accepts_result.rb +10 -14
- data/lib/rigor/type/app.rb +19 -27
- data/lib/rigor/type/bot.rb +4 -6
- data/lib/rigor/type/bound_method.rb +10 -15
- data/lib/rigor/type/combinator.rb +165 -257
- data/lib/rigor/type/constant.rb +23 -34
- data/lib/rigor/type/data_class.rb +10 -15
- data/lib/rigor/type/data_instance.rb +14 -20
- data/lib/rigor/type/difference.rb +21 -32
- data/lib/rigor/type/dynamic.rb +3 -5
- data/lib/rigor/type/hash_shape.rb +32 -18
- data/lib/rigor/type/integer_range.rb +11 -16
- data/lib/rigor/type/intersection.rb +27 -42
- data/lib/rigor/type/nominal.rb +10 -15
- data/lib/rigor/type/plain_lattice.rb +9 -13
- data/lib/rigor/type/refined.rb +67 -114
- data/lib/rigor/type/singleton.rb +4 -6
- data/lib/rigor/type/struct_class.rb +11 -16
- data/lib/rigor/type/struct_instance.rb +15 -21
- data/lib/rigor/type/tuple.rb +14 -19
- data/lib/rigor/type/union.rb +30 -42
- data/lib/rigor/type_node/generic.rb +14 -26
- data/lib/rigor/type_node/identifier.rb +12 -19
- data/lib/rigor/type_node.rb +3 -12
- data/lib/rigor/value_semantics.rb +16 -21
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +18 -30
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +16 -29
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +12 -23
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +22 -39
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +26 -46
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +51 -87
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +10 -17
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +26 -39
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +118 -176
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +30 -51
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +57 -92
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +71 -108
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +13 -22
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +15 -25
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +8 -13
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +16 -24
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +30 -47
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +79 -129
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +36 -57
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +27 -41
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +16 -24
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +148 -235
- data/plugins/rigor-activerecord/lib/rigor-activerecord.rb +3 -5
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +8 -14
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +9 -15
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +4 -7
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +23 -41
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +3 -5
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +6 -10
- data/plugins/rigor-activesupport-core-ext/lib/rigor-activesupport-core-ext.rb +8 -11
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +30 -45
- data/plugins/rigor-devise/lib/rigor-devise.rb +3 -5
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +43 -69
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +26 -35
- data/plugins/rigor-dry-schema/lib/rigor-dry-schema.rb +3 -5
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +30 -47
- data/plugins/rigor-dry-struct/lib/rigor-dry-struct.rb +3 -5
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +67 -113
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +29 -46
- data/plugins/rigor-dry-types/lib/rigor-dry-types.rb +3 -5
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -20
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +14 -23
- data/plugins/rigor-dry-validation/lib/rigor-dry-validation.rb +2 -3
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +22 -37
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +28 -46
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +13 -17
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +30 -47
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +52 -80
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +21 -31
- data/plugins/rigor-graphql/lib/rigor-graphql.rb +3 -5
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +11 -17
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +20 -34
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +45 -75
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +22 -36
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +22 -41
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +17 -32
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +6 -12
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +4 -8
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +17 -32
- data/plugins/rigor-rails/lib/rigor-rails.rb +11 -18
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +58 -92
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +20 -34
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +20 -31
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +49 -82
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +55 -92
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +51 -90
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +17 -30
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +38 -70
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +42 -74
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +280 -497
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +54 -89
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +32 -54
- data/plugins/rigor-rbs-inline/lib/rigor-rbs-inline.rb +8 -13
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +9 -16
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +24 -39
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +13 -22
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +34 -56
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +22 -42
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +43 -75
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +27 -39
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +14 -19
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +20 -34
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +28 -47
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +21 -37
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +13 -22
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +18 -31
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +6 -10
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +14 -29
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +23 -41
- data/plugins/rigor-sinatra/lib/rigor-sinatra.rb +3 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +23 -38
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +57 -96
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +22 -35
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +41 -69
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +6 -10
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +24 -41
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +29 -50
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +55 -88
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +159 -262
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +21 -29
- data/plugins/rigor-statesman/lib/rigor-statesman.rb +3 -5
- data/plugins/rigor-typescript-utility-types/lib/rigor/plugin/typescript_utility_types.rb +23 -37
- data/plugins/rigor-typescript-utility-types/lib/rigor-typescript-utility-types.rb +4 -6
- data/sig/rigor/scope.rbs +6 -0
- metadata +2 -1
|
@@ -4,24 +4,20 @@ require_relative "../type"
|
|
|
4
4
|
|
|
5
5
|
module Rigor
|
|
6
6
|
module Builtins
|
|
7
|
-
# Maps a curated table of canonical regex sub-patterns onto the
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# `
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
# § "v0.1.1 — Planned" Track 1 slice 1 for the binding scope of
|
|
14
|
-
# this recogniser.
|
|
7
|
+
# Maps a curated table of canonical regex sub-patterns onto the imported refinement
|
|
8
|
+
# carriers Rigor already ships (`decimal-int-string`, `hex-int-string`,
|
|
9
|
+
# `octal-int-string`, `lowercase-string`, `uppercase-string`, `numeric-string`). See
|
|
10
|
+
# `docs/type-specification/imported-built-in-types.md` for the registry the refinements
|
|
11
|
+
# come from and `docs/ROADMAP.md` § "v0.1.1 — Planned" Track 1 slice 1 for the binding
|
|
12
|
+
# scope of this recogniser.
|
|
15
13
|
#
|
|
16
|
-
# The intended consumer is `Inference::Narrowing.analyse_match_write`:
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# the
|
|
20
|
-
# `decimal-int-string` whenever the named-capture body matches
|
|
21
|
-
# one of the rows in {RULES}.
|
|
14
|
+
# The intended consumer is `Inference::Narrowing.analyse_match_write`: given `if
|
|
15
|
+
# /(?<year>\d+)/ =~ str; year; end`, the v0.1.0 baseline narrows `year` to plain `String`;
|
|
16
|
+
# v0.1.1 introspects the regex source and narrows further to `decimal-int-string`
|
|
17
|
+
# whenever the named-capture body matches one of the rows in {RULES}.
|
|
22
18
|
#
|
|
23
|
-
# Recognised body shapes (each row admits the `+` quantifier
|
|
24
|
-
#
|
|
19
|
+
# Recognised body shapes (each row admits the `+` quantifier and the bounded `{n}` /
|
|
20
|
+
# `{n,m}` forms with `n >= 1`):
|
|
25
21
|
#
|
|
26
22
|
# - `\d` -> decimal-int-string
|
|
27
23
|
# - `\h` -> hex-int-string
|
|
@@ -32,25 +28,20 @@ module Rigor
|
|
|
32
28
|
# - `[A-Z]` -> uppercase-string
|
|
33
29
|
# - `[[:digit:]]` -> numeric-string
|
|
34
30
|
#
|
|
35
|
-
# Anything outside the table returns `nil` so the calling
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
# set of canonical shapes rather than a general equivalence
|
|
40
|
-
# checker.
|
|
31
|
+
# Anything outside the table returns `nil` so the calling narrowing site falls back to
|
|
32
|
+
# its previous behaviour (plain `String`). Arbitrary regex semantic equivalence is
|
|
33
|
+
# undecidable, so the table is intentionally a small audited set of canonical shapes
|
|
34
|
+
# rather than a general equivalence checker.
|
|
41
35
|
module RegexRefinement
|
|
42
|
-
# `+` (one-or-more) or `{n}` / `{n,m}` (n >= 1, m >= n).
|
|
43
|
-
#
|
|
44
|
-
# {
|
|
45
|
-
# forms like `\d{0,5}` or `\d{5,3}` reject even though they
|
|
46
|
-
# parse syntactically.
|
|
36
|
+
# `+` (one-or-more) or `{n}` / `{n,m}` (n >= 1, m >= n). The bound check is enforced
|
|
37
|
+
# separately by {valid_bounds?} after the structural match succeeds, so forms like
|
|
38
|
+
# `\d{0,5}` or `\d{5,3}` reject even though they parse syntactically.
|
|
47
39
|
QUANTIFIER_SOURCE = '(?:\+|\{\d+(?:,\d+)?\})'
|
|
48
40
|
private_constant :QUANTIFIER_SOURCE
|
|
49
41
|
|
|
50
|
-
# ADR-15 Phase 4b.x — `Ractor.make_shareable` (not `.freeze`)
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
# A worker Ractor iterating `RULES.find { ... }` would trip
|
|
42
|
+
# ADR-15 Phase 4b.x — `Ractor.make_shareable` (not `.freeze`) because the outer Array
|
|
43
|
+
# contains two-element `[Regexp, Symbol]` rows whose inner Arrays are not frozen by the
|
|
44
|
+
# outer freeze. A worker Ractor iterating `RULES.find { ... }` would trip
|
|
54
45
|
# `Ractor::IsolationError` on the first row access.
|
|
55
46
|
RULES = Ractor.make_shareable([
|
|
56
47
|
[/\A\\d#{QUANTIFIER_SOURCE}\z/, :decimal_int_string],
|
|
@@ -70,13 +61,12 @@ module Rigor
|
|
|
70
61
|
|
|
71
62
|
module_function
|
|
72
63
|
|
|
73
|
-
# @param body [String, nil] a regex sub-pattern, typically the
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
#
|
|
78
|
-
#
|
|
79
|
-
# carrier, or `nil` if `body` is not a recognised shape.
|
|
64
|
+
# @param body [String, nil] a regex sub-pattern, typically the inner body of a
|
|
65
|
+
# `(?<name>body)` named capture. Anchors (`\A`, `\z`, `^`, `$`) are not stripped — the
|
|
66
|
+
# recogniser table targets bodies that the regex engine treats as anchored to the
|
|
67
|
+
# capture group bounds.
|
|
68
|
+
# @return [Rigor::Type, nil] the matching imported refinement carrier, or `nil` if
|
|
69
|
+
# `body` is not a recognised shape.
|
|
80
70
|
def for_capture_body(body)
|
|
81
71
|
return nil if body.nil? || body.empty?
|
|
82
72
|
|
|
@@ -87,11 +77,10 @@ module Rigor
|
|
|
87
77
|
Type::Combinator.public_send(rule.last)
|
|
88
78
|
end
|
|
89
79
|
|
|
90
|
-
# Filters the bounded-quantifier forms to ones whose lower
|
|
91
|
-
# bound is at least
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
# not a valid `decimal-int-string`.
|
|
80
|
+
# Filters the bounded-quantifier forms to ones whose lower bound is at least 1 and
|
|
81
|
+
# whose upper bound (if any) is at least the lower bound. Without this, `\d{0,5}` would
|
|
82
|
+
# be accepted even though it admits the empty string, which is not a valid
|
|
83
|
+
# `decimal-int-string`.
|
|
95
84
|
def valid_bounds?(body)
|
|
96
85
|
m = BOUND_RE.match(body)
|
|
97
86
|
return true if m.nil?
|
|
@@ -4,46 +4,35 @@ require_relative "../type"
|
|
|
4
4
|
|
|
5
5
|
module Rigor
|
|
6
6
|
module Builtins
|
|
7
|
-
# Static return-type refinements for stdlib (or other built-in)
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# (the RBS lives in the vendored `ruby/rbs` submodule).
|
|
7
|
+
# Static return-type refinements for stdlib (or other built-in) methods whose upstream
|
|
8
|
+
# RBS signature is broader than the method's documented behaviour and where adding a
|
|
9
|
+
# `%a{rigor:v1:return: ...}` annotation upstream is impractical (the RBS lives in the
|
|
10
|
+
# vendored `ruby/rbs` submodule).
|
|
12
11
|
#
|
|
13
|
-
# This tier sits in `MethodDispatcher.dispatch` between the
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
# table below, so the standard RBS path stays in charge of
|
|
19
|
-
# every other call.
|
|
12
|
+
# This tier sits in `MethodDispatcher.dispatch` between the HKT-builtin tier (which
|
|
13
|
+
# handles parametric / shape-bearing returns like `JSON.parse`'s `json::value`) and the
|
|
14
|
+
# RBS dispatch tier (the canonical lookup). It is consulted only when the method name and
|
|
15
|
+
# arg shape match an entry in the table below, so the standard RBS path stays in charge
|
|
16
|
+
# of every other call.
|
|
20
17
|
#
|
|
21
18
|
# Match policy:
|
|
22
19
|
#
|
|
23
|
-
# - Entries are keyed by `(owner_class_name, method_name,
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
# receiver
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
# on an instance).
|
|
34
|
-
# - `kind: :singleton` / `kind: :instance` restrict the
|
|
35
|
-
# match to one of the two shapes.
|
|
36
|
-
# - The handler is called with `(arg_types)` so future
|
|
37
|
-
# entries can refine based on argument types (e.g. a
|
|
38
|
-
# `File.expand_path(string)` entry that returns
|
|
20
|
+
# - Entries are keyed by `(owner_class_name, method_name, kind)`. `owner_class_name` is
|
|
21
|
+
# the class that *defines* the method (e.g., `"Kernel"`), not necessarily the
|
|
22
|
+
# receiver's static class — Kernel methods are mixed into every non-BasicObject class,
|
|
23
|
+
# so a `__dir__` call on any receiver routes here.
|
|
24
|
+
# - `kind: :both` matches both the singleton-receiver shape (`Kernel.__dir__`,
|
|
25
|
+
# `Singleton[Kernel]` receiver) AND the instance-receiver shape (an implicit-self call
|
|
26
|
+
# like `__dir__` inside any class body, or `obj.__dir__` on an instance).
|
|
27
|
+
# - `kind: :singleton` / `kind: :instance` restrict the match to one of the two shapes.
|
|
28
|
+
# - The handler is called with `(arg_types)` so future entries can refine based on
|
|
29
|
+
# argument types (e.g. a `File.expand_path(string)` entry that returns
|
|
39
30
|
# `non-empty-string` regardless of the upstream return).
|
|
40
31
|
#
|
|
41
|
-
# The override fires ABOVE RBS dispatch — if RBS would have
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
# original upstream shape, so downstream RBS-shaped observers
|
|
46
|
-
# see no difference.
|
|
32
|
+
# The override fires ABOVE RBS dispatch — if RBS would have returned a wider type
|
|
33
|
+
# (`String?` for `Kernel#__dir__`), the override returns the refined union
|
|
34
|
+
# (`non-empty-string | nil`) instead. RBS erasure of the refined return goes back to the
|
|
35
|
+
# original upstream shape, so downstream RBS-shaped observers see no difference.
|
|
47
36
|
module StaticReturnRefinements
|
|
48
37
|
# Pre-built carrier reused across calls so structural
|
|
49
38
|
# equality matches across analyzer invocations.
|
|
@@ -56,35 +45,29 @@ module Rigor
|
|
|
56
45
|
NON_EMPTY_STRING = Type::Combinator.non_empty_string.freeze
|
|
57
46
|
private_constant :NON_EMPTY_STRING
|
|
58
47
|
|
|
59
|
-
# `Kernel#__dir__` returns the canonical directory of the
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
# filesystem-canonical path — never the empty string — so
|
|
64
|
-
# `non-empty-string` is exact.
|
|
48
|
+
# `Kernel#__dir__` returns the canonical directory of the source file the call appears
|
|
49
|
+
# in, or `nil` when the file is invalid / not available (typically `-e` and similar
|
|
50
|
+
# one-liner contexts). When non-nil the value is always a filesystem-canonical path —
|
|
51
|
+
# never the empty string — so `non-empty-string` is exact.
|
|
65
52
|
KERNEL_DIR = ->(_arg_types) { NON_EMPTY_STRING_OR_NIL }
|
|
66
53
|
private_constant :KERNEL_DIR
|
|
67
54
|
|
|
68
|
-
# `File.expand_path(path, ?dir_string)` always returns an
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
# upstream RBS row is `(path file_name, ?path dir_string) ->
|
|
72
|
-
# String`; the refinement tightens to `non-empty-string`.
|
|
73
|
-
#
|
|
74
|
-
# `File.dirname(path, ?level)` always returns at least `"."`
|
|
75
|
-
# (or `"/"` for absolute roots), so the return is never the
|
|
76
|
-
# empty string. Upstream RBS returns `String`; the refinement
|
|
55
|
+
# `File.expand_path(path, ?dir_string)` always returns an absolute path string. Even
|
|
56
|
+
# `File.expand_path("")` expands to the current working directory's absolute path. The
|
|
57
|
+
# upstream RBS row is `(path file_name, ?path dir_string) -> String`; the refinement
|
|
77
58
|
# tightens to `non-empty-string`.
|
|
78
59
|
#
|
|
79
|
-
# `File.
|
|
80
|
-
#
|
|
81
|
-
#
|
|
60
|
+
# `File.dirname(path, ?level)` always returns at least `"."` (or `"/"` for absolute
|
|
61
|
+
# roots), so the return is never the empty string. Upstream RBS returns `String`; the
|
|
62
|
+
# refinement tightens to `non-empty-string`.
|
|
63
|
+
#
|
|
64
|
+
# `File.basename` is intentionally NOT refined: it returns `""` for
|
|
65
|
+
# `File.basename("")`, so `non-empty-string` would be unsound.
|
|
82
66
|
FILE_NON_EMPTY = ->(_arg_types) { NON_EMPTY_STRING }
|
|
83
67
|
private_constant :FILE_NON_EMPTY
|
|
84
68
|
|
|
85
|
-
# Frozen ((owner_class_name, method_name, kind) => handler)
|
|
86
|
-
#
|
|
87
|
-
# `:instance`. New entries SHOULD prefer `:both` unless the
|
|
69
|
+
# Frozen ((owner_class_name, method_name, kind) => handler) table. The kind tag is
|
|
70
|
+
# `:both`, `:singleton`, or `:instance`. New entries SHOULD prefer `:both` unless the
|
|
88
71
|
# singleton- and instance-side shapes genuinely differ.
|
|
89
72
|
OVERRIDES = {
|
|
90
73
|
["Kernel", :__dir__, :both] => KERNEL_DIR,
|
|
@@ -95,21 +78,17 @@ module Rigor
|
|
|
95
78
|
|
|
96
79
|
# Looks up a refined return type for the given call.
|
|
97
80
|
#
|
|
98
|
-
# @param owner_class_name [String, nil] the class on which
|
|
99
|
-
#
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
# `(method_name, kind)` against entries whose owner is
|
|
103
|
-
# currently in the table.
|
|
81
|
+
# @param owner_class_name [String, nil] the class on which the method is defined
|
|
82
|
+
# (e.g., `"Kernel"`). Pass `nil` when the caller hasn't resolved a defining owner
|
|
83
|
+
# yet — the lookup will then fall back to matching by `(method_name, kind)` against
|
|
84
|
+
# entries whose owner is currently in the table.
|
|
104
85
|
# @param method_name [Symbol]
|
|
105
|
-
# @param kind [Symbol] one of `:singleton`, `:instance`. The
|
|
106
|
-
#
|
|
107
|
-
#
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
# @return [Rigor::Type, nil] the refined return type, or
|
|
112
|
-
# `nil` when no override matches.
|
|
86
|
+
# @param kind [Symbol] one of `:singleton`, `:instance`. The caller passes the shape of
|
|
87
|
+
# the actual call site; the table stores `:both` for entries that match either.
|
|
88
|
+
# @param arg_types [Array<Rigor::Type>] positional argument types. Forwarded to the
|
|
89
|
+
# handler so future entries can discriminate on argument shape.
|
|
90
|
+
# @return [Rigor::Type, nil] the refined return type, or `nil` when no override
|
|
91
|
+
# matches.
|
|
113
92
|
def self.lookup(owner_class_name:, method_name:, kind:, arg_types: [])
|
|
114
93
|
return nil if owner_class_name.nil?
|
|
115
94
|
|
|
@@ -119,27 +98,24 @@ module Rigor
|
|
|
119
98
|
handler&.call(arg_types)
|
|
120
99
|
end
|
|
121
100
|
|
|
122
|
-
# Indexed view by `(method_name, kind)` — used by the
|
|
123
|
-
#
|
|
124
|
-
#
|
|
125
|
-
#
|
|
126
|
-
# and the index rebuild cost trivial, but precomputing keeps
|
|
127
|
-
# `dispatch`'s hot path free of an O(n) scan.
|
|
101
|
+
# Indexed view by `(method_name, kind)` — used by the dispatcher when the receiver's
|
|
102
|
+
# owner is not yet resolved but the method name alone uniquely identifies a stdlib
|
|
103
|
+
# override (today: `__dir__` → Kernel). The table is small and the index rebuild cost
|
|
104
|
+
# trivial, but precomputing keeps `dispatch`'s hot path free of an O(n) scan.
|
|
128
105
|
OWNERS_BY_METHOD = OVERRIDES.each_with_object({}) do |((owner, mname, _kind), _h), acc|
|
|
129
106
|
acc[mname] ||= []
|
|
130
107
|
acc[mname] << owner unless acc[mname].include?(owner)
|
|
131
108
|
end.freeze
|
|
132
109
|
private_constant :OWNERS_BY_METHOD
|
|
133
110
|
|
|
134
|
-
# @return [Array<String>] the candidate owner class names
|
|
135
|
-
#
|
|
136
|
-
# names this method.
|
|
111
|
+
# @return [Array<String>] the candidate owner class names for a bare method-name
|
|
112
|
+
# lookup. Empty when no override names this method.
|
|
137
113
|
NO_OWNERS = [].freeze
|
|
138
114
|
private_constant :NO_OWNERS
|
|
139
115
|
|
|
140
|
-
# Consulted on every dispatch (`try_static_refinement`); the miss
|
|
141
|
-
#
|
|
142
|
-
#
|
|
116
|
+
# Consulted on every dispatch (`try_static_refinement`); the miss case is overwhelmingly
|
|
117
|
+
# common, so share one frozen empty array instead of allocating a fresh `[]` per
|
|
118
|
+
# non-refined method.
|
|
143
119
|
def self.owners_for(method_name)
|
|
144
120
|
OWNERS_BY_METHOD[method_name.to_sym] || NO_OWNERS
|
|
145
121
|
end
|
|
@@ -8,40 +8,28 @@ module Rigor
|
|
|
8
8
|
module Cache
|
|
9
9
|
# Cache invalidation descriptor — the typed-slot schema fixed by
|
|
10
10
|
# [`docs/design/20260505-cache-slice-taxonomy.md`](../../../docs/design/20260505-cache-slice-taxonomy.md).
|
|
11
|
-
# Pure value object: no I/O, no global state, fully immutable
|
|
12
|
-
#
|
|
13
|
-
# ([`Rigor::Cache::Store`](store.rb), v0.0.8 slice 2) consumes
|
|
14
|
-
# descriptors but does not extend them.
|
|
11
|
+
# Pure value object: no I/O, no global state, fully immutable after construction. The storage layer
|
|
12
|
+
# ([`Rigor::Cache::Store`](store.rb), v0.0.8 slice 2) consumes descriptors but does not extend them.
|
|
15
13
|
#
|
|
16
|
-
# The descriptor has six slots (`files`, `gems`, `plugins`,
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# by key per slot; conflicts on the comparison fields raise
|
|
20
|
-
# {Conflict}.
|
|
14
|
+
# The descriptor has six slots (`files`, `gems`, `plugins`, `configs`, `dependencies`, `globs`); every slot is
|
|
15
|
+
# an array of typed entries; an empty array means "no dependency in this slot". Composition unions by key per
|
|
16
|
+
# slot; conflicts on the comparison fields raise {Conflict}.
|
|
21
17
|
#
|
|
22
|
-
# See ADR-2 § "Registration, Configuration, and Caching" for
|
|
23
|
-
#
|
|
24
|
-
# decisions that consume this schema.
|
|
18
|
+
# See ADR-2 § "Registration, Configuration, and Caching" for the design rationale and ADR-6 for the storage
|
|
19
|
+
# backend decisions that consume this schema.
|
|
25
20
|
class Descriptor
|
|
26
|
-
# Bumped on incompatible schema changes. The storage layer
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
# `
|
|
30
|
-
#
|
|
31
|
-
# `
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
# inert) MUST be rebuilt for the synthesis to take effect.
|
|
35
|
-
# v4: ADR-60 WD3 added the `globs` slot ({GlobEntry}) for the
|
|
36
|
-
# record-and-validate plugin-producer cache; the new slot
|
|
37
|
-
# changes `#to_canonical_hash` (and is Marshal-dumped inside
|
|
38
|
-
# `fetch_or_validate` entry pairs), so entries written by an
|
|
39
|
-
# older Rigor must read as misses.
|
|
21
|
+
# Bumped on incompatible schema changes. The storage layer mixes this into the cache key, so a bump
|
|
22
|
+
# implicitly invalidates every cached value. v2 added the `dependencies` slot for ADR-10 per-gem-version
|
|
23
|
+
# cache slice invalidation. v3: `RbsLoader.build_env_for` now synthesizes `module`s for namespaces a
|
|
24
|
+
# project's `signature_paths:` RBS references but never declares, so the marshalled RBS env cached by an
|
|
25
|
+
# older Rigor (which would leave those signatures inert) MUST be rebuilt for the synthesis to take effect.
|
|
26
|
+
# v4: ADR-60 WD3 added the `globs` slot ({GlobEntry}) for the record-and-validate plugin-producer cache;
|
|
27
|
+
# the new slot changes `#to_canonical_hash` (and is Marshal-dumped inside `fetch_or_validate` entry
|
|
28
|
+
# pairs), so entries written by an older Rigor must read as misses.
|
|
40
29
|
SCHEMA_VERSION = 4
|
|
41
30
|
|
|
42
|
-
# Per-slot entry value objects. Constructors validate enums /
|
|
43
|
-
#
|
|
44
|
-
# can mutate after the entry is in a Descriptor.
|
|
31
|
+
# Per-slot entry value objects. Constructors validate enums / required fields and freeze the resulting
|
|
32
|
+
# struct so no caller can mutate after the entry is in a Descriptor.
|
|
45
33
|
|
|
46
34
|
class FileEntry
|
|
47
35
|
include Rigor::ValueSemantics
|
|
@@ -125,20 +113,14 @@ module Rigor
|
|
|
125
113
|
end
|
|
126
114
|
end
|
|
127
115
|
|
|
128
|
-
# Per-(gem, version, mode) row carrying the cache slice
|
|
129
|
-
#
|
|
130
|
-
#
|
|
131
|
-
#
|
|
132
|
-
# fresh cache key — invalidating exactly that gem's slice
|
|
133
|
-
# without disturbing other gems' slices or the project's
|
|
134
|
-
# own cache.
|
|
116
|
+
# Per-(gem, version, mode) row carrying the cache slice boundary for ADR-10 dependency-source inference.
|
|
117
|
+
# A `bundle update` that bumps a listed gem's pinned version produces a different `gem_version` here and
|
|
118
|
+
# therefore a fresh cache key — invalidating exactly that gem's slice without disturbing other gems'
|
|
119
|
+
# slices or the project's own cache.
|
|
135
120
|
#
|
|
136
|
-
# `mode` mirrors the
|
|
137
|
-
#
|
|
138
|
-
#
|
|
139
|
-
# change for the same gem also forces invalidation because
|
|
140
|
-
# the inferred shapes depend on whether RBS overrides the
|
|
141
|
-
# walk.
|
|
121
|
+
# `mode` mirrors the [Configuration::Dependencies::VALID_MODES](../configuration/dependencies.rb) enum
|
|
122
|
+
# (`:disabled` / `:when_missing` / `:full`); a mode change for the same gem also forces invalidation
|
|
123
|
+
# because the inferred shapes depend on whether RBS overrides the walk.
|
|
142
124
|
class DependencyEntry
|
|
143
125
|
include Rigor::ValueSemantics
|
|
144
126
|
|
|
@@ -165,14 +147,11 @@ module Rigor
|
|
|
165
147
|
end
|
|
166
148
|
end
|
|
167
149
|
|
|
168
|
-
# ADR-60 WD3 — one glob's-worth of watched files, digested as a
|
|
169
|
-
#
|
|
170
|
-
#
|
|
171
|
-
#
|
|
172
|
-
#
|
|
173
|
-
# deleted file drops one, an edit changes one — all three move
|
|
174
|
-
# the digest. {Descriptor#fresh?} re-runs the same computation
|
|
175
|
-
# and compares.
|
|
150
|
+
# ADR-60 WD3 — one glob's-worth of watched files, digested as a single value so the entry covers content
|
|
151
|
+
# change, addition, AND removal in one row: the digest is the SHA-256 over the sorted
|
|
152
|
+
# `"<path>\0<sha256-of-content>\n"` rows of every file matching `File.join(root, pattern)`. A new file
|
|
153
|
+
# adds a row, a deleted file drops one, an edit changes one — all three move the digest.
|
|
154
|
+
# {Descriptor#fresh?} re-runs the same computation and compares.
|
|
176
155
|
class GlobEntry
|
|
177
156
|
include Rigor::ValueSemantics
|
|
178
157
|
|
|
@@ -192,13 +171,12 @@ module Rigor
|
|
|
192
171
|
new(root: root, pattern: pattern, value: digest_for(root: root, pattern: pattern))
|
|
193
172
|
end
|
|
194
173
|
|
|
195
|
-
# The digest the entry's `value` carries. Per-file read
|
|
196
|
-
#
|
|
197
|
-
#
|
|
198
|
-
# race posture as {Descriptor#file_entry_fresh?}.
|
|
174
|
+
# The digest the entry's `value` carries. Per-file read failures (a file vanishing between the glob and
|
|
175
|
+
# the digest) are treated as the file being absent — same race posture as
|
|
176
|
+
# {Descriptor#file_entry_fresh?}.
|
|
199
177
|
def self.digest_for(root:, pattern:)
|
|
200
|
-
# Dir.glob returns sorted entries by default (sort: true),
|
|
201
|
-
#
|
|
178
|
+
# Dir.glob returns sorted entries by default (sort: true), so the row order — and therefore the
|
|
179
|
+
# digest — is stable.
|
|
202
180
|
rows = Dir.glob(File.join(root, pattern)).filter_map do |path|
|
|
203
181
|
next nil unless File.file?(path)
|
|
204
182
|
|
|
@@ -209,9 +187,8 @@ module Rigor
|
|
|
209
187
|
Digest::SHA256.hexdigest(rows.join)
|
|
210
188
|
end
|
|
211
189
|
|
|
212
|
-
# Composition key — {.compose} unions per (root, pattern)
|
|
213
|
-
#
|
|
214
|
-
# the digest or {Conflict} is raised.
|
|
190
|
+
# Composition key — {.compose} unions per (root, pattern) slot; two contributions for the same slot
|
|
191
|
+
# must agree on the digest or {Conflict} is raised.
|
|
215
192
|
def slot_key
|
|
216
193
|
"#{root}\0#{pattern}"
|
|
217
194
|
end
|
|
@@ -221,11 +198,9 @@ module Rigor
|
|
|
221
198
|
end
|
|
222
199
|
end
|
|
223
200
|
|
|
224
|
-
# Raised when {.compose} encounters incompatible entries
|
|
225
|
-
#
|
|
226
|
-
#
|
|
227
|
-
# invalidating the cache slice rather than choosing one
|
|
228
|
-
# contribution silently.
|
|
201
|
+
# Raised when {.compose} encounters incompatible entries under the same key (file digest mismatch,
|
|
202
|
+
# gem-locked disagreement, …). Callers handle the exception by invalidating the cache slice rather than
|
|
203
|
+
# choosing one contribution silently.
|
|
229
204
|
class Conflict < StandardError; end
|
|
230
205
|
|
|
231
206
|
attr_reader :files, :gems, :plugins, :configs, :dependencies, :globs
|
|
@@ -240,17 +215,13 @@ module Rigor
|
|
|
240
215
|
freeze
|
|
241
216
|
end
|
|
242
217
|
|
|
243
|
-
# ADR-45 — re-validates this descriptor's recorded {FileEntry}s
|
|
244
|
-
#
|
|
245
|
-
#
|
|
246
|
-
#
|
|
247
|
-
#
|
|
248
|
-
#
|
|
249
|
-
#
|
|
250
|
-
# (it was built wrong for this use). ADR-60 WD3 adds `globs`
|
|
251
|
-
# alongside `files` as a re-validatable slot: a {GlobEntry} is
|
|
252
|
-
# fresh when re-globbing + re-digesting reproduces its recorded
|
|
253
|
-
# value.
|
|
218
|
+
# ADR-45 — re-validates this descriptor's recorded {FileEntry}s against the current filesystem. Used by
|
|
219
|
+
# the record-and-validate run-result cache: a value cached alongside its dependency descriptor is fresh
|
|
220
|
+
# iff every recorded file still matches. Only `files` are checked — non-file inputs (config / gems /
|
|
221
|
+
# version) belong in the cache *key*, not the validated dependency set — so a descriptor carrying any
|
|
222
|
+
# non-file slot is never considered fresh (it was built wrong for this use). ADR-60 WD3 adds `globs`
|
|
223
|
+
# alongside `files` as a re-validatable slot: a {GlobEntry} is fresh when re-globbing + re-digesting
|
|
224
|
+
# reproduces its recorded value.
|
|
254
225
|
def fresh?
|
|
255
226
|
return false unless gems.empty? && plugins.empty? && configs.empty? && dependencies.empty?
|
|
256
227
|
|
|
@@ -258,18 +229,15 @@ module Rigor
|
|
|
258
229
|
globs.all? { |entry| glob_entry_fresh?(entry) }
|
|
259
230
|
end
|
|
260
231
|
|
|
261
|
-
# File-comparator strictness ordering. `:digest` is strictest
|
|
262
|
-
#
|
|
263
|
-
#
|
|
264
|
-
# contributors disagree on the comparator for the same
|
|
265
|
-
# `path`, the stricter one wins.
|
|
232
|
+
# File-comparator strictness ordering. `:digest` is strictest (deterministic across machines); `:mtime`
|
|
233
|
+
# is cheaper but local; `:exists` is the weakest signal. When two contributors disagree on the
|
|
234
|
+
# comparator for the same `path`, the stricter one wins.
|
|
266
235
|
COMPARATOR_STRICTNESS = { digest: 2, mtime: 1, exists: 0 }.freeze
|
|
267
236
|
private_constant :COMPARATOR_STRICTNESS
|
|
268
237
|
|
|
269
|
-
# Composes any number of descriptors into a single descriptor
|
|
270
|
-
#
|
|
271
|
-
#
|
|
272
|
-
# value) collapse to a single entry.
|
|
238
|
+
# Composes any number of descriptors into a single descriptor whose slots are the union of the inputs'
|
|
239
|
+
# slots. Conflicts raise {Conflict}; idempotent contributions (same key, same value) collapse to a
|
|
240
|
+
# single entry.
|
|
273
241
|
def self.compose(*descriptors)
|
|
274
242
|
return new if descriptors.empty?
|
|
275
243
|
|
|
@@ -296,9 +264,8 @@ module Rigor
|
|
|
296
264
|
Digest::SHA256.hexdigest(JSON.generate(payload))
|
|
297
265
|
end
|
|
298
266
|
|
|
299
|
-
# Canonical UTF-8 JSON serialisation. Slots appear in
|
|
300
|
-
#
|
|
301
|
-
# so two equivalent descriptors produce identical bytes.
|
|
267
|
+
# Canonical UTF-8 JSON serialisation. Slots appear in lexicographic order; entries are sorted by their
|
|
268
|
+
# key field so two equivalent descriptors produce identical bytes.
|
|
302
269
|
def to_canonical_bytes
|
|
303
270
|
JSON.generate(to_canonical_hash).b
|
|
304
271
|
end
|
|
@@ -325,10 +292,8 @@ module Rigor
|
|
|
325
292
|
end
|
|
326
293
|
|
|
327
294
|
class << self
|
|
328
|
-
# Recursively coerces a Ruby value into a JSON-canonical
|
|
329
|
-
#
|
|
330
|
-
# preserve order; symbols stringify; everything else is
|
|
331
|
-
# JSON-renderable.
|
|
295
|
+
# Recursively coerces a Ruby value into a JSON-canonical structure: hash keys are stringified and
|
|
296
|
+
# sorted; arrays preserve order; symbols stringify; everything else is JSON-renderable.
|
|
332
297
|
def canonicalize_value(value)
|
|
333
298
|
case value
|
|
334
299
|
when Hash
|
|
@@ -360,9 +325,8 @@ module Rigor
|
|
|
360
325
|
false
|
|
361
326
|
end
|
|
362
327
|
|
|
363
|
-
# ADR-60 WD3 — re-runs the entry's glob + digest and compares
|
|
364
|
-
#
|
|
365
|
-
# (recompute), never a crash.
|
|
328
|
+
# ADR-60 WD3 — re-runs the entry's glob + digest and compares against the recorded value. Any failure
|
|
329
|
+
# reads as stale (recompute), never a crash.
|
|
366
330
|
def glob_entry_fresh?(entry)
|
|
367
331
|
GlobEntry.digest_for(root: entry.root, pattern: entry.pattern) == entry.value
|
|
368
332
|
rescue StandardError
|