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,147 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
|
|
5
|
+
require_relative "../../reflection"
|
|
6
|
+
require_relative "../../source/node_children"
|
|
7
|
+
require_relative "../../type"
|
|
8
|
+
|
|
9
|
+
module Rigor
|
|
10
|
+
module Analysis
|
|
11
|
+
module CheckRules
|
|
12
|
+
# Issue #3036 — answers, for a call node `flow.always-raises` proved raises, whether an enclosing
|
|
13
|
+
# `begin` (or `def … rescue … end`, which Prism wraps in the same `BeginNode`) catches the raise
|
|
14
|
+
# before it escapes. When a rescue clause may catch the raised class the exception flows to the
|
|
15
|
+
# handler, so the rule stays silent.
|
|
16
|
+
#
|
|
17
|
+
# The check is deliberately conservative in BOTH directions:
|
|
18
|
+
#
|
|
19
|
+
# - it suppresses only when a clause NAMES a class the raised class orders at-or-below
|
|
20
|
+
# (`rescue ZeroDivisionError`, `rescue StandardError`, `rescue Exception`, or a bare `rescue`,
|
|
21
|
+
# which is `StandardError`), resolved through the typer so lexical constant lookup applies. An
|
|
22
|
+
# exception reference that is not a constant (`rescue *ERRORS`, `rescue klass`) contributes no
|
|
23
|
+
# coverage, and an unresolvable / `:unknown` ordering keeps the diagnostic — a wrong silence is
|
|
24
|
+
# worse than a kept finding.
|
|
25
|
+
# - it never crosses a body boundary ({BODY_BOUNDARIES}): a `def`, `->`, or block body runs when
|
|
26
|
+
# called, not while the `begin` body evaluates, and `BEGIN` / `END` blocks run on their own
|
|
27
|
+
# schedule, so an outer rescue cannot be proven to see their raise. Class / module bodies are
|
|
28
|
+
# NOT boundaries — they evaluate inline, so their raises DO reach the enclosing rescue.
|
|
29
|
+
#
|
|
30
|
+
# Prism nodes carry no parent pointers, so ancestry is answered from a `node => parent` table
|
|
31
|
+
# built lazily on the first query: a file with no zero-division candidate never pays for it.
|
|
32
|
+
class AlwaysRaisesShield
|
|
33
|
+
# The exception class the rule proves today — Integer `/# % div modulo divmod` by a literal
|
|
34
|
+
# zero divisor. A rescue catches it when the clause names the class itself or an ancestor.
|
|
35
|
+
RAISED_CLASS = "ZeroDivisionError"
|
|
36
|
+
|
|
37
|
+
# The implicit class of a bare `rescue` / `rescue => e`.
|
|
38
|
+
BARE_RESCUE_CLASS = "StandardError"
|
|
39
|
+
|
|
40
|
+
# Node classes whose bodies do not execute inside the enclosing `begin` — see the class
|
|
41
|
+
# comment. Mirrors the deferred-body set `Inference::ScopeIndexer` already tracks.
|
|
42
|
+
BODY_BOUNDARIES = [
|
|
43
|
+
Prism::DefNode, Prism::LambdaNode, Prism::BlockNode,
|
|
44
|
+
Prism::PreExecutionNode, Prism::PostExecutionNode
|
|
45
|
+
].freeze
|
|
46
|
+
|
|
47
|
+
def initialize(root)
|
|
48
|
+
@root = root
|
|
49
|
+
@parent_map = nil
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# True when `node` sits in the protected body of a `begin` whose rescue chain may catch
|
|
53
|
+
# {RAISED_CLASS}. `scope` is the scope recorded for `node`; it resolves the clause's exception
|
|
54
|
+
# constants lexically. A nil scope or a node outside `root`'s subtree answers false.
|
|
55
|
+
def rescued?(node, scope)
|
|
56
|
+
return false if scope.nil?
|
|
57
|
+
|
|
58
|
+
parents = parent_map
|
|
59
|
+
child = node
|
|
60
|
+
current = parents[node]
|
|
61
|
+
while current
|
|
62
|
+
return true if protected_body?(current, child, scope)
|
|
63
|
+
return false if BODY_BOUNDARIES.any? { |klass| current.is_a?(klass) }
|
|
64
|
+
|
|
65
|
+
child = current
|
|
66
|
+
current = parents[current]
|
|
67
|
+
end
|
|
68
|
+
false
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
# True when `node` is a `begin` whose protected body (`statements`) contains `child` — the
|
|
74
|
+
# ascent arrived through the body rather than through a rescue / else / ensure clause, whose
|
|
75
|
+
# raises this `begin` does not catch — and whose rescue chain may catch {RAISED_CLASS}. When
|
|
76
|
+
# the chain does NOT catch, the ascent continues: an outer `begin` may still.
|
|
77
|
+
def protected_body?(node, child, scope)
|
|
78
|
+
return false unless node.is_a?(Prism::BeginNode)
|
|
79
|
+
return false unless child.equal?(node.statements)
|
|
80
|
+
return false if node.rescue_clause.nil?
|
|
81
|
+
|
|
82
|
+
rescue_chain_catches?(node.rescue_clause, scope)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Any clause of the `rescue … rescue …` chain may catch — clauses are tried in order and the
|
|
86
|
+
# first match wins, so one catching clause is enough.
|
|
87
|
+
def rescue_chain_catches?(clause, scope)
|
|
88
|
+
while clause
|
|
89
|
+
return true if rescue_clause_catches?(clause, scope)
|
|
90
|
+
|
|
91
|
+
clause = clause.subsequent
|
|
92
|
+
end
|
|
93
|
+
false
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# One `rescue` clause catches {RAISED_CLASS} when any class it names orders at-or-above the
|
|
97
|
+
# raised class. A bare `rescue` names {BARE_RESCUE_CLASS}.
|
|
98
|
+
def rescue_clause_catches?(clause, scope)
|
|
99
|
+
exceptions = clause.exceptions
|
|
100
|
+
return class_catches?(BARE_RESCUE_CLASS, scope) if exceptions.empty?
|
|
101
|
+
|
|
102
|
+
exceptions.any? do |exception|
|
|
103
|
+
class_name = exception_class_name(exception, scope)
|
|
104
|
+
class_name && class_catches?(class_name, scope)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# The class a rescue exception reference names, or nil when it is not a constant path (a splat,
|
|
109
|
+
# a dynamic expression) or does not resolve to a class object. Only `Type::Singleton` counts:
|
|
110
|
+
# the rescue operand is the class itself, and an instance-typed constant cannot be proven to
|
|
111
|
+
# match.
|
|
112
|
+
def exception_class_name(node, scope)
|
|
113
|
+
return nil unless node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode)
|
|
114
|
+
|
|
115
|
+
type = scope.type_of(node)
|
|
116
|
+
type.class_name if type.is_a?(Type::Singleton)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# `class_ordering(raised, rescued)`: `:equal` / `:subclass` mean the rescued class IS the raised
|
|
120
|
+
# class or an ancestor of it — the clause catches. `:superclass`, `:disjoint`, and `:unknown`
|
|
121
|
+
# all keep the diagnostic.
|
|
122
|
+
def class_catches?(rescued_class, scope)
|
|
123
|
+
%i[equal subclass].include?(
|
|
124
|
+
Rigor::Reflection.class_ordering(RAISED_CLASS, rescued_class, scope: scope)
|
|
125
|
+
)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# `node => parent` for every descendant of `root`, built on first use. Iterative so a deeply
|
|
129
|
+
# nested file cannot overflow the stack the way a recursive walk could.
|
|
130
|
+
def parent_map
|
|
131
|
+
@parent_map ||= begin
|
|
132
|
+
parents = {}
|
|
133
|
+
stack = [@root]
|
|
134
|
+
until stack.empty?
|
|
135
|
+
node = stack.pop
|
|
136
|
+
node.rigor_each_child do |child|
|
|
137
|
+
parents[child] = node
|
|
138
|
+
stack << child
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
parents
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
@@ -23,10 +23,11 @@ module Rigor
|
|
|
23
23
|
# `BlockNode` ancestors. Mutation tracking through loop bodies is incomplete (`shift = 0; loop {
|
|
24
24
|
# shift += 7 }` keeps `shift` at `Constant<7>` per the analyzer), so a `Constant<bool>` predicate
|
|
25
25
|
# inside the loop body is suspect.
|
|
26
|
-
# - **Defensive predicate calls**: `.nil?` / `.empty?` / `.zero?` / `.any?` / `.none?` / `.all
|
|
27
|
-
# read like the user explicitly checking for a runtime condition that the type
|
|
28
|
-
# can't happen — Rigor's strict-on-returns RBS often disagrees with the user's
|
|
29
|
-
# `Module#name` returns `String` per RBS but anonymous classes really do return
|
|
26
|
+
# - **Defensive predicate calls**: `.nil?` / `.empty?` / `.zero?` / `.any?` / `.none?` / `.all?` /
|
|
27
|
+
# `.respond_to?`. These read like the user explicitly checking for a runtime condition that the type
|
|
28
|
+
# system already proves can't happen — Rigor's strict-on-returns RBS often disagrees with the user's
|
|
29
|
+
# defensive check (e.g. `Module#name` returns `String` per RBS but anonymous classes really do return
|
|
30
|
+
# `nil`). Skipping
|
|
30
31
|
# these forms keeps the rule useful for genuine logic errors without faulting defensive code.
|
|
31
32
|
#
|
|
32
33
|
# Also skipped (so the literal-only `unreachable-branch` rule doesn't double-fire alongside this one):
|
|
@@ -53,8 +54,6 @@ module Rigor
|
|
|
53
54
|
NODE_CLASSES = [Prism::IfNode, Prism::UnlessNode].freeze
|
|
54
55
|
RULE_WALK_GATES = [:loop_or_block].freeze
|
|
55
56
|
|
|
56
|
-
# @return one entry per qualifying predicate. Empty when the tree carries no firing
|
|
57
|
-
# predicates.
|
|
58
57
|
def initialize(scope_index)
|
|
59
58
|
@scope_index = scope_index
|
|
60
59
|
@results = []
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rigor
|
|
4
|
+
module Analysis
|
|
5
|
+
module CheckRules
|
|
6
|
+
# Issue #2786 — what the per-call-node rules ({CheckRules.call_node_diagnostics}) each derived on their own:
|
|
7
|
+
# the call node's index scope, the type of its receiver under that scope, and the RBS signature the call
|
|
8
|
+
# resolves to on that receiver. One object per call node is built before the rules run and handed to every
|
|
9
|
+
# rule that reads them.
|
|
10
|
+
#
|
|
11
|
+
# The receiver type is computed lazily, on the first rule that asks, from the same `(scope, node)` pair each
|
|
12
|
+
# rule typed before: `scope_index[call_node]` and `call_node.receiver`. Every later rule reads that value
|
|
13
|
+
# instead of re-typing the receiver chain. This is not a memo over `type_of`: nothing is served across call
|
|
14
|
+
# nodes or across scopes, and no fact is computed anywhere but at the first point a rule computed it before.
|
|
15
|
+
# It rests on the `Scope#type_of` contract that one `(scope, node)` pair types structurally equal within a run
|
|
16
|
+
# *under the same ambient inputs* (`docs/internal-spec/inference-engine.md`), which holds here because of
|
|
17
|
+
# an invariant of the rule list: between the first rule that types the receiver and the last that reads it, no
|
|
18
|
+
# frame is installed and nothing types a node inside the receiver's subtree under another state or adds an
|
|
19
|
+
# `optimistic_origins` mark there. The other typings in that window are the call's arguments (outside the
|
|
20
|
+
# subtree), `receiver.receiver` under the same scope (which rewrites what the receiver's typing already
|
|
21
|
+
# wrote), and the key-presence re-walk, which runs on `with_isolated_side_tables`. A rule inserted between
|
|
22
|
+
# two readers that breaks this must type the receiver itself.
|
|
23
|
+
#
|
|
24
|
+
# Of the typing, only the receiver is shared. A rule that types a sub-expression (`receiver.receiver`), an
|
|
25
|
+
# argument, or any node under a scope other than the call's keeps its own `type_of` call: arguments are typed
|
|
26
|
+
# from their own index entry by `call.argument-type-mismatch` and from the call's by `flow.always-raises`, and
|
|
27
|
+
# the two meet only on a divisor.
|
|
28
|
+
class CallFacts
|
|
29
|
+
attr_reader :call_node, :scope_index, :scope
|
|
30
|
+
|
|
31
|
+
def initialize(call_node, scope_index)
|
|
32
|
+
@call_node = call_node
|
|
33
|
+
@scope_index = scope_index
|
|
34
|
+
@scope = scope_index[call_node]
|
|
35
|
+
@receiver_type = nil
|
|
36
|
+
@receiver_signature = nil
|
|
37
|
+
@receiver_signature_resolved = false
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# `scope.type_of(call_node.receiver)`, computed once. Callers check that the call has a receiver and that
|
|
41
|
+
# {#scope} is not nil first, as they did before sharing it.
|
|
42
|
+
def receiver_type
|
|
43
|
+
@receiver_type ||= @scope.type_of(@call_node.receiver)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# The signature `CheckRules.receiver_signature` resolves, its block run on the first ask only. A nil answer
|
|
47
|
+
# is remembered too. A block that raises leaves it unresolved, so the next ask raises as it did before.
|
|
48
|
+
def receiver_signature
|
|
49
|
+
return @receiver_signature if @receiver_signature_resolved
|
|
50
|
+
|
|
51
|
+
signature = yield
|
|
52
|
+
@receiver_signature_resolved = true
|
|
53
|
+
@receiver_signature = signature
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -35,8 +35,8 @@ module Rigor
|
|
|
35
35
|
# exactly like the legacy walk.
|
|
36
36
|
NODE_CLASSES = [Prism::DefNode].freeze
|
|
37
37
|
|
|
38
|
-
#
|
|
39
|
-
#
|
|
38
|
+
# One entry per dead-assignment write, as `{def_node:, write_node:}`. Empty when the tree has no
|
|
39
|
+
# qualifying writes.
|
|
40
40
|
def initialize(scope_index)
|
|
41
41
|
@scope_index = scope_index
|
|
42
42
|
@results = []
|
|
@@ -24,10 +24,10 @@ module Rigor
|
|
|
24
24
|
# currently declaration-sourced ivar*, so a second hop (`c = @count; d = c`) and an `||=` rewrite land on
|
|
25
25
|
# the plain `with_local` path and carry no mark.
|
|
26
26
|
#
|
|
27
|
-
# A branch join
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
27
|
+
# A branch join keeps the mark when both arms stamp it: `if c then r = @count else r = @count end` stamps
|
|
28
|
+
# `(:local, :r)` on each arm, and `join_declaration_sourced` INTERSECTS, so the mark survives. Only an
|
|
29
|
+
# *asymmetric* join drops it, by that intersection rather than by reaching `with_local`. The normative
|
|
30
|
+
# statement — establishment, drop, join and the full list of
|
|
31
31
|
# unmarked shapes, each verified against the implementation — is
|
|
32
32
|
# `docs/internal-spec/inference-engine.md` § "Declaration-sourced provenance mark (ADR-58)"; keep this
|
|
33
33
|
# comment subordinate to it.
|
|
@@ -59,6 +59,14 @@ module Rigor
|
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
# Issue #2656 — true when `node` reads a local a class guard narrowed from a `Dynamic` value
|
|
63
|
+
# ({Scope#with_gradual_narrowing_mark}). `call.argument-type-mismatch` judges such an argument by its class
|
|
64
|
+
# rather than against the parameter's literals (`CheckRules#gradual_narrowing_accepted?`); the other rules
|
|
65
|
+
# read the narrowed `Nominal` as before.
|
|
66
|
+
def gradual_narrowed?(node, scope)
|
|
67
|
+
node.is_a?(Prism::LocalVariableReadNode) && scope.gradual_narrowing?(node.name)
|
|
68
|
+
end
|
|
69
|
+
|
|
62
70
|
# The globals whose declared seeds `node` still reads, empty for none: a read of a marked global, or of a
|
|
63
71
|
# local copied from one or more (`sep = $/`, `Scope#declaration_sourced_global_copies`), bare or in
|
|
64
72
|
# parentheses (`($stdout)`).
|
|
@@ -36,8 +36,8 @@ module Rigor
|
|
|
36
36
|
# a Hash literal, loops and blocks included.
|
|
37
37
|
NODE_CLASSES = [Prism::HashNode, Prism::KeywordHashNode].freeze
|
|
38
38
|
|
|
39
|
-
#
|
|
40
|
-
#
|
|
39
|
+
# One entry per LATER occurrence of a repeated literal key, carrying `hash_node`, `key_node`,
|
|
40
|
+
# `first_key_node` and `key_label` (`key_node` is the repeat the diagnostic points at;
|
|
41
41
|
# `first_key_node` is the earliest occurrence its message references). Empty when the tree has no
|
|
42
42
|
# qualifying duplicates.
|
|
43
43
|
def initialize(scope_index)
|
|
@@ -29,6 +29,13 @@ module Rigor
|
|
|
29
29
|
# under the ivar's `@`-prefixed name, which no local can spell — because the write and the read are
|
|
30
30
|
# separate statements. The mark is method-local: the class-ivar census that seeds a sibling method's
|
|
31
31
|
# ivars types the write's RHS without the inference seed, so a cross-method read is not a lower bound.
|
|
32
|
+
# - `Array(param)` (issue #2280): the conversion passes the parameter's elements through, so
|
|
33
|
+
# `Array(param).first.upcase` reads the lower bound back.
|
|
34
|
+
# - an `if` / `unless` / ternary whose value is the parameter (issue #2280): `(c ? param : param).upcase`.
|
|
35
|
+
# Unlike `&&` / `||`, a branch that does not hold the parameter keeps its diagnostic: the form is rooted
|
|
36
|
+
# when every branch that can run is. A predicate the engine folds to one arm (the same certainty
|
|
37
|
+
# `StatementEvaluator#eval_if` elides the dead arm on) leaves only that arm to decide, so
|
|
38
|
+
# `(c ? param : 1).upcase` declines when `c` folds truthy and reports `Integer` when it folds falsey.
|
|
32
39
|
#
|
|
33
40
|
# Every value derived from an inferred parameter by dispatch is itself a lower bound, so firing against it
|
|
34
41
|
# is an FP by the same construction. No-op on a normal `check` run — `Scope#inferred_param?` is false
|
|
@@ -40,40 +47,105 @@ module Rigor
|
|
|
40
47
|
# length, which is tiny in practice).
|
|
41
48
|
MAX_DEPTH = 64
|
|
42
49
|
|
|
43
|
-
|
|
50
|
+
# `fold` is whether `scope` is still the scope the walked node is evaluated in. The callers pass the scope
|
|
51
|
+
# at the start of the expression; once the walk passes something evaluated earlier inside it (a leading
|
|
52
|
+
# statement, the left operand of `&&`, an earlier literal element, a conditional's predicate), that
|
|
53
|
+
# earlier code may have rebound what a nested predicate reads (`(c = false; c ? param : 1)`), so a
|
|
54
|
+
# conditional below no longer folds its predicate in `scope`.
|
|
55
|
+
def rooted?(node, scope, depth = 0, fold: true)
|
|
44
56
|
return false if node.nil? || depth > MAX_DEPTH
|
|
45
57
|
|
|
46
58
|
case node
|
|
47
59
|
when Prism::LocalVariableReadNode, Prism::InstanceVariableReadNode then scope.inferred_param?(node.name)
|
|
48
|
-
when Prism::CallNode then
|
|
60
|
+
when Prism::CallNode then call_rooted?(node, scope, depth + 1, fold)
|
|
49
61
|
when Prism::ArrayNode, Prism::HashNode, Prism::KeywordHashNode, Prism::AssocNode
|
|
50
|
-
literal_rooted?(node, scope, depth + 1)
|
|
51
|
-
else combining_rooted?(node, scope, depth + 1)
|
|
62
|
+
literal_rooted?(node, scope, depth + 1, fold)
|
|
63
|
+
else combining_rooted?(node, scope, depth + 1, fold)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def call_rooted?(node, scope, depth, fold)
|
|
68
|
+
return rooted?(node.receiver, scope, depth, fold: fold) unless array_conversion?(node, scope)
|
|
69
|
+
|
|
70
|
+
rooted?(node.arguments.arguments.first, scope, depth, fold: fold)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# A receiverless `Array(x)` reaching Kernel#Array: exactly one plain argument, no block, and no project
|
|
74
|
+
# `Array` on `self`'s class or its project ancestors to bind ahead of Kernel's.
|
|
75
|
+
def array_conversion?(node, scope)
|
|
76
|
+
node.receiver.nil? && node.name == :Array && node.block.nil? &&
|
|
77
|
+
node.arguments&.arguments&.size == 1 && !project_array_method?(scope)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def project_array_method?(scope)
|
|
81
|
+
self_type = scope.self_type
|
|
82
|
+
case self_type
|
|
83
|
+
when Type::Nominal then scope.discovered_method_through_ancestors?(self_type.class_name, :Array, :instance)
|
|
84
|
+
when Type::Singleton
|
|
85
|
+
scope.discovered_method_through_ancestors?(self_type.class_name, :Array, :singleton)
|
|
86
|
+
else false
|
|
52
87
|
end
|
|
53
88
|
end
|
|
54
89
|
|
|
55
90
|
# An array or hash literal is rooted when any element is: the engine reads the element back precisely.
|
|
56
|
-
def literal_rooted?(node, scope, depth)
|
|
91
|
+
def literal_rooted?(node, scope, depth, fold)
|
|
57
92
|
parts = node.is_a?(Prism::AssocNode) ? [node.key, node.value] : node.elements
|
|
58
|
-
parts.any? { |part| rooted?(part, scope, depth) }
|
|
93
|
+
parts.each_with_index.any? { |part, index| rooted?(part, scope, depth, fold: fold && index.zero?) }
|
|
59
94
|
end
|
|
60
95
|
|
|
61
96
|
# Value-combining forms: the produced value is a union of the operands, so it is a lower bound when ANY
|
|
62
97
|
# operand is inferred-parameter-rooted (`cps[i] - x rescue -1`, `a && param.foo`, `param || b`). Declining
|
|
63
98
|
# on the whole expression is FP-safe by the same lower-bound argument.
|
|
64
|
-
def combining_rooted?(node, scope, depth)
|
|
99
|
+
def combining_rooted?(node, scope, depth, fold)
|
|
65
100
|
case node
|
|
66
101
|
when Prism::RescueModifierNode
|
|
67
|
-
rooted?(node.expression, scope, depth
|
|
68
|
-
|
|
69
|
-
when Prism::
|
|
70
|
-
|
|
102
|
+
rooted?(node.expression, scope, depth, fold: fold) ||
|
|
103
|
+
rooted?(node.rescue_expression, scope, depth, fold: false)
|
|
104
|
+
when Prism::AndNode, Prism::OrNode
|
|
105
|
+
rooted?(node.left, scope, depth, fold: fold) || rooted?(node.right, scope, depth, fold: false)
|
|
106
|
+
when Prism::ParenthesesNode then body_rooted?(node.body, scope, depth, fold)
|
|
107
|
+
when Prism::StatementsNode then body_rooted?(node, scope, depth, fold)
|
|
108
|
+
when Prism::IfNode, Prism::UnlessNode then conditional_rooted?(node, scope, depth, fold)
|
|
109
|
+
when Prism::ElseNode then rooted?(node.statements, scope, depth, fold: fold)
|
|
110
|
+
else false
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# A body's value is its last statement; any statement before it runs first.
|
|
115
|
+
def body_rooted?(body, scope, depth, fold)
|
|
116
|
+
return rooted?(body, scope, depth, fold: fold) unless body.is_a?(Prism::StatementsNode)
|
|
117
|
+
|
|
118
|
+
rooted?(body.body.last, scope, depth, fold: fold && body.body.size == 1)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Every branch that can run must be rooted; an absent branch is `nil`, never rooted. The predicate is
|
|
122
|
+
# typed only when the branches disagree, so a conditional that never mentions a parameter costs nothing.
|
|
123
|
+
# An arm runs after the predicate, so a conditional nested in one never folds.
|
|
124
|
+
def conditional_rooted?(node, scope, depth, fold)
|
|
125
|
+
truthy_rooted, falsey_rooted = arms(node).map { |arm| rooted?(arm, scope, depth, fold: false) }
|
|
126
|
+
return truthy_rooted if truthy_rooted == falsey_rooted
|
|
127
|
+
return false unless fold
|
|
128
|
+
|
|
129
|
+
case predicate_certainty(node.predicate, scope)
|
|
130
|
+
when :truthy then truthy_rooted
|
|
131
|
+
when :falsey then falsey_rooted
|
|
71
132
|
else false
|
|
72
133
|
end
|
|
73
134
|
end
|
|
74
135
|
|
|
75
|
-
|
|
76
|
-
|
|
136
|
+
# `[runs when the predicate is truthy, runs when it is falsey]`.
|
|
137
|
+
def arms(node)
|
|
138
|
+
node.is_a?(Prism::IfNode) ? [node.statements, node.subsequent] : [node.else_clause, node.statements]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# The arm certainty `StatementEvaluator#branch_certainty` elides on: a decidable version guard, else the
|
|
142
|
+
# predicate's folded truthiness unless an optimistic carrier props it up.
|
|
143
|
+
def predicate_certainty(predicate, scope)
|
|
144
|
+
guard = Inference::VersionGuard.verdict(predicate)
|
|
145
|
+
return guard if guard
|
|
146
|
+
return nil unless Inference::OptimisticOrigin.resolve(predicate, scope).nil?
|
|
147
|
+
|
|
148
|
+
Inference::Narrowing.predicate_certainty(scope.type_of(predicate))
|
|
77
149
|
end
|
|
78
150
|
end
|
|
79
151
|
end
|
|
@@ -6,6 +6,7 @@ require_relative "rule_walk"
|
|
|
6
6
|
require_relative "../../scope/resolution_chain"
|
|
7
7
|
require_relative "../../source/constant_path"
|
|
8
8
|
require_relative "../../source/node_children"
|
|
9
|
+
require_relative "../../inference/census_foreign_block"
|
|
9
10
|
|
|
10
11
|
module Rigor
|
|
11
12
|
module Analysis
|
|
@@ -28,6 +29,9 @@ module Rigor
|
|
|
28
29
|
# doesn't yet model.
|
|
29
30
|
# - Nested classes / modules / defs inside a method body are barriers, mirroring the indexer's
|
|
30
31
|
# `IVAR_BARRIER_NODES` policy.
|
|
32
|
+
# - Issue #2943: a write directly in a block or `class << obj` body whose `self` is provably not an instance
|
|
33
|
+
# of the class (`Other.new.instance_eval { @r = 1 }`), by the class-ivar census's rule
|
|
34
|
+
# ({Inference::CensusForeignBlock}).
|
|
31
35
|
class IvarWriteCollector
|
|
32
36
|
BARRIER_NODES = [Prism::DefNode, Prism::ClassNode, Prism::ModuleNode].freeze
|
|
33
37
|
private_constant :BARRIER_NODES
|
|
@@ -44,8 +48,9 @@ module Rigor
|
|
|
44
48
|
NODE_CLASSES = [Prism::DefNode, Prism::CallNode].freeze
|
|
45
49
|
RULE_WALK_GATES = %i[inside_def detached_ivar_facet].freeze
|
|
46
50
|
|
|
47
|
-
#
|
|
48
|
-
# method_name
|
|
51
|
+
# Groups the writes by class qualified name, then by ivar name. Each write carries `node`, `type`,
|
|
52
|
+
# `declared`, `method_name`, `visibility`, `per_invocation` and `action`. Empty when the tree has no
|
|
53
|
+
# qualifying writes.
|
|
49
54
|
# `declared` is the translated type of the class's RBS declaration of the ivar — an `@x: T` member or
|
|
50
55
|
# the slot an `attr_*` member implies, own or inherited — and nil when the RBS declares none.
|
|
51
56
|
#
|
|
@@ -155,7 +160,8 @@ module Rigor
|
|
|
155
160
|
|
|
156
161
|
def collect_def_writes(def_node, qualified_prefix)
|
|
157
162
|
return if def_node.body.nil? || qualified_prefix.empty?
|
|
158
|
-
|
|
163
|
+
# A def with a receiver writes the ivars of another object than an instance (#3155).
|
|
164
|
+
return if def_node.receiver
|
|
159
165
|
|
|
160
166
|
class_name = qualified_prefix.join("::")
|
|
161
167
|
gather_writes(def_node.body, class_name, def_node.name)
|
|
@@ -167,7 +173,13 @@ module Rigor
|
|
|
167
173
|
record_write(node, class_name, method_name) if node.is_a?(Prism::InstanceVariableWriteNode)
|
|
168
174
|
return if BARRIER_NODES.any? { |klass| node.is_a?(klass) }
|
|
169
175
|
|
|
170
|
-
|
|
176
|
+
# #2943 — a block or `class <<` body whose `self` is provably another object writes that object's ivars, as
|
|
177
|
+
# the class-ivar census reads it ({Inference::CensusForeignBlock}); a `define_method` block nested in it that
|
|
178
|
+
# runs on an instance again still counts. A `def` it reenters stays a barrier here.
|
|
179
|
+
children = Inference::CensusForeignBlock.census_children(node, class_name)
|
|
180
|
+
return node.rigor_each_child { |child| gather_writes(child, class_name, method_name) } unless children
|
|
181
|
+
|
|
182
|
+
children.each { |child| gather_writes(child, class_name, method_name) }
|
|
171
183
|
end
|
|
172
184
|
|
|
173
185
|
def record_write(node, class_name, method_name)
|
|
@@ -5,6 +5,7 @@ require "prism"
|
|
|
5
5
|
require_relative "../../inference/scope_indexer"
|
|
6
6
|
require_relative "../../source/node_children"
|
|
7
7
|
require_relative "rebinding_block_callees"
|
|
8
|
+
require_relative "replaced_singleton_defs"
|
|
8
9
|
|
|
9
10
|
module Rigor
|
|
10
11
|
module Analysis
|
|
@@ -42,6 +43,11 @@ module Rigor
|
|
|
42
43
|
|
|
43
44
|
def refinements = (@refinements ||= Inference::InEffectRefinements.new(@root))
|
|
44
45
|
|
|
46
|
+
# Issue #3155 — the file's singleton defs a later certain def replaces, for `def.return-type-mismatch`.
|
|
47
|
+
def replaced_singleton_defs(scope_index)
|
|
48
|
+
@replaced_singleton_defs ||= ReplacedSingletonDefs.new(@root, scope_index)
|
|
49
|
+
end
|
|
50
|
+
|
|
45
51
|
# Issue #1817 — the file's block-passing calls, for `call.unresolved-toplevel`'s rebinding-callee decline.
|
|
46
52
|
def block_calls = (@block_calls ||= RebindingBlockCallees::Sites.new(@root))
|
|
47
53
|
|
|
@@ -152,7 +158,6 @@ module Rigor
|
|
|
152
158
|
end
|
|
153
159
|
end
|
|
154
160
|
|
|
155
|
-
# `o.singleton_class`.
|
|
156
161
|
def singleton_class_of_local?(node)
|
|
157
162
|
node.is_a?(Prism::CallNode) && node.name == :singleton_class && node.arguments.nil? &&
|
|
158
163
|
node.receiver.is_a?(Prism::LocalVariableReadNode)
|
|
@@ -12,16 +12,13 @@ module Rigor
|
|
|
12
12
|
# decides, with NO loop / block suppression and NO threaded context — so the collector declares no
|
|
13
13
|
# `RULE_WALK_GATES` and ignores the walk's `context`.
|
|
14
14
|
#
|
|
15
|
-
# The per-node dispatch
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# accumulated {Diagnostic}s, in the same emission order the inline `NodeWalker.each` produced them (the
|
|
20
|
-
# shared walk is the same visit-before-descend DFS over `compact_child_nodes`).
|
|
15
|
+
# The per-node dispatch stays in `CheckRules` (ADR-53 WD4) and is handed in as a callable built in the
|
|
16
|
+
# `CheckRules` module context, so its calls to the private diagnostic builders remain implicit-self. The
|
|
17
|
+
# collector only owns the traversal hook and accumulation; `#results` are the accumulated {Diagnostic}s in
|
|
18
|
+
# the walk's emission order.
|
|
21
19
|
class MainPassCollector
|
|
22
20
|
# The node classes the main pass branches on. A plain `Prism::IfNode` covers ternaries and postfix `if`
|
|
23
|
-
# too
|
|
24
|
-
# classes carry issue #1367's `global.*` rules.
|
|
21
|
+
# too. The global-write classes and `MultiWriteNode` carry issue #1367's `global.*` rules.
|
|
25
22
|
NODE_CLASSES = [
|
|
26
23
|
Prism::CallNode, Prism::DefNode, Prism::IfNode, Prism::UnlessNode,
|
|
27
24
|
Prism::GlobalVariableWriteNode, Prism::GlobalVariableOperatorWriteNode, Prism::MultiWriteNode
|