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,40 +7,31 @@ require_relative "singleton_folding"
|
|
|
7
7
|
module Rigor
|
|
8
8
|
module Inference
|
|
9
9
|
module MethodDispatcher
|
|
10
|
-
# Folds `Shellwords` module-function calls on statically known
|
|
11
|
-
# string constants.
|
|
10
|
+
# Folds `Shellwords` module-function calls on statically known string constants.
|
|
12
11
|
#
|
|
13
|
-
# `Shellwords` is a pure, side-effect-free module whose functions
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# for `join`), the analyzer can evaluate the call at inference
|
|
18
|
-
# time and return the concrete `Constant<T>` result.
|
|
12
|
+
# `Shellwords` is a pure, side-effect-free module whose functions are deterministic over their
|
|
13
|
+
# inputs — the same string always produces the same escaped / split / joined result. When all
|
|
14
|
+
# relevant arguments are `Constant[String]` (or `Tuple` of them for `join`), the analyzer can
|
|
15
|
+
# evaluate the call at inference time and return the concrete `Constant<T>` result.
|
|
19
16
|
#
|
|
20
17
|
# === Supported methods
|
|
21
18
|
#
|
|
22
|
-
# * `escape` / `shellescape(str)` — returns `Constant[String]`.
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
# refinement will get it for free once that carrier is wired
|
|
26
|
-
# to scalar-constant returns.
|
|
19
|
+
# * `escape` / `shellescape(str)` — returns `Constant[String]`. `Shellwords.escape("")` → `"''"`, so
|
|
20
|
+
# the result is always non-empty; callers that care about the `non-empty-string` refinement will
|
|
21
|
+
# get it for free once that carrier is wired to scalar-constant returns.
|
|
27
22
|
#
|
|
28
|
-
# * `split` / `shellsplit` / `shellwords(line)` — splits the
|
|
29
|
-
#
|
|
30
|
-
# `
|
|
31
|
-
# unmatched quotes; the handler declines (returns `nil`) so
|
|
32
|
-
# the RBS tier widens gracefully.
|
|
23
|
+
# * `split` / `shellsplit` / `shellwords(line)` — splits the shell command line into tokens, returns
|
|
24
|
+
# `Tuple[Constant[String], …]`. Raises `ArgumentError` on unmatched quotes; the handler declines
|
|
25
|
+
# (returns `nil`) so the RBS tier widens gracefully.
|
|
33
26
|
#
|
|
34
|
-
# * `join` / `shelljoin(array)` — joins an array of tokens into
|
|
35
|
-
#
|
|
36
|
-
# is a `Constant[String]`; returns `Constant[String]`.
|
|
27
|
+
# * `join` / `shelljoin(array)` — joins an array of tokens into a shell-safe string. Requires a
|
|
28
|
+
# `Tuple` whose every element is a `Constant[String]`; returns `Constant[String]`.
|
|
37
29
|
#
|
|
38
30
|
# === Non-constant / unsupported cases
|
|
39
31
|
#
|
|
40
32
|
# Any call where:
|
|
41
33
|
# - the receiver is not `Singleton[Shellwords]`,
|
|
42
|
-
# - the required argument is not a `Constant[String]` (or
|
|
43
|
-
# `Tuple[Constant[String]…]` for `join`),
|
|
34
|
+
# - the required argument is not a `Constant[String]` (or `Tuple[Constant[String]…]` for `join`),
|
|
44
35
|
# - the method is not in the supported set, or
|
|
45
36
|
# - `Shellwords.split` raises on malformed input
|
|
46
37
|
#
|
|
@@ -53,9 +44,8 @@ module Rigor
|
|
|
53
44
|
SHELLWORDS_ESCAPE_METHODS | SHELLWORDS_SPLIT_METHODS | SHELLWORDS_JOIN_METHODS
|
|
54
45
|
).freeze
|
|
55
46
|
|
|
56
|
-
# Maximum number of tokens `split` may produce before we
|
|
57
|
-
#
|
|
58
|
-
# in ConstantFolding — keep the result Tuple manageable).
|
|
47
|
+
# Maximum number of tokens `split` may produce before we decline the fold (same rationale as
|
|
48
|
+
# STRING_ARRAY_LIFT_LIMIT in ConstantFolding — keep the result Tuple manageable).
|
|
59
49
|
SHELLWORDS_SPLIT_LIMIT = 64
|
|
60
50
|
|
|
61
51
|
private_constant :SHELLWORDS_ESCAPE_METHODS, :SHELLWORDS_SPLIT_METHODS,
|
|
@@ -91,8 +81,8 @@ module Rigor
|
|
|
91
81
|
Type::Combinator.constant_of(Shellwords.escape(str))
|
|
92
82
|
end
|
|
93
83
|
|
|
94
|
-
# `Shellwords.split(line)` / `.shellsplit` / `.shellwords` —
|
|
95
|
-
#
|
|
84
|
+
# `Shellwords.split(line)` / `.shellsplit` / `.shellwords` — one String arg, result lifted to
|
|
85
|
+
# Tuple[Constant[String]…].
|
|
96
86
|
def fold_split(args)
|
|
97
87
|
return nil unless args.size == 1
|
|
98
88
|
|
|
@@ -104,13 +94,13 @@ module Rigor
|
|
|
104
94
|
|
|
105
95
|
Type::Combinator.tuple_of(*tokens.map { |t| Type::Combinator.constant_of(t) })
|
|
106
96
|
rescue ArgumentError
|
|
107
|
-
# Unmatched quotes / invalid shell syntax — decline so the
|
|
108
|
-
#
|
|
97
|
+
# Unmatched quotes / invalid shell syntax — decline so the RBS tier returns the widened
|
|
98
|
+
# Array[String] envelope.
|
|
109
99
|
nil
|
|
110
100
|
end
|
|
111
101
|
|
|
112
|
-
# `Shellwords.join(array)` / `.shelljoin` — one Tuple arg
|
|
113
|
-
#
|
|
102
|
+
# `Shellwords.join(array)` / `.shelljoin` — one Tuple arg whose every element is
|
|
103
|
+
# `Constant[String]`.
|
|
114
104
|
def fold_join(args)
|
|
115
105
|
return nil unless args.size == 1
|
|
116
106
|
|
|
@@ -5,38 +5,32 @@ require_relative "../../type"
|
|
|
5
5
|
module Rigor
|
|
6
6
|
module Inference
|
|
7
7
|
module MethodDispatcher
|
|
8
|
-
# Shared helpers for the per-singleton folding tiers (CGI, URI,
|
|
9
|
-
#
|
|
8
|
+
# Shared helpers for the per-singleton folding tiers (CGI, URI, Shellwords, Math, Time, Regexp, Set,
|
|
9
|
+
# File, …).
|
|
10
10
|
#
|
|
11
|
-
# Each of those tiers folds a pure module-/class-function call on
|
|
12
|
-
#
|
|
13
|
-
# two questions:
|
|
11
|
+
# Each of those tiers folds a pure module-/class-function call on `Constant` receivers, and every one
|
|
12
|
+
# of them opens with the same two questions:
|
|
14
13
|
#
|
|
15
14
|
# 1. "Is the receiver the `Singleton[X]` I handle?" — a one-line
|
|
16
|
-
# `is_a?(Type::Singleton) && class_name == "X"` that used to be
|
|
17
|
-
#
|
|
18
|
-
# 2. "Is this argument a foldable `Constant[String]`?" — the
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
# on before evaluating the literal.
|
|
15
|
+
# `is_a?(Type::Singleton) && class_name == "X"` that used to be copied verbatim into nine
|
|
16
|
+
# modules.
|
|
17
|
+
# 2. "Is this argument a foldable `Constant[String]`?" — the `is_a?(Type::Constant) &&
|
|
18
|
+
# value.is_a?(String)` unwrap the string-folding tiers (CGI / URI / Shellwords / Regexp) gate on
|
|
19
|
+
# before evaluating the literal.
|
|
22
20
|
#
|
|
23
|
-
# Centralising both here keeps the receiver-shape and
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
# rather than nine. The per-tier fold bodies stay where they are —
|
|
27
|
-
# only the shared gate moves.
|
|
21
|
+
# Centralising both here keeps the receiver-shape and constant-unwrap knowledge in one place: if
|
|
22
|
+
# `Type::Singleton` or `Type::Constant` ever changes shape, there is a single edit rather than nine.
|
|
23
|
+
# The per-tier fold bodies stay where they are — only the shared gate moves.
|
|
28
24
|
module SingletonFolding
|
|
29
25
|
module_function
|
|
30
26
|
|
|
31
|
-
# True when `receiver` is the class/module singleton named
|
|
32
|
-
# `class_name` (e.g. `Singleton[Math]`).
|
|
27
|
+
# True when `receiver` is the class/module singleton named `class_name` (e.g. `Singleton[Math]`).
|
|
33
28
|
def receiver?(receiver, class_name)
|
|
34
29
|
receiver.is_a?(Type::Singleton) && receiver.class_name == class_name
|
|
35
30
|
end
|
|
36
31
|
|
|
37
|
-
# The `String` value carried by a `Constant[String]` argument, or
|
|
38
|
-
#
|
|
39
|
-
# and decline (return nil, deferring to the RBS tier) otherwise.
|
|
32
|
+
# The `String` value carried by a `Constant[String]` argument, or `nil` for any other carrier.
|
|
33
|
+
# Callers fold when this is non-nil and decline (return nil, deferring to the RBS tier) otherwise.
|
|
40
34
|
def constant_string(arg)
|
|
41
35
|
return nil unless arg.is_a?(Type::Constant)
|
|
42
36
|
|
|
@@ -7,38 +7,32 @@ require_relative "member_shape_projection"
|
|
|
7
7
|
module Rigor
|
|
8
8
|
module Inference
|
|
9
9
|
module MethodDispatcher
|
|
10
|
-
# ADR-48 Struct follow-up — `Struct.new` value folding, the mutable
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
# path does not need: **mutation soundness.**
|
|
10
|
+
# ADR-48 Struct follow-up — `Struct.new` value folding, the mutable sibling of {DataFolding}. Same
|
|
11
|
+
# fully-decidable-shape, degrade-on-any-uncertainty discipline, with one extra premise the immutable
|
|
12
|
+
# `Data` path does not need: **mutation soundness.**
|
|
14
13
|
#
|
|
15
|
-
# A `Struct` instance is mutable (`s.x = v`, `s[:x] = v`, escape), so a
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
# locals is the deferred slice 3 (relax the fresh-receiver gate to a
|
|
24
|
-
# fold-safe-local scan); precise mutated-member re-typing is slice 4.
|
|
14
|
+
# A `Struct` instance is mutable (`s.x = v`, `s[:x] = v`, escape), so a member map bound to a
|
|
15
|
+
# variable can be invalidated by a later write. This slice (ADR-48 slices 1+2 in the sound
|
|
16
|
+
# *transient* form) folds a member read ONLY off a **fresh** instance — the transient receiver of a
|
|
17
|
+
# `.new(...).x` / `.with(...).x` chain, which provably cannot have been mutated between
|
|
18
|
+
# materialisation and the read. A member read off a *stored* binding degrades to `Dynamic[top]`
|
|
19
|
+
# rather than fold a possibly-stale value. Promoting the fold to mutation-free bound locals is the
|
|
20
|
+
# deferred slice 3 (relax the fresh-receiver gate to a fold-safe-local scan); precise mutated-member
|
|
21
|
+
# re-typing is slice 4.
|
|
25
22
|
#
|
|
26
23
|
# Responsibilities:
|
|
27
24
|
#
|
|
28
|
-
# 1. `Struct.new(:x, :y [, keyword_init: <bool>])` on a
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
# projected type; member *setters* (`s.x = v`) return the assigned
|
|
40
|
-
# value type. Unhandled / stored-receiver calls return nil so the
|
|
41
|
-
# pipeline projects the instance to its nominal through RbsDispatch.
|
|
25
|
+
# 1. `Struct.new(:x, :y [, keyword_init: <bool>])` on a `Singleton[Struct]` receiver, literal-Symbol
|
|
26
|
+
# members, NO block -> `StructClass{members:, keyword_init:}`. A block / non-literal members
|
|
27
|
+
# defer.
|
|
28
|
+
# 2. `.new` / `.[]` on a `StructClass` (or a `Singleton[Point]` with a recorded struct layout) -> a
|
|
29
|
+
# `StructInstance`, the member map built from the call's arguments (positional or keyword per the
|
|
30
|
+
# class's `keyword_init`). A form / arity mismatch degrades to `Dynamic[top]` rather than a wrong
|
|
31
|
+
# member map. `.members` on the class folds.
|
|
32
|
+
# 3. member reads + `[]` / `to_h` / `deconstruct` / `deconstruct_keys` / `members` / `with` on a
|
|
33
|
+
# *fresh* `StructInstance` -> the precise projected type; member *setters* (`s.x = v`) return the
|
|
34
|
+
# assigned value type. Unhandled / stored-receiver calls return nil so the pipeline projects the
|
|
35
|
+
# instance to its nominal through RbsDispatch.
|
|
42
36
|
#
|
|
43
37
|
# See docs/adr/48-data-struct-value-folding.md § "Struct follow-up".
|
|
44
38
|
module StructFolding
|
|
@@ -64,12 +58,10 @@ module Rigor
|
|
|
64
58
|
end
|
|
65
59
|
end
|
|
66
60
|
|
|
67
|
-
# A `Struct.new`-defined class assigned to a constant (or a
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
# (`Scope#struct_member_layout`) rather than from the receiver
|
|
72
|
-
# carrier.
|
|
61
|
+
# A `Struct.new`-defined class assigned to a constant (or a `class Point < Struct.new(...)`
|
|
62
|
+
# subclass) is canonicalised by the engine to `Singleton[Point]`, not a `StructClass` — so its
|
|
63
|
+
# member layout is read from the project side-table the scope indexer built
|
|
64
|
+
# (`Scope#struct_member_layout`) rather than from the receiver carrier.
|
|
73
65
|
def fold_named_new(singleton, context)
|
|
74
66
|
scope = context.scope
|
|
75
67
|
return nil if scope.nil?
|
|
@@ -84,8 +76,8 @@ module Rigor
|
|
|
84
76
|
|
|
85
77
|
def fold_struct_new(context)
|
|
86
78
|
return nil unless context.method_name == :new
|
|
87
|
-
# Block-form (`Struct.new(:x) do ... end`) defers — the named
|
|
88
|
-
#
|
|
79
|
+
# Block-form (`Struct.new(:x) do ... end`) defers — the named constant/subclass forms still fold
|
|
80
|
+
# via the layout side-table.
|
|
89
81
|
return nil unless context.block_type.nil?
|
|
90
82
|
|
|
91
83
|
parsed = parse_struct_new_args(context.args)
|
|
@@ -94,10 +86,9 @@ module Rigor
|
|
|
94
86
|
Type::Combinator.struct_class_of(members: parsed[:members], keyword_init: parsed[:keyword_init])
|
|
95
87
|
end
|
|
96
88
|
|
|
97
|
-
# Parses `Struct.new`'s arguments into `{ members:, keyword_init: }`,
|
|
98
|
-
#
|
|
99
|
-
#
|
|
100
|
-
# class name and the trailing `keyword_init:` option hash.
|
|
89
|
+
# Parses `Struct.new`'s arguments into `{ members:, keyword_init: }`, or nil when any argument is
|
|
90
|
+
# non-conforming (a dynamic member name, an unexpected trailing keyword). Handles the optional
|
|
91
|
+
# leading String class name and the trailing `keyword_init:` option hash.
|
|
101
92
|
def parse_struct_new_args(args)
|
|
102
93
|
rest = args.dup
|
|
103
94
|
keyword_init = false
|
|
@@ -125,9 +116,9 @@ module Rigor
|
|
|
125
116
|
{ members: members, keyword_init: keyword_init }
|
|
126
117
|
end
|
|
127
118
|
|
|
128
|
-
# A trailing hash is the `Struct.new` options hash only when it is a
|
|
129
|
-
#
|
|
130
|
-
#
|
|
119
|
+
# A trailing hash is the `Struct.new` options hash only when it is a closed shape whose sole key is
|
|
120
|
+
# `:keyword_init`. Any other key means the call is not a recognisable member-list definition ->
|
|
121
|
+
# defer.
|
|
131
122
|
def struct_options_hash?(shape)
|
|
132
123
|
shape.closed? && shape.optional_keys.empty? &&
|
|
133
124
|
shape.pairs.keys.all? { |key| key == :keyword_init }
|
|
@@ -154,10 +145,9 @@ module Rigor
|
|
|
154
145
|
Type::Combinator.struct_instance_of(members: map, class_name: class_name)
|
|
155
146
|
end
|
|
156
147
|
|
|
157
|
-
# Builds the member -> type map honouring the class's `keyword_init`
|
|
158
|
-
#
|
|
159
|
-
# a
|
|
160
|
-
# is a different runtime shape, so it degrades rather than fold.
|
|
148
|
+
# Builds the member -> type map honouring the class's `keyword_init` flag: a `keyword_init: true`
|
|
149
|
+
# struct accepts only the keyword form, a positional struct only the positional form. The
|
|
150
|
+
# mismatched form is a different runtime shape, so it degrades rather than fold.
|
|
161
151
|
def member_map_for_new(members, keyword_init, context)
|
|
162
152
|
if keyword_new?(context)
|
|
163
153
|
keyword_init ? keyword_member_map(members, context.args) : nil
|
|
@@ -166,9 +156,9 @@ module Rigor
|
|
|
166
156
|
end
|
|
167
157
|
end
|
|
168
158
|
|
|
169
|
-
# `Point.new(x: 1)` arrives as a single trailing `HashShape` whose
|
|
170
|
-
#
|
|
171
|
-
#
|
|
159
|
+
# `Point.new(x: 1)` arrives as a single trailing `HashShape` whose call node is a
|
|
160
|
+
# `KeywordHashNode`; distinguishing it from a positional hash needs the call node (both type to a
|
|
161
|
+
# `HashShape`).
|
|
172
162
|
def keyword_new?(context)
|
|
173
163
|
node = context.call_node
|
|
174
164
|
return false if node.nil?
|
|
@@ -179,9 +169,9 @@ module Rigor
|
|
|
179
169
|
arguments.last.is_a?(Prism::KeywordHashNode)
|
|
180
170
|
end
|
|
181
171
|
|
|
182
|
-
# `Struct.new(:a, :b).new(v)` is legal — trailing members default to
|
|
183
|
-
#
|
|
184
|
-
#
|
|
172
|
+
# `Struct.new(:a, :b).new(v)` is legal — trailing members default to `nil` — so fewer positionals
|
|
173
|
+
# than members pad with `Constant[nil]`; more positionals than members is an ArgumentError ->
|
|
174
|
+
# degrade.
|
|
185
175
|
def positional_member_map(members, args)
|
|
186
176
|
return nil if args.size > members.size
|
|
187
177
|
|
|
@@ -190,9 +180,8 @@ module Rigor
|
|
|
190
180
|
end
|
|
191
181
|
end
|
|
192
182
|
|
|
193
|
-
# `Struct.new(:a, :b).new(a: 1)` is legal — omitted members default
|
|
194
|
-
#
|
|
195
|
-
# ArgumentError -> degrade.
|
|
183
|
+
# `Struct.new(:a, :b).new(a: 1)` is legal — omitted members default to `nil` — so a keyword subset
|
|
184
|
+
# pads the rest; an unknown key is an ArgumentError -> degrade.
|
|
196
185
|
def keyword_member_map(members, args)
|
|
197
186
|
return nil unless args.size == 1
|
|
198
187
|
|
|
@@ -204,9 +193,8 @@ module Rigor
|
|
|
204
193
|
members.to_h { |name| [name, shape.pairs[name] || Type::Combinator.constant_of(nil)] }
|
|
205
194
|
end
|
|
206
195
|
|
|
207
|
-
# A `.new` whose arguments cannot soundly populate the member map
|
|
208
|
-
#
|
|
209
|
-
# instances), never a wrong map.
|
|
196
|
+
# A `.new` whose arguments cannot soundly populate the member map degrades to `Dynamic[top]`
|
|
197
|
+
# (today's behaviour for `Struct.new(...)` instances), never a wrong map.
|
|
210
198
|
def degraded_instance
|
|
211
199
|
Type::Combinator.untyped
|
|
212
200
|
end
|
|
@@ -218,27 +206,24 @@ module Rigor
|
|
|
218
206
|
args = context.args
|
|
219
207
|
members = instance.members
|
|
220
208
|
|
|
221
|
-
# Member setter `s.x = v`: returns the assigned value type. Sound
|
|
222
|
-
#
|
|
223
|
-
#
|
|
224
|
-
# existence table does not register.
|
|
209
|
+
# Member setter `s.x = v`: returns the assigned value type. Sound regardless of mutation state
|
|
210
|
+
# (it models the setter's own return), and avoids a fall-through undefined-method on a writer
|
|
211
|
+
# the existence table does not register.
|
|
225
212
|
setter = member_setter_target(method_name, members)
|
|
226
213
|
return args.first if setter && args.size == 1
|
|
227
214
|
|
|
228
215
|
foldable = foldable_receiver?(context)
|
|
229
216
|
|
|
230
217
|
if members.key?(method_name) && args.empty? && !reader_overridden?(instance, method_name, context.scope)
|
|
231
|
-
# A stored receiver folds only when the bound local is proven
|
|
232
|
-
#
|
|
233
|
-
# otherwise the member value may be stale, so it degrades to
|
|
218
|
+
# A stored receiver folds only when the bound local is proven fold-safe (ADR-48 slice 3 — never
|
|
219
|
+
# mutated / aliased / escaped); otherwise the member value may be stale, so it degrades to
|
|
234
220
|
# `Dynamic[top]` (not nil -> no undefined-method fall-through).
|
|
235
221
|
return foldable ? members.fetch(method_name) : Type::Combinator.untyped
|
|
236
222
|
end
|
|
237
223
|
|
|
238
|
-
# Projections fold only off a foldable (fresh, or proven fold-safe)
|
|
239
|
-
#
|
|
240
|
-
#
|
|
241
|
-
# which is sound and non-regressive.
|
|
224
|
+
# Projections fold only off a foldable (fresh, or proven fold-safe) instance; off any other
|
|
225
|
+
# stored binding they defer to Struct's RBS (`to_h` / `[]` / `members` / `deconstruct*` all exist
|
|
226
|
+
# on `Struct`), which is sound and non-regressive.
|
|
242
227
|
return nil unless foldable
|
|
243
228
|
|
|
244
229
|
fold_fresh_projection(instance, method_name, args)
|
|
@@ -258,9 +243,9 @@ module Rigor
|
|
|
258
243
|
end
|
|
259
244
|
end
|
|
260
245
|
|
|
261
|
-
# The member name a `:<member>=` setter targets, or nil. Comparison
|
|
262
|
-
#
|
|
263
|
-
#
|
|
246
|
+
# The member name a `:<member>=` setter targets, or nil. Comparison operators (`==`, `>=`, ...) end
|
|
247
|
+
# with `=` too but never strip to a member symbol, so they fall through to the normal dispatch
|
|
248
|
+
# path.
|
|
264
249
|
def member_setter_target(method_name, members)
|
|
265
250
|
name = method_name.to_s
|
|
266
251
|
return nil unless name.end_with?("=")
|
|
@@ -269,17 +254,16 @@ module Rigor
|
|
|
269
254
|
members.key?(base) ? base : nil
|
|
270
255
|
end
|
|
271
256
|
|
|
272
|
-
# A member read is foldable when the receiver is either FRESH (the
|
|
273
|
-
#
|
|
274
|
-
#
|
|
275
|
-
#
|
|
276
|
-
# the binding is never mutated / aliased / escaped in its scope).
|
|
257
|
+
# A member read is foldable when the receiver is either FRESH (the transient result of a
|
|
258
|
+
# `.new(...)`/`.with(...)` chain, which cannot have been mutated between materialisation and this
|
|
259
|
+
# read) or a FOLD-SAFE stored local (ADR-48 slice 3 — `StructFoldSafety` proved the binding is
|
|
260
|
+
# never mutated / aliased / escaped in its scope).
|
|
277
261
|
def foldable_receiver?(context)
|
|
278
262
|
fresh_receiver?(context) || fold_safe_local_receiver?(context)
|
|
279
263
|
end
|
|
280
264
|
|
|
281
|
-
# A fresh receiver is the transient result of a chained call
|
|
282
|
-
#
|
|
265
|
+
# A fresh receiver is the transient result of a chained call (`Point.new(1, 2).x`,
|
|
266
|
+
# `inst.with(x: 9).y`).
|
|
283
267
|
def fresh_receiver?(context)
|
|
284
268
|
node = context.call_node
|
|
285
269
|
return false if node.nil?
|
|
@@ -287,8 +271,8 @@ module Rigor
|
|
|
287
271
|
node.receiver.is_a?(Prism::CallNode)
|
|
288
272
|
end
|
|
289
273
|
|
|
290
|
-
# A fold-safe stored receiver is a local-variable read whose name the
|
|
291
|
-
#
|
|
274
|
+
# A fold-safe stored receiver is a local-variable read whose name the body's fold-safe set (on the
|
|
275
|
+
# scope) marks as never mutated.
|
|
292
276
|
def fold_safe_local_receiver?(context)
|
|
293
277
|
node = context.call_node
|
|
294
278
|
receiver = node&.receiver
|
|
@@ -6,28 +6,23 @@ require_relative "singleton_folding"
|
|
|
6
6
|
module Rigor
|
|
7
7
|
module Inference
|
|
8
8
|
module MethodDispatcher
|
|
9
|
-
# Folds the zone-pinned `Time` class constructors on statically
|
|
10
|
-
# known arguments.
|
|
9
|
+
# Folds the zone-pinned `Time` class constructors on statically known arguments.
|
|
11
10
|
#
|
|
12
|
-
# Only `Time.utc` / `Time.gm` are folded. They pin the result
|
|
13
|
-
#
|
|
14
|
-
# (`year`, `hour`, `utc_offset`, `strftime`, …) yields the same
|
|
11
|
+
# Only `Time.utc` / `Time.gm` are folded. They pin the result to UTC, so the literal is
|
|
12
|
+
# machine-independent — every reader (`year`, `hour`, `utc_offset`, `strftime`, …) yields the same
|
|
15
13
|
# answer on any analysis host.
|
|
16
14
|
#
|
|
17
|
-
# `Time.now` (non-deterministic), `Time.at` / `Time.local` /
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# is blocklisted in `TIME_CATALOG` so a folded `Constant[Time]`
|
|
23
|
-
# cannot produce a machine-dependent local-zone copy.
|
|
15
|
+
# `Time.now` (non-deterministic), `Time.at` / `Time.local` / `Time.mktime` / `Time.new` (local-zone —
|
|
16
|
+
# the result's wall clock and `utc_offset` depend on the analysis machine's timezone) are
|
|
17
|
+
# deliberately NOT folded; they keep their `Nominal[Time]` RBS answer. For the same reason
|
|
18
|
+
# `Time#getlocal` is blocklisted in `TIME_CATALOG` so a folded `Constant[Time]` cannot produce a
|
|
19
|
+
# machine-dependent local-zone copy.
|
|
24
20
|
module TimeFolding
|
|
25
21
|
TIME_UTC_METHODS = Set[:utc, :gm].freeze
|
|
26
22
|
private_constant :TIME_UTC_METHODS
|
|
27
23
|
|
|
28
|
-
# `Time.utc` accepts the 1–7 positional (year-first) form and
|
|
29
|
-
# a
|
|
30
|
-
# call declines cheaply rather than reaching the constructor.
|
|
24
|
+
# `Time.utc` accepts the 1–7 positional (year-first) form and a 10-arg (sec-first) form; cap the
|
|
25
|
+
# arity so a malformed call declines cheaply rather than reaching the constructor.
|
|
31
26
|
MAX_TIME_ARITY = 10
|
|
32
27
|
private_constant :MAX_TIME_ARITY
|
|
33
28
|
|
|
@@ -7,23 +7,19 @@ require_relative "singleton_folding"
|
|
|
7
7
|
module Rigor
|
|
8
8
|
module Inference
|
|
9
9
|
module MethodDispatcher
|
|
10
|
-
# Folds `URI` module-function calls on statically known
|
|
11
|
-
# string constants.
|
|
10
|
+
# Folds `URI` module-function calls on statically known string constants.
|
|
12
11
|
#
|
|
13
|
-
# `URI.encode_www_form_component` / `decode_www_form_component`
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# evaluate the call at inference time and return the concrete
|
|
18
|
-
# `Constant[String]` result.
|
|
12
|
+
# `URI.encode_www_form_component` / `decode_www_form_component` and the newer `encode_uri_component`
|
|
13
|
+
# / `decode_uri_component` are pure, deterministic functions over their string inputs. When the
|
|
14
|
+
# argument is a `Constant[String]`, the analyzer can evaluate the call at inference time and return
|
|
15
|
+
# the concrete `Constant[String]` result.
|
|
19
16
|
#
|
|
20
17
|
# === Supported methods
|
|
21
18
|
#
|
|
22
|
-
# * `encode_www_form_component(str)` / `decode_www_form_component(str)` —
|
|
23
|
-
#
|
|
24
|
-
# * `encode_uri_component(str)` / `decode_uri_component(str)` —
|
|
25
|
-
#
|
|
26
|
-
# (Ruby 3.2+). Returns `Constant[String]`.
|
|
19
|
+
# * `encode_www_form_component(str)` / `decode_www_form_component(str)` — RFC 3986 percent-encode /
|
|
20
|
+
# decode. Returns `Constant[String]`.
|
|
21
|
+
# * `encode_uri_component(str)` / `decode_uri_component(str)` — Same encoding but may preserve
|
|
22
|
+
# additional reserved chars (Ruby 3.2+). Returns `Constant[String]`.
|
|
27
23
|
#
|
|
28
24
|
# === Non-constant / unsupported cases
|
|
29
25
|
#
|