rigortype 0.4.6 → 0.4.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 +4 -2
- data/data/core_overlay/bigdecimal.rbs +33 -0
- data/data/core_overlay/resolv.rbs +30 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +45 -13
- data/data/gem_overlay/addressable/addressable.rbs +40 -0
- data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
- data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
- data/docs/handbook/01-getting-started.md +6 -3
- data/docs/handbook/11-sig-gen.md +85 -3
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/install.md +20 -0
- data/docs/manual/01-installation.md +67 -26
- data/docs/manual/02-cli-reference.md +455 -113
- data/docs/manual/03-configuration.md +76 -5
- data/docs/manual/04-diagnostics.md +5 -4
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/08-skills.md +11 -0
- data/docs/manual/09-editor-integration.md +21 -2
- data/docs/manual/10-mcp-server.md +38 -2
- data/docs/manual/12-caching.md +4 -3
- data/docs/manual/14-rails-quickstart.md +1 -1
- data/docs/manual/19-effect-labels.md +3 -4
- data/docs/manual/plugins/rigor-actionpack.md +32 -15
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +23 -1
- data/docs/manual/plugins/rigor-sorbet.md +17 -3
- data/lib/rigor/analysis/baseline.rb +24 -27
- data/lib/rigor/analysis/check_rules/always_raises_shield.rb +147 -0
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
- data/lib/rigor/analysis/check_rules/call_facts.rb +58 -0
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +12 -4
- data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +85 -13
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +16 -4
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +6 -1
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
- data/lib/rigor/analysis/check_rules/plugin_api_surface.rb +195 -0
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +41 -12
- data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +27 -1
- data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +3 -3
- data/lib/rigor/analysis/check_rules/replaced_singleton_defs.rb +96 -0
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- data/lib/rigor/analysis/check_rules/rule_ids.rb +9 -2
- data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules.rb +720 -141
- data/lib/rigor/analysis/crash_signature.rb +45 -3
- data/lib/rigor/analysis/dependency_recorder.rb +237 -80
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/edit_replay.rb +93 -6
- data/lib/rigor/analysis/fact_store.rb +3 -3
- data/lib/rigor/analysis/incremental.rb +11 -1
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +4 -3
- data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
- data/lib/rigor/analysis/incremental_session.rb +306 -53
- data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -3
- data/lib/rigor/analysis/rule_catalog.rb +39 -3
- data/lib/rigor/analysis/run_cache_key.rb +27 -3
- data/lib/rigor/analysis/run_disclosure_credit.rb +26 -6
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -2
- data/lib/rigor/analysis/runner/declaration_position.rb +8 -8
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +264 -112
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +4 -9
- data/lib/rigor/analysis/runner/pool_coordinator.rb +173 -328
- data/lib/rigor/analysis/runner/project_pre_passes.rb +4 -37
- data/lib/rigor/analysis/runner/run_level_facts.rb +304 -0
- data/lib/rigor/analysis/runner/run_snapshots.rb +44 -54
- data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
- data/lib/rigor/analysis/runner.rb +298 -109
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +62 -23
- data/lib/rigor/analysis/template_unit_positions.rb +2 -3
- data/lib/rigor/analysis/template_units.rb +9 -1
- data/lib/rigor/analysis/worker_session.rb +65 -71
- data/lib/rigor/bleeding_edge.rb +22 -10
- data/lib/rigor/builtins/imported_refinements.rb +6 -8
- data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
- data/lib/rigor/builtins/regex_refinement.rb +4 -6
- data/lib/rigor/cache/descriptor.rb +6 -5
- data/lib/rigor/cache/file_digest.rb +39 -31
- data/lib/rigor/cache/incremental_snapshot.rb +95 -17
- data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
- data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
- data/lib/rigor/cache/rbs_constant_table.rb +1 -1
- data/lib/rigor/cache/rbs_descriptor.rb +90 -4
- data/lib/rigor/cache/rbs_environment.rb +8 -1
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
- data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
- data/lib/rigor/cache/store.rb +11 -10
- data/lib/rigor/ci_detector.rb +1 -1
- data/lib/rigor/cli/annotate_command.rb +24 -19
- data/lib/rigor/cli/baseline_command.rb +160 -57
- data/lib/rigor/cli/catalogue.rb +256 -0
- data/lib/rigor/cli/check_command.rb +112 -104
- data/lib/rigor/cli/check_runner_factory.rb +2 -2
- data/lib/rigor/cli/command.rb +15 -1
- data/lib/rigor/cli/coverage_command.rb +21 -8
- data/lib/rigor/cli/coverage_mutation.rb +5 -7
- data/lib/rigor/cli/coverage_renderer.rb +2 -1
- data/lib/rigor/cli/coverage_scan.rb +3 -5
- data/lib/rigor/cli/diff_command.rb +25 -20
- data/lib/rigor/cli/docs_command.rb +1 -2
- data/lib/rigor/cli/doctor_command.rb +12 -12
- data/lib/rigor/cli/effects_command.rb +50 -40
- data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
- data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
- data/lib/rigor/cli/effects_renderer.rb +2 -1
- data/lib/rigor/cli/effects_snapshot_command.rb +26 -18
- data/lib/rigor/cli/explain_command.rb +18 -8
- data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
- data/lib/rigor/cli/json_document.rb +41 -0
- data/lib/rigor/cli/lens_command.rb +29 -10
- data/lib/rigor/cli/lsp_command.rb +250 -23
- data/lib/rigor/cli/mcp_command.rb +12 -7
- data/lib/rigor/cli/mutation_fork_scan.rb +1 -1
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -2
- data/lib/rigor/cli/options.rb +40 -1
- data/lib/rigor/cli/plugin_command.rb +93 -46
- data/lib/rigor/cli/plugins_command.rb +17 -18
- data/lib/rigor/cli/plugins_renderer.rb +10 -6
- data/lib/rigor/cli/protection_fork_scan.rb +2 -2
- data/lib/rigor/cli/protection_renderer.rb +2 -1
- data/lib/rigor/cli/show_bleedingedge_command.rb +21 -15
- data/lib/rigor/cli/sig_gen_command.rb +92 -27
- data/lib/rigor/cli/skill_command.rb +32 -3
- data/lib/rigor/cli/skill_deep_probe.rb +1 -1
- data/lib/rigor/cli/skill_describe.rb +2 -1
- data/lib/rigor/cli/trace_command.rb +14 -9
- data/lib/rigor/cli/triage_command.rb +10 -11
- data/lib/rigor/cli/triage_renderer.rb +2 -1
- data/lib/rigor/cli/type_of_command.rb +16 -11
- data/lib/rigor/cli/type_of_project_scope.rb +1 -4
- data/lib/rigor/cli/type_of_renderer.rb +4 -1
- data/lib/rigor/cli/type_scan_command.rb +14 -9
- data/lib/rigor/cli/type_scan_renderer.rb +2 -1
- data/lib/rigor/cli/unused_command.rb +27 -17
- data/lib/rigor/cli/upgrade_command.rb +5 -0
- data/lib/rigor/cli.rb +214 -56
- data/lib/rigor/config_audit.rb +33 -19
- data/lib/rigor/configuration/severity_profile.rb +14 -5
- data/lib/rigor/configuration.rb +104 -35
- data/lib/rigor/effects/collector.rb +53 -21
- data/lib/rigor/effects/definition_context.rb +0 -2
- data/lib/rigor/effects/envelope.rb +1 -1
- data/lib/rigor/effects/framework_units.rb +1 -1
- data/lib/rigor/effects/mutation_classifier.rb +4 -7
- data/lib/rigor/effects/propagator.rb +1 -1
- data/lib/rigor/effects/registry.rb +2 -2
- data/lib/rigor/effects/snapshot.rb +1 -1
- data/lib/rigor/effects/taint_cause.rb +0 -2
- data/lib/rigor/effects/unit_scan.rb +9 -13
- data/lib/rigor/environment/bundle_sig_discovery.rb +64 -33
- data/lib/rigor/environment/class_registry.rb +3 -3
- data/lib/rigor/environment/constant_type_cache_holder.rb +5 -0
- data/lib/rigor/environment/core_root.rb +77 -0
- data/lib/rigor/environment/default_libraries.rb +18 -1
- data/lib/rigor/environment/deferred_signature_plan.rb +381 -0
- data/lib/rigor/environment/gated_signature_guard.rb +1 -1
- data/lib/rigor/environment/gated_signature_plan.rb +5 -2
- data/lib/rigor/environment/hkt_registry_holder.rb +6 -0
- data/lib/rigor/environment/lockfile_resolver.rb +28 -12
- data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
- data/lib/rigor/environment/member_consistency.rb +56 -7
- data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
- data/lib/rigor/environment/project_path_gems.rb +124 -0
- data/lib/rigor/environment/rbs_collection_discovery.rb +123 -8
- data/lib/rigor/environment/rbs_coverage_report.rb +12 -11
- data/lib/rigor/environment/rbs_hierarchy.rb +12 -15
- data/lib/rigor/environment/rbs_loader.rb +772 -110
- data/lib/rigor/environment/reflection.rb +7 -7
- data/lib/rigor/environment/required_features.rb +0 -3
- data/lib/rigor/environment/ruby_library_roots.rb +1 -1
- data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
- data/lib/rigor/environment.rb +169 -42
- data/lib/rigor/flow_contribution/merger.rb +4 -4
- data/lib/rigor/inference/acceptance.rb +136 -40
- data/lib/rigor/inference/alias_mutation_floor/foreign_locals.rb +117 -0
- data/lib/rigor/inference/alias_mutation_floor/memos.rb +85 -0
- data/lib/rigor/inference/alias_mutation_floor/reach.rb +119 -0
- data/lib/rigor/inference/alias_mutation_floor/site_collector.rb +252 -0
- data/lib/rigor/inference/alias_mutation_floor.rb +339 -0
- data/lib/rigor/inference/anonymous_meta_class.rb +18 -0
- data/lib/rigor/inference/block_auto_splat.rb +1 -1
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/block_run_count.rb +185 -0
- data/lib/rigor/inference/budget_trace.rb +8 -2
- data/lib/rigor/inference/builtins/array_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
- data/lib/rigor/inference/callee_mutation_chain.rb +88 -77
- data/lib/rigor/inference/callee_write_census.rb +815 -0
- data/lib/rigor/inference/captured_locals.rb +2 -0
- data/lib/rigor/inference/census_foreign_block.rb +354 -0
- data/lib/rigor/inference/clause_widening.rb +1 -1
- data/lib/rigor/inference/closure_escape_analyzer.rb +11 -4
- data/lib/rigor/inference/construction_escape.rb +258 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
- data/lib/rigor/inference/declaration_walk.rb +4 -0
- data/lib/rigor/inference/deep_erasure.rb +39 -7
- data/lib/rigor/inference/def_handle.rb +1 -1
- data/lib/rigor/inference/def_node_resolver.rb +33 -15
- data/lib/rigor/inference/def_return_typer.rb +47 -5
- data/lib/rigor/inference/definer_resolution.rb +19 -9
- data/lib/rigor/inference/definers_index.rb +162 -0
- data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
- data/lib/rigor/inference/element_read_widening.rb +49 -3
- data/lib/rigor/inference/error_info.rb +16 -0
- data/lib/rigor/inference/expression_typer.rb +1559 -320
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/flow_tracer.rb +31 -14
- data/lib/rigor/inference/fork_map.rb +3 -0
- data/lib/rigor/inference/fresh_frame_blocks.rb +52 -1
- data/lib/rigor/inference/global_write_census.rb +81 -5
- data/lib/rigor/inference/global_writer_reach.rb +260 -0
- data/lib/rigor/inference/hkt_body.rb +1 -2
- data/lib/rigor/inference/hkt_body_parser.rb +3 -5
- data/lib/rigor/inference/hkt_reducer.rb +2 -2
- data/lib/rigor/inference/hkt_registry.rb +3 -5
- data/lib/rigor/inference/in_effect_refinements.rb +7 -0
- data/lib/rigor/inference/indexed_narrowing.rb +61 -15
- data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
- data/lib/rigor/inference/ivar_writer_census.rb +154 -8
- data/lib/rigor/inference/key_presence_guard.rb +51 -24
- data/lib/rigor/inference/last_line/self_evidence.rb +30 -0
- data/lib/rigor/inference/last_line.rb +5 -2
- data/lib/rigor/inference/loop_value_record.rb +66 -14
- data/lib/rigor/inference/match_rebinding/frame.rb +57 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +101 -0
- data/lib/rigor/inference/match_rebinding.rb +28 -5
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +44 -30
- data/lib/rigor/inference/method_dispatcher/class_keyed_block_params.rb +65 -0
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +3 -3
- data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
- data/lib/rigor/inference/method_dispatcher/core_enumerable_receiver.rb +44 -0
- data/lib/rigor/inference/method_dispatcher/declared_constructor.rb +151 -0
- data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/interface_shape.rb +181 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +51 -27
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +606 -78
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +3 -3
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +9 -7
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +467 -84
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
- data/lib/rigor/inference/method_dispatcher/singleton_type_params.rb +104 -0
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +123 -25
- data/lib/rigor/inference/method_parameter_binder.rb +136 -56
- data/lib/rigor/inference/module_function_state/spellings.rb +2 -2
- data/lib/rigor/inference/module_function_state.rb +24 -3
- data/lib/rigor/inference/multi_target_binder.rb +2 -2
- data/lib/rigor/inference/mutation_rejoin.rb +240 -4
- data/lib/rigor/inference/mutation_widening.rb +199 -29
- data/lib/rigor/inference/narrowing.rb +238 -50
- data/lib/rigor/inference/operand_effects.rb +43 -2
- data/lib/rigor/inference/optimistic_origin.rb +134 -9
- data/lib/rigor/inference/origin_lookup.rb +1 -1
- data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +1 -1
- data/lib/rigor/inference/precision_scanner.rb +1 -1
- data/lib/rigor/inference/project_method_ownership.rb +30 -9
- data/lib/rigor/inference/project_patched_methods.rb +65 -13
- data/lib/rigor/inference/project_patched_scanner.rb +120 -25
- data/lib/rigor/inference/project_patched_visibility.rb +39 -3
- data/lib/rigor/inference/project_yielder.rb +11 -17
- data/lib/rigor/inference/rbs_type_translator.rb +68 -15
- data/lib/rigor/inference/receiver_alias.rb +20 -0
- data/lib/rigor/inference/receiver_writer_call.rb +67 -0
- data/lib/rigor/inference/recursion_rebinding.rb +157 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +57 -17
- data/lib/rigor/inference/refined_dispatch/own_method.rb +51 -16
- data/lib/rigor/inference/refined_dispatch.rb +13 -0
- data/lib/rigor/inference/reflective_ivar_write.rb +19 -11
- data/lib/rigor/inference/remover_holder_scan.rb +287 -0
- data/lib/rigor/inference/remover_narrowing.rb +224 -0
- data/lib/rigor/inference/rewrite_mutation.rb +53 -2
- data/lib/rigor/inference/scope_indexer/certainty.rb +0 -1
- data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +13 -6
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +70 -26
- data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
- data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
- data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +295 -0
- data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +136 -9
- data/lib/rigor/inference/scope_indexer.rb +2213 -226
- data/lib/rigor/inference/self_call_ivar_writes.rb +103 -12
- data/lib/rigor/inference/self_preserving_block.rb +87 -0
- data/lib/rigor/inference/set_mutation.rb +189 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +42 -8
- data/lib/rigor/inference/singleton_object_constant.rb +32 -2
- data/lib/rigor/inference/statement_evaluator.rb +3268 -358
- data/lib/rigor/inference/string_mutation.rb +14 -0
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_scanner.rb +40 -17
- data/lib/rigor/inference/toplevel_def_census.rb +67 -0
- data/lib/rigor/inference/unknown_store_widening.rb +103 -4
- data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
- data/lib/rigor/inference/untyped_rejoin.rb +1 -1
- data/lib/rigor/inference/void_tail_summary.rb +29 -9
- data/lib/rigor/language_server/buffer_table.rb +3 -3
- data/lib/rigor/language_server/completion_provider.rb +26 -20
- data/lib/rigor/language_server/config_warning_announcer.rb +28 -0
- data/lib/rigor/language_server/diagnostic_publisher.rb +8 -3
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -2
- data/lib/rigor/language_server/hover_provider.rb +1 -7
- data/lib/rigor/language_server/hover_renderer.rb +6 -8
- data/lib/rigor/language_server/project_context.rb +39 -1
- data/lib/rigor/language_server/publish_batcher.rb +3 -4
- data/lib/rigor/language_server/selection_range_provider.rb +2 -4
- data/lib/rigor/language_server/server.rb +7 -3
- data/lib/rigor/language_server/signature_help_provider.rb +4 -4
- data/lib/rigor/lens/claims/comparison.rb +78 -0
- data/lib/rigor/lens/claims/request.rb +131 -0
- data/lib/rigor/lens/claims/type_reader.rb +216 -0
- data/lib/rigor/lens/claims.rb +208 -0
- data/lib/rigor/lens/contract_reader.rb +5 -0
- data/lib/rigor/lens/document.rb +1 -1
- data/lib/rigor/lens/file_reader.rb +21 -8
- data/lib/rigor/lens/plugins.rb +1 -1
- data/lib/rigor/lens/run.rb +8 -0
- data/lib/rigor/lens.rb +1 -1
- data/lib/rigor/library_probe.rb +68 -0
- data/lib/rigor/mcp/server.rb +73 -2
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/memo_audit.rb +282 -0
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- data/lib/rigor/plugin/base.rb +15 -2
- data/lib/rigor/plugin/bundled_catalog.rb +1 -12
- data/lib/rigor/plugin/io_boundary.rb +26 -4
- data/lib/rigor/plugin/loader.rb +25 -8
- data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
- data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
- data/lib/rigor/plugin/manifest.rb +16 -16
- data/lib/rigor/plugin/registry.rb +16 -5
- data/lib/rigor/plugin/services.rb +2 -3
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
- data/lib/rigor/plugin/trust_policy.rb +48 -2
- data/lib/rigor/plugin/type_node_resolver.rb +2 -4
- data/lib/rigor/plugin.rb +6 -8
- data/lib/rigor/plugin_gap_advisory.rb +5 -1
- data/lib/rigor/project_environment.rb +16 -2
- data/lib/rigor/protection/analysis_guard.rb +1 -1
- data/lib/rigor/protection/discovery_seed.rb +44 -5
- data/lib/rigor/protection/mutation_cache.rb +4 -1
- data/lib/rigor/protection/mutator.rb +1 -1
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
- data/lib/rigor/rbs_extended/reporter.rb +27 -3
- data/lib/rigor/rbs_extended.rb +2 -3
- data/lib/rigor/reflection/constant_ancestors.rb +81 -22
- data/lib/rigor/reflection/declaration_collectors.rb +55 -0
- data/lib/rigor/reflection/declarations.rb +58 -0
- data/lib/rigor/reflection/project_method.rb +177 -0
- data/lib/rigor/reflection.rb +2 -0
- data/lib/rigor/rooted_glob.rb +32 -1
- data/lib/rigor/run_scoped_slots.rb +38 -0
- data/lib/rigor/runtime/jit.rb +1 -1
- data/lib/rigor/scope/discovery_index.rb +168 -8
- data/lib/rigor/scope/resolution_chain/relevance.rb +14 -1
- data/lib/rigor/scope/resolution_chain.rb +901 -54
- data/lib/rigor/scope.rb +644 -37
- data/lib/rigor/sig_gen/bot_proposal_guard.rb +306 -0
- data/lib/rigor/sig_gen/classification.rb +18 -1
- data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
- data/lib/rigor/sig_gen/generator.rb +588 -66
- data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
- data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
- data/lib/rigor/sig_gen/layout_index.rb +3 -3
- data/lib/rigor/sig_gen/method_candidate.rb +37 -4
- data/lib/rigor/sig_gen/observation_collector.rb +14 -7
- data/lib/rigor/sig_gen/observed_call.rb +1 -1
- data/lib/rigor/sig_gen/path_mapper.rb +6 -0
- data/lib/rigor/sig_gen/platform_guarded_files.rb +184 -0
- data/lib/rigor/sig_gen/renderer.rb +15 -9
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +62 -2
- data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
- data/lib/rigor/sig_gen/write_reporter.rb +63 -14
- data/lib/rigor/sig_gen/write_result.rb +23 -12
- data/lib/rigor/sig_gen/writer.rb +238 -37
- data/lib/rigor/sig_gen.rb +1 -0
- data/lib/rigor/source/forwardable_delegators.rb +122 -0
- data/lib/rigor/source/node_children.rb +13 -0
- data/lib/rigor/type/acceptance_router.rb +2 -2
- data/lib/rigor/type/accepts_result.rb +1 -1
- data/lib/rigor/type/app.rb +4 -6
- data/lib/rigor/type/combinator.rb +173 -21
- data/lib/rigor/type/constant.rb +4 -5
- data/lib/rigor/type/hash_shape.rb +21 -40
- data/lib/rigor/type/interface.rb +60 -0
- data/lib/rigor/type/union.rb +1 -1
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/identifier.rb +1 -2
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node/string_literal.rb +1 -2
- data/lib/rigor/type_node/symbol_literal.rb +1 -2
- data/lib/rigor/type_node/union.rb +3 -6
- data/lib/rigor/type_of_memo/serving.rb +99 -0
- data/lib/rigor/type_of_memo/stats.rb +43 -0
- data/lib/rigor/type_of_memo.rb +337 -0
- data/lib/rigor/typing_state.rb +1094 -0
- data/lib/rigor/value_semantics.rb +1 -10
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +38 -36
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +3 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +115 -36
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +3 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +25 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +16 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +4 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +132 -33
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +20 -6
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +7 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +54 -17
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +19 -5
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +11 -14
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +30 -9
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +55 -13
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +15 -21
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -6
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +1 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +6 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +5 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +1 -1
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +8 -7
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +12 -2
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +8 -2
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +200 -4
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +4 -3
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +5 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -2
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +3 -4
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +6 -8
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +3 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +210 -3
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +146 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +52 -34
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +97 -24
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +16 -16
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +4 -8
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
- data/sig/rigor/analysis/baseline.rbs +0 -3
- data/sig/rigor/analysis/check_rules/rule_walk.rbs +6 -0
- data/sig/rigor/analysis/fact_store.rbs +2 -2
- data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
- data/sig/rigor/cache/descriptor.rbs +14 -0
- data/sig/rigor/cli/diff_command.rbs +1 -1
- data/sig/rigor/cli/explain_command.rbs +1 -1
- data/sig/rigor/effects/registry.rbs +2 -2
- data/sig/rigor/environment.rbs +4 -1
- data/sig/rigor/inference.rbs +4 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +53 -18
- data/sig/rigor/plugin/effect_attribution.rbs +3 -3
- data/sig/rigor/plugin/effect_edge.rbs +1 -1
- data/sig/rigor/plugin/effect_entry_points.rbs +3 -3
- data/sig/rigor/plugin/interfaces.rbs +51 -0
- data/sig/rigor/plugin/io_boundary.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +9 -9
- data/sig/rigor/plugin/node_context.rbs +21 -0
- data/sig/rigor/plugin/protocol_contract.rbs +3 -7
- data/sig/rigor/reflection.rbs +37 -0
- data/sig/rigor/scope.rbs +43 -4
- data/sig/rigor/trinary.rbs +1 -0
- data/sig/rigor/type.rbs +9 -3
- data/sig/rigor.rbs +3 -2
- data/skills/rigor-ask/SKILL.md +1 -0
- data/skills/rigor-plugin-author/SKILL.md +4 -4
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
- data/skills/rigor-plugin-review/SKILL.md +3 -3
- data/skills/rigor-project-init/references/01-detect.md +1 -0
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +17 -2
- data/skills/rigor-project-init/references/06-agent-contract.md +4 -4
- data/skills/rigor-rbs-contract/SKILL.md +147 -0
- data/skills/rigor-rbs-contract/references/01-finding-candidates.md +212 -0
- data/skills/rigor-rbs-contract/references/02-contract-rules.md +157 -0
- data/skills/rigor-rbs-contract/references/03-target-own-code.md +90 -0
- data/skills/rigor-rbs-contract/references/04-target-gem-rbs-collection.md +210 -0
- data/skills/rigor-rbs-contract/references/05-declaration-mutants.md +182 -0
- data/skills/rigor-rbs-setup/SKILL.md +3 -1
- data/skills/rigor-type-oracle/SKILL.md +26 -1
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +4 -3
- metadata +82 -3
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
|
|
5
|
+
require_relative "../../source/node_walker"
|
|
6
|
+
|
|
7
|
+
module Rigor
|
|
8
|
+
module Inference
|
|
9
|
+
module ScopeIndexer
|
|
10
|
+
# Issue #2291 — the class-ivar census types an `@x = LIT` write from the type the constant's own write binds, the
|
|
11
|
+
# same way a method body reads `LIT`. The census runs before the file's in-source constant table is seeded, so
|
|
12
|
+
# without it the write typed under no constant table and a reader over the ivar (`def q = @q`) read `Dynamic[top]`
|
|
13
|
+
# where `def g = LIT` read the constant's type.
|
|
14
|
+
#
|
|
15
|
+
# One instance serves one census walk. The file's constant table is built on the first constant-valued write, so
|
|
16
|
+
# a file with none pays nothing; it is built as {ScopeIndexer#index} builds it later (typed under the census's
|
|
17
|
+
# scope, merged over the scope's project seed, a mutated literal widened), and resolved through the ordinary
|
|
18
|
+
# constant lookup. {#type_of} answers nil, leaving the write's ordinary typing, when:
|
|
19
|
+
#
|
|
20
|
+
# - the value is not a constant read (`LIT`, `A::LIT`, `::LIT`); a path over anything else (`self.class::LIT`)
|
|
21
|
+
# resolves to nothing, as it does in a method body;
|
|
22
|
+
# - the file may rebind a constant of that last segment: a second write in any form, an operator, `||=` or `&&=`
|
|
23
|
+
# write, a multiple-assignment target, or a `const_set` / `remove_const` naming it — or naming anything but a
|
|
24
|
+
# literal, which may be it.
|
|
25
|
+
#
|
|
26
|
+
# A constant another file assigns resolves only through the scope's own project seed, as it did before.
|
|
27
|
+
class ConstantIvarValue
|
|
28
|
+
WRITE_NODES = [Prism::ConstantWriteNode, Prism::ConstantPathWriteNode].freeze
|
|
29
|
+
REBIND_NODES = [Prism::ConstantOperatorWriteNode, Prism::ConstantOrWriteNode, Prism::ConstantAndWriteNode,
|
|
30
|
+
Prism::ConstantPathOperatorWriteNode, Prism::ConstantPathOrWriteNode,
|
|
31
|
+
Prism::ConstantPathAndWriteNode, Prism::ConstantTargetNode,
|
|
32
|
+
Prism::ConstantPathTargetNode].freeze
|
|
33
|
+
REBIND_CALLS = %i[const_set remove_const].freeze
|
|
34
|
+
ANY_NAME = :any
|
|
35
|
+
private_constant :WRITE_NODES, :REBIND_NODES, :REBIND_CALLS, :ANY_NAME
|
|
36
|
+
|
|
37
|
+
def initialize(root, scope)
|
|
38
|
+
@root = root
|
|
39
|
+
@scope = scope
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# The type `value` reads under the file's constant table, or nil when the write keeps its ordinary typing.
|
|
43
|
+
def type_of(value, scope)
|
|
44
|
+
return nil unless value.is_a?(Prism::ConstantReadNode) || value.is_a?(Prism::ConstantPathNode)
|
|
45
|
+
return nil if rebound?(value.name)
|
|
46
|
+
|
|
47
|
+
scope.with_discovery(scope.discovery.with(in_source_constants: constants)).type_of(value)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def constants
|
|
53
|
+
@constants ||= begin
|
|
54
|
+
literal_mutations = ScopeIndexer.collect_literal_receiver_mutations(@root)
|
|
55
|
+
per_file = ScopeIndexer.widen_mutated_constants(
|
|
56
|
+
ScopeIndexer.build_in_source_constants(@root, @scope), literal_mutations[:constants]
|
|
57
|
+
)
|
|
58
|
+
ScopeIndexer.merge_seeded_constants(@scope.in_source_constants, per_file)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def rebound?(name)
|
|
63
|
+
@rebound ||= rebound_names
|
|
64
|
+
@rebound.include?(ANY_NAME) || @rebound.include?(name)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def rebound_names
|
|
68
|
+
written = Set.new
|
|
69
|
+
rebound = Set.new
|
|
70
|
+
Source::NodeWalker.each(@root) do |node|
|
|
71
|
+
case node
|
|
72
|
+
when *WRITE_NODES
|
|
73
|
+
name = write_name(node)
|
|
74
|
+
rebound << name unless written.add?(name)
|
|
75
|
+
when *REBIND_NODES then rebound << write_name(node)
|
|
76
|
+
when Prism::CallNode then rebound << call_name(node) if REBIND_CALLS.include?(node.name)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
rebound
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def write_name(node) = node.respond_to?(:target) ? node.target.name : node.name
|
|
83
|
+
|
|
84
|
+
def call_name(node)
|
|
85
|
+
argument = node.arguments&.arguments&.first
|
|
86
|
+
return argument.unescaped.to_sym if argument.is_a?(Prism::SymbolNode) || argument.is_a?(Prism::StringNode)
|
|
87
|
+
|
|
88
|
+
ANY_NAME
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -4,8 +4,10 @@ require "prism"
|
|
|
4
4
|
|
|
5
5
|
require_relative "../../reflection"
|
|
6
6
|
require_relative "../method_parameter_binder"
|
|
7
|
+
require_relative "../../typing_state"
|
|
7
8
|
require_relative "../global_write_census"
|
|
8
9
|
require_relative "unseen_ivar_writers"
|
|
10
|
+
require_relative "derived_param_value"
|
|
9
11
|
|
|
10
12
|
module Rigor
|
|
11
13
|
module Inference
|
|
@@ -21,7 +23,13 @@ module Rigor
|
|
|
21
23
|
# every gate, and {EMPTY} otherwise; an absent write keeps the census's ordinary rvalue typing, so a declined
|
|
22
24
|
# case reads exactly what it read before:
|
|
23
25
|
#
|
|
24
|
-
# - the write's value
|
|
26
|
+
# - the write's value reads at least one parameter, and every local it reads is a parameter with a declared type.
|
|
27
|
+
# A bare read (`@x = x`) records that type; an expression over parameters (`@x = x.dup.freeze`,
|
|
28
|
+
# `@x = Array(x)`, `@x = f ? x : y`, issue #2321) records the value typed with each parameter it reads bound to
|
|
29
|
+
# its declared type, unless {DerivedParamValue} declines it (a parameter that holds `untyped` at any depth or
|
|
30
|
+
# has a default outside its declared type, or a call argument whose type may match a narrower overload). A
|
|
31
|
+
# value that holds a block, a lambda or a nested `def` is not credited: their own parameters are locals the
|
|
32
|
+
# scan does not bind;
|
|
25
33
|
# - the write is not inside a block or a lambda, so a block parameter of the same name and a block that runs
|
|
26
34
|
# under another `self` (`instance_eval`, `Class.new`, `define_method`) never borrow the method's parameter;
|
|
27
35
|
# - the method body never writes the parameter, in any form and at any depth (`x = Integer(x)`, `x ||= …`,
|
|
@@ -33,8 +41,8 @@ module Rigor
|
|
|
33
41
|
# - the class's primary RBS declaration is the project's (`sig/` or rbs-inline), not core, stdlib or a gem's:
|
|
34
42
|
# a project reopening of a library class (`Thread::SizedQueue#initialize`) would otherwise borrow the
|
|
35
43
|
# library's parameter type for its own `def`;
|
|
36
|
-
# - the body neither calls `binding` nor passes code
|
|
37
|
-
# `
|
|
44
|
+
# - the body neither calls `binding` nor passes code as an argument to an `eval`-family call
|
|
45
|
+
# (`OPAQUE_EVAL_CALLS`), any of which can rewrite the parameter unseen;
|
|
38
46
|
# - nothing outside the class's own census may write the ivar ({UnseenIvarWriters}): no project ancestor, and
|
|
39
47
|
# no other object's code (`o.instance_variable_set(:@x, v)`);
|
|
40
48
|
# - the declared type is not `untyped`, which is what the write already records.
|
|
@@ -51,27 +59,27 @@ module Rigor
|
|
|
51
59
|
module_exec].to_set.freeze
|
|
52
60
|
private_constant :OPAQUE_EVAL_CALLS
|
|
53
61
|
|
|
54
|
-
|
|
55
|
-
|
|
62
|
+
# ADR-129 WD1 — a Frame: the scope is installed around a census and restored on the way out.
|
|
63
|
+
WRITER_SCOPE = TypingState.frame(
|
|
64
|
+
:declared_param_writer_scope,
|
|
65
|
+
reason: "the scope whose project tables the writer gates read, installed per census",
|
|
66
|
+
# G1′: the census types its defaults under the Frame, but only the writer gates, outside `type_of`, read it.
|
|
67
|
+
typing_never_reads: true
|
|
68
|
+
)
|
|
69
|
+
private_constant :WRITER_SCOPE
|
|
56
70
|
|
|
57
71
|
module_function
|
|
58
72
|
|
|
59
73
|
# Runs the block with `scope` as the one whose project tables the unseen-writer and single-file gates read, in
|
|
60
|
-
# place of the census's own scope. `rigor sig-gen` indexes each file
|
|
61
|
-
#
|
|
62
|
-
def with_writer_scope(scope)
|
|
74
|
+
# place of the census's own scope. `rigor sig-gen` indexes each file over the project tables less the member
|
|
75
|
+
# layouts; this scope carries all of them, so another file's writer, ancestor or reopening is seen.
|
|
76
|
+
def with_writer_scope(scope, &)
|
|
63
77
|
return yield if scope.nil?
|
|
64
78
|
|
|
65
|
-
|
|
66
|
-
Thread.current[WRITER_SCOPE_KEY] = scope
|
|
67
|
-
begin
|
|
68
|
-
yield
|
|
69
|
-
ensure
|
|
70
|
-
Thread.current[WRITER_SCOPE_KEY] = previous
|
|
71
|
-
end
|
|
79
|
+
TypingState.current.with(WRITER_SCOPE, scope, &)
|
|
72
80
|
end
|
|
73
81
|
|
|
74
|
-
def writer_scope(scope) =
|
|
82
|
+
def writer_scope(scope) = TypingState.current[WRITER_SCOPE] || scope
|
|
75
83
|
|
|
76
84
|
# `{InstanceVariableWriteNode => Rigor::Type}` for `def_node`'s creditable writes, or {EMPTY}. The caller
|
|
77
85
|
# passes only an instance `def`: a singleton body's `@x` is the class object's.
|
|
@@ -82,13 +90,15 @@ module Rigor
|
|
|
82
90
|
writes = []
|
|
83
91
|
return EMPTY if scan(def_node.body, written, writes, in_block: false) == :opaque
|
|
84
92
|
|
|
85
|
-
|
|
86
|
-
return EMPTY if
|
|
93
|
+
reads = unwritten_reads(writes, written)
|
|
94
|
+
return EMPTY if reads.empty?
|
|
87
95
|
|
|
88
96
|
types = declared_types(def_node, class_name, scope)
|
|
89
97
|
writers = writer_scope(scope)
|
|
90
|
-
credited =
|
|
91
|
-
|
|
98
|
+
credited = reads.each_with_object({}.compare_by_identity) do |(write, names), acc|
|
|
99
|
+
next unless names.all? { |name| types.key?(name) }
|
|
100
|
+
|
|
101
|
+
type = value_type(write.value, names, types, def_node, scope)
|
|
92
102
|
acc[write] = type if type && !UnseenIvarWriters.possible?(writers, class_name, write.name)
|
|
93
103
|
end
|
|
94
104
|
credited.empty? ? EMPTY : credited
|
|
@@ -96,7 +106,45 @@ module Rigor
|
|
|
96
106
|
EMPTY
|
|
97
107
|
end
|
|
98
108
|
|
|
99
|
-
#
|
|
109
|
+
# `{write => names of the locals its value reads}` for each write whose value reads at least one local and no
|
|
110
|
+
# local the body writes.
|
|
111
|
+
def unwritten_reads(writes, written)
|
|
112
|
+
writes.each_with_object({}.compare_by_identity) do |write, acc|
|
|
113
|
+
names = parameter_reads(write.value)
|
|
114
|
+
next if names.nil? || names.empty? || names.any? { |name| written.include?(name) }
|
|
115
|
+
|
|
116
|
+
acc[write] = names
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# The names of the locals `value` reads, or nil when it holds a block, a lambda or a scope barrier.
|
|
121
|
+
def parameter_reads(value, names = Set.new)
|
|
122
|
+
return names unless value.is_a?(Prism::Node)
|
|
123
|
+
return nil if BLOCK_NODES.any? { |klass| value.is_a?(klass) }
|
|
124
|
+
return nil if SCOPE_BARRIERS.any? { |klass| value.is_a?(klass) }
|
|
125
|
+
|
|
126
|
+
names << value.name if value.is_a?(Prism::LocalVariableReadNode)
|
|
127
|
+
value.rigor_each_child do |child|
|
|
128
|
+
return nil if parameter_reads(child, names).nil?
|
|
129
|
+
end
|
|
130
|
+
names
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# A bare read takes the declared type itself; an expression over the parameters is typed with each bound to
|
|
134
|
+
# its declared type, unless {DerivedParamValue} declines it.
|
|
135
|
+
def value_type(value, names, types, def_node, scope)
|
|
136
|
+
return types[value.name] if value.is_a?(Prism::LocalVariableReadNode)
|
|
137
|
+
return nil if DerivedParamValue.declined?(names, types, def_node, scope)
|
|
138
|
+
|
|
139
|
+
bound = names.reduce(scope) { |acc, name| acc.with_local(name, types[name]) }
|
|
140
|
+
return nil if DerivedParamValue.overload_sensitive_argument?(value, bound)
|
|
141
|
+
|
|
142
|
+
bound.type_of(value)
|
|
143
|
+
rescue StandardError
|
|
144
|
+
nil
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Collects the `@x = …` writes outside every block, and the name of every local the body writes in
|
|
100
148
|
# any form at any depth. A read at depth zero outside a block names the method's own local, which is the
|
|
101
149
|
# parameter when the body never writes the name.
|
|
102
150
|
#
|
|
@@ -113,7 +161,7 @@ module Rigor
|
|
|
113
161
|
when Prism::CallNode
|
|
114
162
|
return :opaque if opaque_call?(node)
|
|
115
163
|
end
|
|
116
|
-
writes << node if !in_block &&
|
|
164
|
+
writes << node if !in_block && node.is_a?(Prism::InstanceVariableWriteNode)
|
|
117
165
|
inner = in_block || BLOCK_NODES.any? { |klass| node.is_a?(klass) }
|
|
118
166
|
node.rigor_each_child do |child|
|
|
119
167
|
return :opaque if scan(child, written, writes, in_block: inner) == :opaque
|
|
@@ -129,10 +177,6 @@ module Rigor
|
|
|
129
177
|
OPAQUE_EVAL_CALLS.include?(node.name) && !(node.arguments&.arguments || []).empty?
|
|
130
178
|
end
|
|
131
179
|
|
|
132
|
-
def local_read_write?(node)
|
|
133
|
-
node.is_a?(Prism::InstanceVariableWriteNode) && node.value.is_a?(Prism::LocalVariableReadNode)
|
|
134
|
-
end
|
|
135
|
-
|
|
136
180
|
# The declared, non-`untyped` type of each parameter, or `{}` when a gate declines the method.
|
|
137
181
|
def declared_types(def_node, class_name, scope)
|
|
138
182
|
environment = scope&.environment
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
|
|
5
|
+
require_relative "../acceptance"
|
|
6
|
+
|
|
7
|
+
module Rigor
|
|
8
|
+
module Inference
|
|
9
|
+
module ScopeIndexer
|
|
10
|
+
# Issue #2321 — the declines {DeclaredParamWrites} applies to a write whose value is an expression over declared
|
|
11
|
+
# parameters, beyond the ones a bare read (`@x = x`) shares. Each says the expression's type, evaluated with the
|
|
12
|
+
# parameters bound to their declared types, may not be the type the ivar holds at runtime:
|
|
13
|
+
#
|
|
14
|
+
# - a parameter whose declared type holds `untyped` at any depth (`Array[untyped]`, #2325): the answer is built
|
|
15
|
+
# on the part the author left untyped;
|
|
16
|
+
# - an optional parameter whose default value's type is not within its declared type
|
|
17
|
+
# (`def initialize(x = nil)` under `(?Integer x)`): the value may run on the default, which the binding does not
|
|
18
|
+
# carry;
|
|
19
|
+
# - an argument of any call in the value (`a + b`, `a + (b > 0 ? b : 0.5)`, `a[i]`, `Array(x)`, `super(…)`,
|
|
20
|
+
# `yield(…)`), typed under the parameter bindings, whose type is anything but a literal or an instance of a
|
|
21
|
+
# class no RBS class names as its superclass: the engine picks the first overload such an argument matches
|
|
22
|
+
# (`Integer#+` given `Integer | Float` answers `Integer`), not the join of every overload it may reach. A
|
|
23
|
+
# receiver dispatches per member and is not declined. A splat or keyword argument types as neither, so it
|
|
24
|
+
# declines too.
|
|
25
|
+
module DerivedParamValue
|
|
26
|
+
module_function
|
|
27
|
+
|
|
28
|
+
# Whether a write over the parameters `names`, declared `types`, is not credited for a parameter it reads.
|
|
29
|
+
def declined?(names, types, def_node, scope)
|
|
30
|
+
names.any? do |name|
|
|
31
|
+
carries_dynamic?(types[name]) || default_outside?(def_node, name, types[name], scope)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Whether a call in `value` takes an argument whose type, under the parameter bindings of `bound`, may match an
|
|
36
|
+
# overload narrower than itself.
|
|
37
|
+
def overload_sensitive_argument?(value, bound)
|
|
38
|
+
return false unless value.is_a?(Prism::Node)
|
|
39
|
+
|
|
40
|
+
if value.is_a?(Prism::CallNode) || value.is_a?(Prism::SuperNode) || value.is_a?(Prism::YieldNode)
|
|
41
|
+
arguments = value.arguments&.arguments || []
|
|
42
|
+
return true if arguments.any? { |argument| overload_sensitive?(bound.type_of(argument), bound) }
|
|
43
|
+
end
|
|
44
|
+
value.rigor_each_child { |child| return true if overload_sensitive_argument?(child, bound) }
|
|
45
|
+
false
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def carries_dynamic?(type)
|
|
49
|
+
case type
|
|
50
|
+
when Type::Dynamic then true
|
|
51
|
+
when Type::Union, Type::Intersection then type.members.any? { |member| carries_dynamic?(member) }
|
|
52
|
+
when Type::Nominal then type.type_args.any? { |arg| carries_dynamic?(arg) }
|
|
53
|
+
when Type::Tuple then type.elements.any? { |element| carries_dynamic?(element) }
|
|
54
|
+
when Type::HashShape then type.pairs.each_value.any? { |pair_value| carries_dynamic?(pair_value) }
|
|
55
|
+
# No RBS parameter spelling yields a refinement over `untyped`; the arm keeps the walk total.
|
|
56
|
+
when Type::Difference, Type::Refined then carries_dynamic?(type.base)
|
|
57
|
+
else false
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Whether an argument of this declared type may match an overload narrower than itself.
|
|
62
|
+
def overload_sensitive?(type, scope)
|
|
63
|
+
case type
|
|
64
|
+
when Type::Constant then false
|
|
65
|
+
when Type::Nominal then !leaf_class?(type.class_name, scope&.environment)
|
|
66
|
+
else true
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def leaf_class?(class_name, environment)
|
|
71
|
+
return false if environment.nil? || environment.rbs_module?(class_name)
|
|
72
|
+
# A guard: a validated RBS signature names no unknown class, so this answers only for a broken environment.
|
|
73
|
+
return false unless environment.class_known?(class_name)
|
|
74
|
+
|
|
75
|
+
superclasses = environment.rbs_loader&.superclass_names
|
|
76
|
+
return false if superclasses.nil? || superclasses.empty?
|
|
77
|
+
|
|
78
|
+
!superclasses.include?(class_name.delete_prefix("::"))
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Whether `name` is an optional parameter of `def_node` whose default value's type is not within `declared`.
|
|
82
|
+
def default_outside?(def_node, name, declared, scope)
|
|
83
|
+
default = default_value(def_node.parameters, name)
|
|
84
|
+
return false if default.nil?
|
|
85
|
+
|
|
86
|
+
default_type = scope.type_of(default)
|
|
87
|
+
return true if carries_dynamic?(default_type)
|
|
88
|
+
|
|
89
|
+
!Acceptance.accepts(declared, default_type).yes? && !empty_collection_within?(declared, default_type)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# An empty `[]` or `{}` default is within any `Array[T]` or `Hash[K, V]` the declaration names, which the
|
|
93
|
+
# acceptance check answers only `maybe` (the empty literal has no element type to compare).
|
|
94
|
+
def empty_collection_within?(declared, default_type)
|
|
95
|
+
collection =
|
|
96
|
+
if default_type.is_a?(Type::Tuple) && default_type.elements.empty? then "Array"
|
|
97
|
+
elsif default_type.is_a?(Type::HashShape) && default_type.pairs.empty? then "Hash"
|
|
98
|
+
end
|
|
99
|
+
return false if collection.nil?
|
|
100
|
+
|
|
101
|
+
members = declared.is_a?(Type::Union) ? declared.members : [declared]
|
|
102
|
+
members.any? { |member| member.is_a?(Type::Nominal) && member.class_name == collection }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def default_value(parameters, name)
|
|
106
|
+
candidates = parameters.optionals + parameters.keywords
|
|
107
|
+
candidates.find { |param| param.respond_to?(:value) && param.name == name }&.value
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
require_relative "../../type"
|
|
5
|
+
|
|
6
|
+
module Rigor
|
|
7
|
+
module Inference
|
|
8
|
+
module ScopeIndexer
|
|
9
|
+
# Issue #2499 — the accumulator idiom: a heap slot (an instance, class or global variable, or a constant)
|
|
10
|
+
# seeded with an empty literal and filled later, often from a method, a subclass, a mixin or another file
|
|
11
|
+
# the census cannot see. The seed is never proof that the slot is still empty, so the census records it
|
|
12
|
+
# without its emptiness: `""` and `+""` as `String`, `[]` as `Array[untyped]`, `{}` as
|
|
13
|
+
# `Hash[untyped, untyped]`. `String.new`, `Array.new` and `Hash.new` already type as their nominal.
|
|
14
|
+
#
|
|
15
|
+
# Only the empty spellings count. A non-empty literal (`@order = "asc"`, `MODES = %w[a b]`,
|
|
16
|
+
# `@opts = { a: 1 }`) keeps its exact type: such slots are modes and config tables, and an RBS parameter
|
|
17
|
+
# typed `"asc" | "desc"` must still accept them. A local keeps its flow precision; this applies only where
|
|
18
|
+
# the census seeds a heap slot.
|
|
19
|
+
module EmptySeed
|
|
20
|
+
module_function
|
|
21
|
+
|
|
22
|
+
# `type` with its emptiness dropped when `node` is an empty seed literal, otherwise `type` itself.
|
|
23
|
+
def erase(node, type)
|
|
24
|
+
return type unless empty_seed?(node)
|
|
25
|
+
|
|
26
|
+
members = type.is_a?(Type::Union) ? type.members : [type]
|
|
27
|
+
Type::Combinator.union(*members.map { |member| unpinned(member) })
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# `{ name => value node }` for each leading target of a multiple assignment whose right-hand side is a literal
|
|
31
|
+
# list (`@a, @b = {}, []`), so each slot is erased from its own element. Empty for any other right-hand side.
|
|
32
|
+
def multi_write_values(node)
|
|
33
|
+
value = node.value
|
|
34
|
+
return EMPTY_VALUES unless value.is_a?(Prism::ArrayNode)
|
|
35
|
+
|
|
36
|
+
elements = value.elements
|
|
37
|
+
node.lefts.each_with_index.with_object({}) do |(target, index), values|
|
|
38
|
+
break values if index >= elements.size || elements[index].is_a?(Prism::SplatNode)
|
|
39
|
+
next unless target.respond_to?(:name)
|
|
40
|
+
|
|
41
|
+
values[target.name] = elements[index]
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
EMPTY_VALUES = {}.freeze
|
|
46
|
+
private_constant :EMPTY_VALUES
|
|
47
|
+
|
|
48
|
+
# A chained write (`@a = @b = ""`) stores the inner write's value in the outer slot too, so it is followed.
|
|
49
|
+
def empty_seed?(node)
|
|
50
|
+
case node
|
|
51
|
+
when Prism::StringNode then node.unescaped.empty?
|
|
52
|
+
when Prism::ArrayNode, Prism::HashNode then node.elements.empty?
|
|
53
|
+
when Prism::CallNode then copied_empty_seed?(node)
|
|
54
|
+
when *CHAINED_WRITES then empty_seed?(node.value)
|
|
55
|
+
else false
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
CHAINED_WRITES = [
|
|
60
|
+
Prism::LocalVariableWriteNode, Prism::InstanceVariableWriteNode, Prism::ClassVariableWriteNode,
|
|
61
|
+
Prism::GlobalVariableWriteNode, Prism::ConstantWriteNode, Prism::ConstantPathWriteNode
|
|
62
|
+
].freeze
|
|
63
|
+
private_constant :CHAINED_WRITES
|
|
64
|
+
|
|
65
|
+
# `+""`, the unfrozen empty String the accumulator idiom spells under `# frozen_string_literal: true`, and a
|
|
66
|
+
# `.dup` of an empty seed (`"".dup`, `[].dup`, `{}.dup`).
|
|
67
|
+
def copied_empty_seed?(node)
|
|
68
|
+
return false unless node.arguments.nil? && node.block.nil?
|
|
69
|
+
|
|
70
|
+
case node.name
|
|
71
|
+
when :+@ then node.receiver.is_a?(Prism::StringNode) && node.receiver.unescaped.empty?
|
|
72
|
+
when :dup then empty_seed?(node.receiver)
|
|
73
|
+
else false
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def unpinned(member)
|
|
78
|
+
case member
|
|
79
|
+
when Type::Constant
|
|
80
|
+
member.value.is_a?(String) && member.value.empty? ? Type::Combinator.nominal_of("String") : member
|
|
81
|
+
when Type::Tuple
|
|
82
|
+
if member.elements.empty?
|
|
83
|
+
Type::Combinator.nominal_of("Array",
|
|
84
|
+
type_args: [Type::Combinator.untyped])
|
|
85
|
+
else
|
|
86
|
+
member
|
|
87
|
+
end
|
|
88
|
+
when Type::HashShape
|
|
89
|
+
return member unless member.pairs.empty?
|
|
90
|
+
|
|
91
|
+
Type::Combinator.nominal_of("Hash", type_args: [Type::Combinator.untyped, Type::Combinator.untyped])
|
|
92
|
+
else member
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
private_class_method :copied_empty_seed?, :unpinned
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|