rigortype 0.3.8 → 0.4.0
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 +2 -2
- data/data/capability_roles/capability_roles.rbs +36 -0
- data/data/core_overlay/enumerable.rbs +51 -0
- data/data/core_overlay/enumerator.rbs +84 -0
- data/data/core_overlay/hash_rbs3.rbs +41 -0
- data/data/core_overlay/process.rbs +40 -0
- data/data/core_overlay/string_io.rbs +33 -0
- data/data/effects/core.yml +3 -3
- data/data/gem_overlay/activesupport/core_ext.rbs +236 -17
- data/docs/handbook/02-everyday-types.md +17 -14
- data/docs/handbook/03-narrowing.md +132 -14
- data/docs/handbook/04-tuples-and-shapes.md +8 -6
- data/docs/handbook/05-methods-and-blocks.md +1 -1
- data/docs/handbook/06-classes.md +2 -2
- data/docs/handbook/07-rbs-and-extended.md +19 -1
- data/docs/handbook/10-sorbet.md +9 -10
- data/docs/handbook/11-sig-gen.md +525 -13
- data/docs/handbook/README.md +1 -1
- data/docs/handbook/appendix-elixir.md +2 -2
- data/docs/handbook/appendix-go.md +2 -2
- data/docs/handbook/appendix-java-csharp.md +2 -2
- data/docs/handbook/appendix-mypy.md +2 -2
- data/docs/handbook/appendix-phpstan.md +1 -1
- data/docs/handbook/appendix-rust.md +1 -1
- data/docs/handbook/appendix-type-theory.md +4 -4
- data/docs/handbook/appendix-typescript.md +1 -1
- data/docs/llms.txt +2 -0
- data/docs/manual/02-cli-reference.md +95 -6
- data/docs/manual/03-configuration.md +18 -0
- data/docs/manual/04-diagnostics.md +15 -0
- data/docs/manual/07-plugins.md +15 -5
- data/docs/manual/08-skills.md +23 -2
- data/docs/manual/10-mcp-server.md +3 -2
- data/docs/manual/11-ci.md +9 -0
- data/docs/manual/15-type-protection-coverage.md +8 -0
- data/docs/manual/16-rbs-extended-annotations.md +46 -8
- data/docs/manual/18-removing-dead-code.md +10 -8
- data/docs/manual/19-effect-labels.md +10 -0
- data/docs/manual/plugins/README.md +7 -0
- data/docs/manual/plugins/rigor-actioncable.md +8 -1
- data/docs/manual/plugins/rigor-actionmailer.md +7 -0
- data/docs/manual/plugins/rigor-actionpack.md +261 -0
- data/docs/manual/plugins/rigor-active-model-serializers.md +153 -0
- data/docs/manual/plugins/rigor-activejob.md +7 -0
- data/docs/manual/plugins/rigor-activerecord.md +138 -4
- data/docs/manual/plugins/rigor-activestorage.md +8 -1
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +43 -7
- data/docs/manual/plugins/rigor-grape.md +106 -0
- data/docs/manual/plugins/rigor-graphql.md +23 -2
- data/docs/manual/plugins/rigor-pundit.md +8 -1
- data/docs/manual/plugins/rigor-rails-i18n.md +10 -5
- data/docs/manual/plugins/rigor-rails-routes.md +7 -0
- data/docs/manual/plugins/rigor-rbs-inline.md +229 -1
- data/docs/manual/plugins/rigor-sidekiq.md +8 -1
- data/docs/manual/plugins/rigor-sorbet.md +21 -5
- data/exe/rigor +19 -4
- data/lib/rigor/analysis/baseline.rb +3 -3
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +167 -2
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +189 -0
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +6 -4
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
- data/lib/rigor/analysis/check_rules/rule_ids.rb +21 -6
- data/lib/rigor/analysis/check_rules/rule_walk.rb +41 -5
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/source_arity.rb +323 -0
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +146 -0
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +38 -4
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
- data/lib/rigor/analysis/check_rules.rb +718 -79
- data/lib/rigor/analysis/crash_signature.rb +8 -24
- data/lib/rigor/analysis/dependency_recorder.rb +23 -0
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
- data/lib/rigor/analysis/erb_template_detector.rb +1 -2
- data/lib/rigor/analysis/fact_store.rb +15 -1
- data/lib/rigor/analysis/incremental.rb +42 -0
- data/lib/rigor/analysis/incremental_session.rb +117 -26
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
- data/lib/rigor/analysis/project_scan.rb +11 -1
- data/lib/rigor/analysis/reachability/graph.rb +3 -5
- data/lib/rigor/analysis/reachability/plugin_roots.rb +5 -5
- data/lib/rigor/analysis/reachability/project_files.rb +2 -2
- data/lib/rigor/analysis/reachability/scan.rb +5 -5
- data/lib/rigor/analysis/reachability/scan_cache.rb +3 -2
- data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
- data/lib/rigor/analysis/result.rb +1 -3
- data/lib/rigor/analysis/rule_catalog.rb +131 -1
- data/lib/rigor/analysis/run_cache_key.rb +21 -12
- data/lib/rigor/analysis/run_cache_probe.rb +3 -5
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +340 -33
- data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +18 -14
- data/lib/rigor/analysis/runner/pool_coordinator.rb +357 -97
- data/lib/rigor/analysis/runner/project_pre_passes.rb +38 -13
- data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
- data/lib/rigor/analysis/runner.rb +407 -51
- data/lib/rigor/analysis/template_unit_collector.rb +303 -0
- data/lib/rigor/analysis/template_unit_paths.rb +91 -0
- data/lib/rigor/analysis/template_unit_positions.rb +293 -0
- data/lib/rigor/analysis/template_units.rb +399 -0
- data/lib/rigor/analysis/worker_session.rb +62 -20
- data/lib/rigor/bleeding_edge.rb +10 -24
- data/lib/rigor/builtins/hkt_builtins.rb +3 -2
- data/lib/rigor/builtins/imported_refinements.rb +159 -21
- data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
- data/lib/rigor/builtins/regex_refinement.rb +21 -13
- data/lib/rigor/builtins/static_return_refinements.rb +7 -6
- data/lib/rigor/cache/annotation_location.rb +2 -4
- data/lib/rigor/cache/descriptor.rb +63 -20
- data/lib/rigor/cache/engine_source.rb +33 -5
- data/lib/rigor/cache/file_digest.rb +9 -1
- data/lib/rigor/cache/incremental_snapshot.rb +72 -6
- data/lib/rigor/cache/rbs_cache_producer.rb +4 -0
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
- data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
- data/lib/rigor/cache/rbs_constant_table.rb +0 -3
- data/lib/rigor/cache/rbs_descriptor.rb +112 -14
- data/lib/rigor/cache/rbs_environment.rb +8 -4
- data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
- data/lib/rigor/cache/store.rb +76 -32
- data/lib/rigor/ci_detector.rb +1 -0
- data/lib/rigor/cli/annotate_command.rb +5 -6
- data/lib/rigor/cli/check_command.rb +57 -10
- data/lib/rigor/cli/check_invocation.rb +6 -11
- data/lib/rigor/cli/check_runner_factory.rb +1 -5
- data/lib/rigor/cli/coverage_command.rb +1 -1
- data/lib/rigor/cli/coverage_mutation.rb +12 -3
- data/lib/rigor/cli/coverage_scan.rb +1 -5
- data/lib/rigor/cli/diff_command.rb +1 -1
- data/lib/rigor/cli/doc_links.rb +4 -4
- data/lib/rigor/cli/docs_command.rb +6 -6
- data/lib/rigor/cli/doctor_command.rb +85 -38
- data/lib/rigor/cli/effects_command.rb +2 -2
- data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
- data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
- data/lib/rigor/cli/explain_command.rb +34 -1
- data/lib/rigor/cli/fused_protection_report.rb +7 -1
- data/lib/rigor/cli/lsp_command.rb +1 -1
- data/lib/rigor/cli/mcp_command.rb +1 -1
- data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
- data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
- data/lib/rigor/cli/mutation_protection_report.rb +7 -1
- data/lib/rigor/cli/plugin_command.rb +4 -4
- data/lib/rigor/cli/plugins_command.rb +2 -1
- data/lib/rigor/cli/prism_colorizer.rb +1 -2
- data/lib/rigor/cli/protection_fork_scan.rb +6 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
- data/lib/rigor/cli/sig_gen_command.rb +212 -31
- data/lib/rigor/cli/skill_command.rb +2 -2
- data/lib/rigor/cli/skill_deep_probe.rb +4 -4
- data/lib/rigor/cli/skill_describe.rb +35 -35
- data/lib/rigor/cli/trace_command.rb +4 -4
- data/lib/rigor/cli/trace_renderer.rb +5 -6
- data/lib/rigor/cli/triage_command.rb +1 -1
- data/lib/rigor/cli/type_of_command.rb +29 -11
- data/lib/rigor/cli/type_of_renderer.rb +20 -9
- data/lib/rigor/cli/type_of_template_probe.rb +189 -0
- data/lib/rigor/cli/type_scan_command.rb +4 -4
- data/lib/rigor/cli/unused_command.rb +11 -3
- data/lib/rigor/cli/upgrade_command.rb +1 -1
- data/lib/rigor/cli.rb +89 -9
- data/lib/rigor/config_audit.rb +30 -5
- data/lib/rigor/configuration/severity_profile.rb +25 -9
- data/lib/rigor/configuration.rb +109 -7
- data/lib/rigor/effects/ancestry_recorder.rb +191 -0
- data/lib/rigor/effects/attribution.rb +12 -5
- data/lib/rigor/effects/callee_rule.rb +368 -0
- data/lib/rigor/effects/catalog.rb +7 -4
- data/lib/rigor/effects/collector.rb +11 -5
- data/lib/rigor/effects/config_envelopes.rb +15 -10
- data/lib/rigor/effects/definition_context.rb +179 -0
- data/lib/rigor/effects/definition_lines.rb +25 -6
- data/lib/rigor/effects/effect_table.rb +11 -4
- data/lib/rigor/effects/entry_points.rb +0 -2
- data/lib/rigor/effects/envelope_check.rb +9 -9
- data/lib/rigor/effects/envelope_index.rb +20 -8
- data/lib/rigor/effects/file_collection.rb +59 -5
- data/lib/rigor/effects/framework_units.rb +5 -7
- data/lib/rigor/effects/identity.rb +20 -6
- data/lib/rigor/effects/inline_anchor.rb +7 -7
- data/lib/rigor/effects/label_intent.rb +3 -4
- data/lib/rigor/effects/liskov_check.rb +8 -8
- data/lib/rigor/effects/local_ownership.rb +38 -12
- data/lib/rigor/effects/method_key.rb +22 -1
- data/lib/rigor/effects/mutation_classifier.rb +23 -12
- data/lib/rigor/effects/plugin_facts.rb +47 -37
- data/lib/rigor/effects/propagator.rb +297 -19
- data/lib/rigor/effects/registry.rb +1 -1
- data/lib/rigor/effects/scanner.rb +122 -73
- data/lib/rigor/effects/signature_sources.rb +4 -6
- data/lib/rigor/effects/snapshot.rb +12 -11
- data/lib/rigor/effects/snapshot_diff.rb +1 -1
- data/lib/rigor/effects/summary.rb +27 -4
- data/lib/rigor/effects/unit_scan.rb +393 -38
- data/lib/rigor/effects/unknown_label_check.rb +3 -8
- data/lib/rigor/effects/unknown_label_report.rb +3 -4
- data/lib/rigor/effects/visibility.rb +101 -0
- data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
- data/lib/rigor/environment/class_registry.rb +3 -1
- data/lib/rigor/environment/failure_slot.rb +2 -2
- data/lib/rigor/environment/installed_gem_set.rb +85 -0
- data/lib/rigor/environment/lockfile_resolver.rb +68 -4
- data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
- data/lib/rigor/environment/member_consistency.rb +298 -0
- data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
- data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
- data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
- data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
- data/lib/rigor/environment/rbs_loader.rb +674 -79
- data/lib/rigor/environment.rb +174 -24
- data/lib/rigor/flow_contribution/merger.rb +0 -2
- data/lib/rigor/flow_contribution.rb +11 -13
- data/lib/rigor/hashing/xxh3.rb +264 -0
- data/lib/rigor/inference/acceptance.rb +292 -17
- data/lib/rigor/inference/block_auto_splat.rb +216 -0
- data/lib/rigor/inference/block_call_timing.rb +338 -0
- data/lib/rigor/inference/block_parameter_binder.rb +75 -30
- data/lib/rigor/inference/block_repetition.rb +71 -0
- data/lib/rigor/inference/body_fixpoint.rb +7 -6
- data/lib/rigor/inference/budget_trace.rb +3 -6
- data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
- data/lib/rigor/inference/builtins/string_catalog.rb +1 -1
- data/lib/rigor/inference/captured_locals.rb +389 -18
- data/lib/rigor/inference/closure_escape_analyzer.rb +159 -17
- data/lib/rigor/inference/content_join.rb +200 -27
- data/lib/rigor/inference/coverage_scanner.rb +4 -6
- data/lib/rigor/inference/def_return_typer.rb +11 -7
- data/lib/rigor/inference/define_method_block_self.rb +64 -0
- data/lib/rigor/inference/dynamic_origin.rb +1 -1
- data/lib/rigor/inference/element_read_widening.rb +180 -0
- data/lib/rigor/inference/error_info.rb +196 -0
- data/lib/rigor/inference/expression_typer.rb +2124 -468
- data/lib/rigor/inference/external_ancestor_resolution.rb +267 -0
- data/lib/rigor/inference/fork_map.rb +5 -7
- data/lib/rigor/inference/fresh_frame_blocks.rb +127 -0
- data/lib/rigor/inference/global_write_census.rb +239 -0
- data/lib/rigor/inference/guard_rebinding.rb +447 -0
- data/lib/rigor/inference/hash_lookup_mutation.rb +88 -0
- data/lib/rigor/inference/hkt_reducer.rb +2 -3
- data/lib/rigor/inference/hkt_registry.rb +4 -7
- data/lib/rigor/inference/index_write_widening.rb +16 -7
- data/lib/rigor/inference/indexed_narrowing.rb +61 -9
- data/lib/rigor/inference/jump_targets.rb +82 -0
- data/lib/rigor/inference/last_line/implicit_self.rb +210 -0
- data/lib/rigor/inference/last_line/self_evidence.rb +329 -0
- data/lib/rigor/inference/last_line.rb +340 -0
- data/lib/rigor/inference/last_status.rb +144 -0
- data/lib/rigor/inference/macro_block_self_type.rb +168 -21
- data/lib/rigor/inference/match_rebinding/calls.rb +281 -0
- data/lib/rigor/inference/match_rebinding/frame.rb +148 -0
- data/lib/rigor/inference/match_rebinding/operands.rb +236 -0
- data/lib/rigor/inference/match_rebinding/self_calls.rb +83 -0
- data/lib/rigor/inference/match_rebinding.rb +392 -0
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +87 -33
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +147 -0
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/hash_transform_keys_folding.rb +191 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +6 -2
- data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
- data/lib/rigor/inference/method_dispatcher/match_data_folding.rb +159 -0
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +165 -111
- data/lib/rigor/inference/method_dispatcher/process_folding.rb +56 -0
- data/lib/rigor/inference/method_dispatcher/proven_overload.rb +72 -0
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +1046 -69
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +142 -0
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +129 -54
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -6
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +225 -25
- data/lib/rigor/inference/method_parameter_binder.rb +12 -9
- data/lib/rigor/inference/multi_target_binder.rb +341 -55
- data/lib/rigor/inference/mutation_rejoin.rb +171 -0
- data/lib/rigor/inference/mutation_widening.rb +156 -85
- data/lib/rigor/inference/narrowing.rb +835 -162
- data/lib/rigor/inference/operand_effects.rb +167 -0
- data/lib/rigor/inference/operand_walk.rb +88 -0
- data/lib/rigor/inference/optimistic_origin.rb +153 -16
- data/lib/rigor/inference/origin_lookup.rb +2 -3
- data/lib/rigor/inference/parameter_inference_collector.rb +7 -7
- data/lib/rigor/inference/pre_eval_constants.rb +6 -5
- data/lib/rigor/inference/precision_scanner.rb +3 -4
- data/lib/rigor/inference/project_method_ownership.rb +136 -0
- data/lib/rigor/inference/project_patched_methods.rb +9 -4
- data/lib/rigor/inference/project_patched_scanner.rb +10 -6
- data/lib/rigor/inference/protection_scanner.rb +1 -2
- data/lib/rigor/inference/range_constant.rb +57 -0
- data/lib/rigor/inference/rbs_type_translator.rb +49 -11
- data/lib/rigor/inference/receiver_alias.rb +93 -4
- data/lib/rigor/inference/receiver_blind_block.rb +219 -0
- data/lib/rigor/inference/refinement_mutation.rb +76 -0
- data/lib/rigor/inference/repeated_or_writes.rb +463 -0
- data/lib/rigor/inference/return_barrier.rb +54 -0
- data/lib/rigor/inference/rewrite_mutation.rb +120 -0
- data/lib/rigor/inference/scope_indexer.rb +5228 -627
- data/lib/rigor/inference/statement_evaluator.rb +3452 -595
- data/lib/rigor/inference/stored_block_call.rb +54 -0
- data/lib/rigor/inference/string_mutation.rb +97 -0
- data/lib/rigor/inference/struct_fold_safety.rb +6 -7
- data/lib/rigor/inference/synthetic_method_index.rb +1 -2
- data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
- data/lib/rigor/inference/unknown_store_widening.rb +200 -0
- data/lib/rigor/inference/unthreaded_rebinds.rb +282 -0
- data/lib/rigor/inference/version_guard.rb +28 -21
- data/lib/rigor/inference/void_origin.rb +3 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -4
- data/lib/rigor/language_server/buffer_table.rb +4 -4
- data/lib/rigor/language_server/completion_provider.rb +1 -1
- data/lib/rigor/language_server/debouncer.rb +0 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +7 -6
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -1
- data/lib/rigor/language_server/hover_provider.rb +1 -1
- data/lib/rigor/language_server/hover_renderer.rb +4 -4
- data/lib/rigor/language_server/incremental_sync.rb +6 -6
- data/lib/rigor/language_server/project_context.rb +7 -8
- data/lib/rigor/language_server/publish_batcher.rb +2 -2
- data/lib/rigor/language_server/selection_range_provider.rb +2 -2
- data/lib/rigor/language_server/server.rb +9 -9
- data/lib/rigor/language_server/signature_help_provider.rb +1 -1
- data/lib/rigor/language_server/uri.rb +1 -1
- data/lib/rigor/mcp/server.rb +2 -1
- data/lib/rigor/plugin/base.rb +258 -26
- data/lib/rigor/plugin/box_probe.rb +91 -0
- data/lib/rigor/plugin/bundled_catalog.rb +167 -0
- data/lib/rigor/plugin/effect_attribution.rb +65 -11
- data/lib/rigor/plugin/effect_entry_points.rb +3 -3
- data/lib/rigor/plugin/fact_store.rb +6 -6
- data/lib/rigor/plugin/io_boundary.rb +93 -8
- data/lib/rigor/plugin/isolation.rb +29 -6
- data/lib/rigor/plugin/loader.rb +33 -13
- data/lib/rigor/plugin/macro/block_as_method.rb +45 -6
- data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
- data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
- data/lib/rigor/plugin/manifest.rb +94 -11
- data/lib/rigor/plugin/registry.rb +76 -12
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +21 -4
- data/lib/rigor/plugin/template_unit.rb +196 -0
- data/lib/rigor/plugin/trust_policy.rb +2 -4
- data/lib/rigor/plugin/type_node_resolver.rb +3 -3
- data/lib/rigor/plugin.rb +1 -0
- data/lib/rigor/plugin_gap_advisory.rb +96 -0
- data/lib/rigor/project_environment.rb +138 -0
- data/lib/rigor/protection/analysis_guard.rb +93 -14
- data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
- data/lib/rigor/protection/dependency_closure.rb +5 -8
- data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
- data/lib/rigor/protection/discovery_seed.rb +14 -12
- data/lib/rigor/protection/kill_signature.rb +1 -4
- data/lib/rigor/protection/measurement_integrity.rb +1 -3
- data/lib/rigor/protection/mutation_cache.rb +7 -11
- data/lib/rigor/protection/mutation_scanner.rb +12 -15
- data/lib/rigor/protection/mutator.rb +2 -1
- data/lib/rigor/protection/test_suite_oracle.rb +5 -5
- data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/reporter.rb +36 -10
- data/lib/rigor/rbs_extended.rb +88 -21
- data/lib/rigor/reflection/constant_ancestors.rb +97 -0
- data/lib/rigor/reflection/constant_path.rb +139 -0
- data/lib/rigor/reflection.rb +93 -110
- data/lib/rigor/runtime/jit.rb +6 -8
- data/lib/rigor/scope/discovery_index.rb +165 -2
- data/lib/rigor/scope.rb +1041 -32
- data/lib/rigor/sig_gen/alias_index.rb +289 -0
- data/lib/rigor/sig_gen/classification.rb +22 -5
- data/lib/rigor/sig_gen/declaration_equivalence.rb +127 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +202 -0
- data/lib/rigor/sig_gen/generator.rb +673 -53
- data/lib/rigor/sig_gen/inline_declarations.rb +184 -0
- data/lib/rigor/sig_gen/layout_index.rb +3 -4
- data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
- data/lib/rigor/sig_gen/method_candidate.rb +47 -3
- data/lib/rigor/sig_gen/observation_collector.rb +18 -16
- data/lib/rigor/sig_gen/path_mapper.rb +4 -6
- data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
- data/lib/rigor/sig_gen/renderer.rb +128 -14
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +154 -0
- data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
- data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
- data/lib/rigor/sig_gen/write_result.rb +19 -3
- data/lib/rigor/sig_gen/writer.rb +189 -41
- data/lib/rigor/sig_gen.rb +3 -0
- data/lib/rigor/signature_path_audit.rb +153 -6
- data/lib/rigor/source/literals.rb +0 -23
- data/lib/rigor/source/node_children.rb +0 -2
- data/lib/rigor/source/node_locator.rb +5 -11
- data/lib/rigor/source/node_walker.rb +0 -6
- data/lib/rigor/source/parameter_envelope.rb +72 -0
- data/lib/rigor/source.rb +1 -0
- data/lib/rigor/triage/catalogue.rb +1 -3
- data/lib/rigor/triage.rb +3 -5
- data/lib/rigor/type/accepts_result.rb +24 -6
- data/lib/rigor/type/combinator.rb +144 -5
- data/lib/rigor/type/data_class.rb +2 -2
- data/lib/rigor/type/data_instance.rb +4 -4
- data/lib/rigor/type/difference.rb +1 -0
- data/lib/rigor/type/float_range.rb +128 -0
- data/lib/rigor/type/hash_shape.rb +6 -6
- data/lib/rigor/type/integer_range.rb +13 -8
- data/lib/rigor/type/nominal.rb +8 -2
- data/lib/rigor/type/refined.rb +4 -3
- data/lib/rigor/type/struct_class.rb +3 -3
- data/lib/rigor/type/struct_instance.rb +4 -4
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/generic.rb +1 -1
- data/lib/rigor/type_node/range_literal.rb +25 -0
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node.rb +1 -0
- data/lib/rigor/version.rb +1 -1
- data/lib/rigor.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/effects.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +10 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +14 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +32 -30
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +96 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/effects.rb +65 -8
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +270 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +402 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +370 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_units.rb +132 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +264 -3
- data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
- data/plugins/rigor-actionpack/sig/action_view.rbs +43 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +220 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_index.rb +56 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +263 -0
- data/plugins/rigor-active-model-serializers/lib/rigor-active-model-serializers.rb +7 -0
- data/plugins/rigor-active-model-serializers/sig/active_model_serializers.rbs +45 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +2 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +10 -5
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +35 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +72 -12
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +383 -7
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +14 -6
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +322 -41
- data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +206 -31
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +26 -14
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +290 -19
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +3 -0
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +2 -0
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +1 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
- data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +141 -0
- data/plugins/rigor-grape/lib/rigor-grape.rb +6 -0
- data/plugins/rigor-grape/sig/grape.rbs +263 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +78 -5
- data/plugins/rigor-graphql/sig/graphql.rbs +485 -0
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +2 -3
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +12 -7
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +36 -37
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +0 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +5 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +3 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +10 -17
- data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
- data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline/same_line_annotations.rb +149 -0
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +324 -18
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
- data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +2 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +10 -5
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +3 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +51 -6
- data/plugins/rigor-sorbet/sig/sorbet.rbs +277 -0
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
- data/sig/rigor/analysis/baseline.rbs +69 -7
- data/sig/rigor/analysis/fact_store.rbs +1 -1
- data/sig/rigor/analysis/project_scan.rbs +74 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
- data/sig/rigor/cache.rbs +2 -3
- data/sig/rigor/effects/config_envelopes.rbs +100 -0
- data/sig/rigor/effects/effect_table.rbs +60 -0
- data/sig/rigor/effects/envelope.rbs +97 -0
- data/sig/rigor/effects/envelope_index.rbs +33 -0
- data/sig/rigor/effects/file_collection.rbs +81 -0
- data/sig/rigor/effects/label.rbs +26 -0
- data/sig/rigor/effects/label_set.rbs +46 -0
- data/sig/rigor/effects/method_key.rbs +24 -0
- data/sig/rigor/effects/origin.rbs +50 -0
- data/sig/rigor/effects/plugin_facts.rbs +141 -0
- data/sig/rigor/effects/registry.rbs +68 -0
- data/sig/rigor/effects/summary.rbs +50 -0
- data/sig/rigor/effects/taint_cause.rbs +12 -0
- data/sig/rigor/environment.rbs +17 -10
- data/sig/rigor/inference/optimistic_origin.rbs +10 -0
- data/sig/rigor/inference.rbs +9 -14
- data/sig/rigor/plugin/additional_initializer.rbs +36 -0
- data/sig/rigor/plugin/base.rbs +30 -7
- data/sig/rigor/plugin/effect_ancestry.rbs +27 -0
- data/sig/rigor/plugin/effect_attribution.rbs +66 -0
- data/sig/rigor/plugin/effect_edge.rbs +33 -0
- data/sig/rigor/plugin/effect_entry_points.rbs +25 -0
- data/sig/rigor/plugin/io_boundary.rbs +4 -0
- data/sig/rigor/plugin/loader.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +51 -11
- data/sig/rigor/plugin/protocol_contract.rbs +68 -0
- data/sig/rigor/plugin/registry.rbs +62 -1
- data/sig/rigor/plugin.rbs +1 -1
- data/sig/rigor/rbs_extended.rbs +1 -1
- data/sig/rigor/reflection.rbs +9 -6
- data/sig/rigor/scope.rbs +148 -12
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +21 -0
- data/sig/rigor/source.rbs +4 -4
- data/sig/rigor/testing.rbs +10 -4
- data/sig/rigor/type.rbs +33 -1
- data/sig/rigor.rbs +39 -20
- data/skills/rigor-ask/SKILL.md +16 -11
- data/skills/rigor-baseline-reduce/SKILL.md +4 -6
- data/skills/rigor-ci-setup/SKILL.md +17 -21
- data/skills/rigor-doctor/SKILL.md +24 -22
- data/skills/rigor-doctor/references/01-checks.md +97 -33
- data/skills/rigor-editor-setup/SKILL.md +6 -4
- data/skills/rigor-mcp-setup/SKILL.md +5 -4
- data/skills/rigor-monkeypatch-resolve/SKILL.md +5 -3
- data/skills/rigor-next-steps/SKILL.md +4 -2
- data/skills/rigor-plugin-author/SKILL.md +19 -23
- data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +9 -8
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +25 -27
- data/skills/rigor-plugin-author/references/03-test-and-ship.md +6 -8
- data/skills/rigor-plugin-review/SKILL.md +6 -4
- data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +2 -1
- data/skills/rigor-plugin-tune/SKILL.md +4 -2
- data/skills/rigor-project-init/SKILL.md +15 -10
- data/skills/rigor-project-init/references/01-detect.md +13 -9
- data/skills/rigor-project-init/references/02-configure.md +43 -14
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +14 -14
- data/skills/rigor-project-init/references/04-sig-uplift.md +27 -14
- data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
- data/skills/rigor-protection-uplift/SKILL.md +4 -6
- data/skills/rigor-rbs-setup/SKILL.md +4 -2
- data/skills/rigor-type-oracle/SKILL.md +226 -0
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +265 -0
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +125 -0
- data/skills/rigor-unused-adjudicate/SKILL.md +12 -7
- data/skills/rigor-upgrade/SKILL.md +13 -8
- metadata +133 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
|
|
5
|
+
require_relative "../source/node_walker"
|
|
6
|
+
require_relative "captured_locals"
|
|
7
|
+
|
|
8
|
+
module Rigor
|
|
9
|
+
module Inference
|
|
10
|
+
# The names of a {CapturedLocals.writes} set that a block body rebinds somewhere `StatementEvaluator` does
|
|
11
|
+
# not carry into the body's exit scope.
|
|
12
|
+
#
|
|
13
|
+
# The per-element fold's issue #587 (b) fixpoint reads each pass's exit binding out of that evaluator, so a
|
|
14
|
+
# rebind the exit never sees leaves the fixpoint on a binding the program has already moved past, and every
|
|
15
|
+
# position of the fold re-reads it. Issue #617's unmoved-pin floor caught such a name only while nothing else
|
|
16
|
+
# moved its binding: `seen = nil; [1, 2].find { |e| seen ||= 0; (seen += 1) == 2 }` threads the `||=`, so the
|
|
17
|
+
# fixpoint left the seed, and every predicate read `0 + 1 == 2`. A name this scan returns takes the floor
|
|
18
|
+
# whatever the fixpoint converged to.
|
|
19
|
+
#
|
|
20
|
+
# The scan admits a write from these positions only, each one the evaluator threads, measured against it
|
|
21
|
+
# rather than read off it:
|
|
22
|
+
#
|
|
23
|
+
# - a statement, and the body of `(…)`, `begin` / `rescue` / `else` / `ensure`;
|
|
24
|
+
# - an assignment's value — every variable-write form, and an index `||=` / `&&=` / `op=`, but not its
|
|
25
|
+
# receiver or index arguments;
|
|
26
|
+
# - an `if` / `unless` / `case` predicate and every branch, but not a `when` condition or an `in` guard;
|
|
27
|
+
# - both operands of `&&` / `||`;
|
|
28
|
+
# - a `while` / `until` predicate and body, a `for` index, collection and body;
|
|
29
|
+
# - a multi-assign value and its local and instance-variable targets, and the LOCAL a `rescue => e`
|
|
30
|
+
# reference, a `for` index, a pattern capture or `=~`'s named captures binds — the evaluator binds no
|
|
31
|
+
# class variable or global through any of them, and no instance variable outside a multi-assign;
|
|
32
|
+
# - a nested block or lambda body, for a local or an instance variable: the call's write-back fixpoint, which
|
|
33
|
+
# joins the block's `next` and `break` paths, or the escaping-block floor covers both, and nothing covers
|
|
34
|
+
# a class variable or global there.
|
|
35
|
+
#
|
|
36
|
+
# Every other position counts as unthreaded: a call's receiver and arguments, an array or hash literal, an
|
|
37
|
+
# interpolation, a `return` / `next` / `break` value, `rescue` modifier, `self.w =` (which rebinds `@w` with
|
|
38
|
+
# no write node at all), a `def` or class body. The list is a whitelist, so a position the evaluator gains
|
|
39
|
+
# later floors a name needlessly rather than letting a pin through. Issue #1223 is such a gain: the evaluator
|
|
40
|
+
# threads a call's receiver and arguments, the literals, the interpolations and a `rescue` modifier
|
|
41
|
+
# ({StatementEvaluator#eval_call}), and the scan does not admit them yet.
|
|
42
|
+
#
|
|
43
|
+
# A threaded rebind can still miss the exit binding when a jump leaves after it. The fold's pass and a
|
|
44
|
+
# nested block's write-back join every block-level `next` (and the write-back every `break`) into the exit
|
|
45
|
+
# ({StatementEvaluator#evaluate_invocation}), but a `while` / `until` / `for` loop's continuation joins its
|
|
46
|
+
# `next` and `break` paths only in part ({StatementEvaluator#loop_iteration}): a `for` body runs one pass,
|
|
47
|
+
# and a name outside the `while` / `until` rebind fixpoint (an instance variable) sees the first pass's
|
|
48
|
+
# `next` exits and no `break` — so the rebind such a branch made can still drop out — and `redo` / `retry`
|
|
49
|
+
# re-enter code with bindings no join sees. A rebind that textually precedes such a jump is therefore
|
|
50
|
+
# unthreaded too; the body runs forward-only, so a rebind after every jump cannot be on a jump's path, and
|
|
51
|
+
# a jump that is its construct's final statement leaves with the exit scope itself. A rebind in an `ensure`
|
|
52
|
+
# runs after any jump in its `begin`, so under any such jump it counts as preceding one.
|
|
53
|
+
module UnthreadedRebinds
|
|
54
|
+
VARIABLE_WRITE_NODES = (CapturedLocals::LOCAL_WRITE_NODES | CapturedLocals::NON_LOCAL_WRITE_NODES).freeze
|
|
55
|
+
private_constant :VARIABLE_WRITE_NODES
|
|
56
|
+
|
|
57
|
+
TARGET_NODES = Set[
|
|
58
|
+
Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode,
|
|
59
|
+
Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode
|
|
60
|
+
].freeze
|
|
61
|
+
private_constant :TARGET_NODES
|
|
62
|
+
|
|
63
|
+
VALUE_ONLY = { value: :code }.freeze
|
|
64
|
+
private_constant :VALUE_ONLY
|
|
65
|
+
|
|
66
|
+
# Parent class => the child fields the evaluator threads, each with how it is entered: `:code` threads
|
|
67
|
+
# writes, `:binding` threads only the targets the construct binds ({#binding_target_threaded?}),
|
|
68
|
+
# `:ensure` is an `ensure` body, `:loop` a loop body, `:nested` a call's block and `:lambda` a lambda's
|
|
69
|
+
# body.
|
|
70
|
+
THREADED_FIELDS = {
|
|
71
|
+
Prism::StatementsNode => { body: :code },
|
|
72
|
+
Prism::ParenthesesNode => { body: :code },
|
|
73
|
+
Prism::BeginNode => { statements: :code, rescue_clause: :code, else_clause: :code, ensure_clause: :code },
|
|
74
|
+
Prism::RescueNode => { reference: :binding, statements: :code, subsequent: :code },
|
|
75
|
+
Prism::EnsureNode => { statements: :ensure },
|
|
76
|
+
Prism::ElseNode => { statements: :code },
|
|
77
|
+
Prism::IfNode => { predicate: :code, statements: :code, subsequent: :code },
|
|
78
|
+
Prism::UnlessNode => { predicate: :code, statements: :code, else_clause: :code },
|
|
79
|
+
Prism::CaseNode => { predicate: :code, conditions: :code, else_clause: :code },
|
|
80
|
+
Prism::WhenNode => { statements: :code },
|
|
81
|
+
Prism::CaseMatchNode => { predicate: :code, conditions: :code, else_clause: :code },
|
|
82
|
+
Prism::InNode => { pattern: :binding, statements: :code },
|
|
83
|
+
Prism::AndNode => { left: :code, right: :code },
|
|
84
|
+
Prism::OrNode => { left: :code, right: :code },
|
|
85
|
+
Prism::MatchPredicateNode => { value: :code, pattern: :binding },
|
|
86
|
+
Prism::MatchRequiredNode => { value: :code, pattern: :binding },
|
|
87
|
+
Prism::MatchWriteNode => { targets: :binding },
|
|
88
|
+
Prism::MultiWriteNode => { lefts: :binding, rest: :binding, rights: :binding, value: :code },
|
|
89
|
+
Prism::WhileNode => { predicate: :code, statements: :loop },
|
|
90
|
+
Prism::UntilNode => { predicate: :code, statements: :loop },
|
|
91
|
+
Prism::ForNode => { index: :binding, collection: :code, statements: :loop },
|
|
92
|
+
Prism::CallNode => { block: :nested },
|
|
93
|
+
Prism::BlockNode => { body: :code },
|
|
94
|
+
Prism::LambdaNode => { body: :lambda },
|
|
95
|
+
Prism::IndexOrWriteNode => VALUE_ONLY,
|
|
96
|
+
Prism::IndexAndWriteNode => VALUE_ONLY,
|
|
97
|
+
Prism::IndexOperatorWriteNode => VALUE_ONLY,
|
|
98
|
+
**(VARIABLE_WRITE_NODES - TARGET_NODES).to_h { |klass| [klass, VALUE_ONLY] }
|
|
99
|
+
}.transform_values(&:freeze).freeze
|
|
100
|
+
private_constant :THREADED_FIELDS
|
|
101
|
+
|
|
102
|
+
# The jumps whose path the exit binding misses, per construct. A block's `next` and `break` are joined
|
|
103
|
+
# ({StatementEvaluator#evaluate_invocation}, the write-back), and a `break` out of the fold's own call ends
|
|
104
|
+
# it, so no later iteration reads what it carried; `redo` and `retry` are joined nowhere. A loop joins
|
|
105
|
+
# its `next` and `break` paths only in part, so both still count.
|
|
106
|
+
BLOCK_JUMPS = Set[Prism::RedoNode, Prism::RetryNode].freeze
|
|
107
|
+
LOOP_JUMPS = (BLOCK_JUMPS | [Prism::NextNode, Prism::BreakNode]).freeze
|
|
108
|
+
private_constant :BLOCK_JUMPS, :LOOP_JUMPS
|
|
109
|
+
|
|
110
|
+
# The constructs that own the jumps inside them.
|
|
111
|
+
JUMP_BOUNDARY_NODES = Set[
|
|
112
|
+
Prism::BlockNode, Prism::LambdaNode, Prism::DefNode,
|
|
113
|
+
Prism::WhileNode, Prism::UntilNode, Prism::ForNode
|
|
114
|
+
].freeze
|
|
115
|
+
private_constant :JUMP_BOUNDARY_NODES
|
|
116
|
+
|
|
117
|
+
NO_JUMP = -1
|
|
118
|
+
private_constant :NO_JUMP
|
|
119
|
+
|
|
120
|
+
EMPTY = Set.new.freeze
|
|
121
|
+
private_constant :EMPTY
|
|
122
|
+
|
|
123
|
+
# How a walk position reaches the exit scope: whether a local rebind there is threaded, whether an
|
|
124
|
+
# instance variable's is, whether a class variable's or global's is, the offset below which a rebind
|
|
125
|
+
# precedes a jump,
|
|
126
|
+
# whether the position binds targets only (`:multi_assign` under a multi-assign, `:capture` under any
|
|
127
|
+
# other binding construct, false otherwise), and the locals a nested block shadows.
|
|
128
|
+
Position = Data.define(:local, :ivar, :other, :horizon, :binding, :shadowed)
|
|
129
|
+
private_constant :Position
|
|
130
|
+
|
|
131
|
+
module_function
|
|
132
|
+
|
|
133
|
+
# @param names — the rebound names from {CapturedLocals.writes}, sigil and all.
|
|
134
|
+
# @return the subset of `names` the body rebinds somewhere the exit scope misses.
|
|
135
|
+
def names(block_node, names)
|
|
136
|
+
body = block_node.body
|
|
137
|
+
return EMPTY if body.nil? || names.empty?
|
|
138
|
+
|
|
139
|
+
wanted = names.to_set
|
|
140
|
+
found = Set.new
|
|
141
|
+
horizon = last_jump_offset(body, BLOCK_JUMPS)
|
|
142
|
+
start = Position.new(local: true, ivar: true, other: true, horizon: horizon, binding: false, shadowed: EMPTY)
|
|
143
|
+
visit(body, start, wanted, found)
|
|
144
|
+
found
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def visit(node, position, wanted, found)
|
|
148
|
+
return unless node.is_a?(Prism::Node)
|
|
149
|
+
return if node.is_a?(Prism::DefinedNode)
|
|
150
|
+
|
|
151
|
+
check(node, position, wanted, found)
|
|
152
|
+
return node.rigor_each_child { |child| visit(child, position, wanted, found) } if position.binding
|
|
153
|
+
|
|
154
|
+
fields = THREADED_FIELDS[node.class]
|
|
155
|
+
return node.rigor_each_child { |child| visit_unthreaded(child, wanted, found) } if fields.nil?
|
|
156
|
+
|
|
157
|
+
threaded = threaded_children(node, fields)
|
|
158
|
+
node.rigor_each_child do |child|
|
|
159
|
+
mode = threaded[child]
|
|
160
|
+
if mode
|
|
161
|
+
visit(child, enter(node, child, mode, position), wanted, found)
|
|
162
|
+
else
|
|
163
|
+
visit_unthreaded(child, wanted, found)
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def threaded_children(node, fields)
|
|
169
|
+
threaded = {}.compare_by_identity
|
|
170
|
+
fields.each do |field, mode|
|
|
171
|
+
value = node.public_send(field)
|
|
172
|
+
if value.is_a?(Array)
|
|
173
|
+
value.each { |child| threaded[child] = mode }
|
|
174
|
+
elsif value
|
|
175
|
+
threaded[value] = mode
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
threaded
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Every rebind under an unthreaded position misses the exit scope, whatever the position beneath it.
|
|
182
|
+
def visit_unthreaded(node, wanted, found)
|
|
183
|
+
Source::NodeWalker.each(node) do |descendant|
|
|
184
|
+
name = rebound_name(descendant)
|
|
185
|
+
found << name if name && wanted.include?(name)
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def enter(parent, child, mode, position)
|
|
190
|
+
case mode
|
|
191
|
+
when :binding then position.with(binding: parent.is_a?(Prism::MultiWriteNode) ? :multi_assign : :capture)
|
|
192
|
+
when :ensure then position.with(horizon: position.horizon == NO_JUMP ? NO_JUMP : Float::INFINITY)
|
|
193
|
+
when :loop then extend_horizon(position, child, LOOP_JUMPS)
|
|
194
|
+
when :nested then nested_block(child, position)
|
|
195
|
+
when :lambda then nested_body(parent, child, position)
|
|
196
|
+
else position
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def extend_horizon(position, body, kinds)
|
|
201
|
+
position.with(horizon: [position.horizon, last_jump_offset(body, kinds)].max)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# A call's block. A block-pass argument (`&blk`) carries no body the call's write-back reads.
|
|
205
|
+
def nested_block(child, position)
|
|
206
|
+
return position.with(local: false, ivar: false, other: false) unless child.is_a?(Prism::BlockNode)
|
|
207
|
+
|
|
208
|
+
nested_body(child, child.body, position)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# A block or lambda body: its parameters and block-locals shadow, its own jumps join the horizon, and a
|
|
212
|
+
# class variable's or global's rebind is no longer carried out.
|
|
213
|
+
def nested_body(owner, body, position)
|
|
214
|
+
extend_horizon(position, body, BLOCK_JUMPS).with(
|
|
215
|
+
other: false, shadowed: position.shadowed | CapturedLocals.introduced_locals(owner)
|
|
216
|
+
)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def check(node, position, wanted, found)
|
|
220
|
+
name = rebound_name(node)
|
|
221
|
+
return if name.nil? || !wanted.include?(name)
|
|
222
|
+
return if position.shadowed.include?(name)
|
|
223
|
+
|
|
224
|
+
found << name unless threaded_rebind?(node, name, position)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def threaded_rebind?(node, name, position)
|
|
228
|
+
return false unless VARIABLE_WRITE_NODES.include?(node.class)
|
|
229
|
+
return false unless TARGET_NODES.include?(node.class) == (position.binding ? true : false)
|
|
230
|
+
|
|
231
|
+
kind = CapturedLocals.variable_kind(name)
|
|
232
|
+
return false if position.binding && !binding_target_threaded?(kind, position.binding)
|
|
233
|
+
return false unless kind_threaded?(kind, position)
|
|
234
|
+
|
|
235
|
+
node.location.start_offset >= position.horizon
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def kind_threaded?(kind, position)
|
|
239
|
+
case kind
|
|
240
|
+
when :local then position.local
|
|
241
|
+
when :ivar then position.ivar
|
|
242
|
+
else position.other
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# A binding construct's target reaches the exit scope only for the kinds the evaluator binds there: a
|
|
247
|
+
# multi-assign binds locals and instance variables, and a `rescue =>` reference, a `for` index, a pattern
|
|
248
|
+
# capture or a named capture binds locals only. `a, $g = 1, :z` leaves `$g` where it was.
|
|
249
|
+
def binding_target_threaded?(kind, binding)
|
|
250
|
+
kind == :local || (kind == :ivar && binding == :multi_assign)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def rebound_name(node)
|
|
254
|
+
return node.name if VARIABLE_WRITE_NODES.include?(node.class)
|
|
255
|
+
|
|
256
|
+
CapturedLocals.setter_ivar_name(node)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# The start offset of the last `kinds` jump that belongs to the construct whose body is `body`, or
|
|
260
|
+
# {NO_JUMP}. A jump that is the body's final statement is skipped: it leaves with the exit scope itself.
|
|
261
|
+
def last_jump_offset(body, kinds)
|
|
262
|
+
return NO_JUMP if body.nil?
|
|
263
|
+
|
|
264
|
+
final = body.body.last if body.is_a?(Prism::StatementsNode)
|
|
265
|
+
offset = NO_JUMP
|
|
266
|
+
each_owned_jump(body, kinds) do |jump|
|
|
267
|
+
offset = [offset, jump.location.start_offset].max unless jump.equal?(final)
|
|
268
|
+
end
|
|
269
|
+
offset
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def each_owned_jump(node, kinds, &)
|
|
273
|
+
yield node if kinds.include?(node.class)
|
|
274
|
+
return if node.is_a?(Prism::DefinedNode)
|
|
275
|
+
|
|
276
|
+
node.rigor_each_child do |child|
|
|
277
|
+
each_owned_jump(child, kinds, &) unless JUMP_BOUNDARY_NODES.include?(child.class)
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
end
|
|
@@ -48,6 +48,8 @@ module Rigor
|
|
|
48
48
|
# with the running Ruby**, where "the analyzer's Ruby is the target's Ruby" already covers them.
|
|
49
49
|
# A third-party gem's `VERSION` is not read: its version is the project's `Gemfile.lock`'s to say,
|
|
50
50
|
# and the lockfile is not visible from the inference layer.
|
|
51
|
+
# * The ROOTED spelling of any of the above — `::RUBY_VERSION`, `::RUBY_ENGINE`, `::Psych::VERSION` —
|
|
52
|
+
# names the same constant as its bare twin and folds identically; see {.read_constant}.
|
|
51
53
|
# * `RUBY_PLATFORM` — **never** folded. Every comparison against it is by construction
|
|
52
54
|
# platform-dependent, and the machine running `rigor` need not be the machine running the program.
|
|
53
55
|
# * `<=>` — never folded (it yields -1/0/1, not a branch verdict), and neither are `defined?(Ractor)`
|
|
@@ -68,7 +70,7 @@ module Rigor
|
|
|
68
70
|
|
|
69
71
|
# Predefined constants read from the analyzer's runtime, mapped to the operand kind they produce.
|
|
70
72
|
# `:string` operands accept every comparison; `:engine` operands accept equality only.
|
|
71
|
-
PREDEFINED = { RUBY_VERSION
|
|
73
|
+
PREDEFINED = { "RUBY_VERSION" => :string, "RUBY_ENGINE" => :engine }.freeze
|
|
72
74
|
private_constant :PREDEFINED
|
|
73
75
|
|
|
74
76
|
# Operand kinds that carry a plain Ruby String, i.e. the ones `Gem::Version.new` may wrap and the
|
|
@@ -88,8 +90,8 @@ module Rigor
|
|
|
88
90
|
GEM_VERSION_PATH = "Gem::Version"
|
|
89
91
|
private_constant :GEM_VERSION_PATH
|
|
90
92
|
|
|
91
|
-
# @param node
|
|
92
|
-
# @return
|
|
93
|
+
# @param node — an `if` / `unless` predicate
|
|
94
|
+
# @return `:truthy` / `:falsey` when the guard is decidable on the analyzer's Ruby,
|
|
93
95
|
# otherwise nil (both arms stay live)
|
|
94
96
|
def verdict(node)
|
|
95
97
|
return nil unless node.is_a?(Prism::CallNode)
|
|
@@ -109,35 +111,40 @@ module Rigor
|
|
|
109
111
|
end
|
|
110
112
|
|
|
111
113
|
# Reads one side of the comparison into `[kind, value]`, or nil when it is not readable.
|
|
112
|
-
#
|
|
113
|
-
# @return [Array(Symbol, Object), nil]
|
|
114
114
|
def read_operand(node)
|
|
115
115
|
case node
|
|
116
116
|
when Prism::StringNode then [:literal_string, node.unescaped]
|
|
117
|
-
when Prism::ConstantReadNode then
|
|
118
|
-
when Prism::ConstantPathNode then read_version_constant(node)
|
|
117
|
+
when Prism::ConstantReadNode, Prism::ConstantPathNode then read_constant(node)
|
|
119
118
|
when Prism::CallNode then read_gem_version(node)
|
|
120
119
|
end
|
|
121
120
|
end
|
|
122
121
|
private_class_method :read_operand
|
|
123
122
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
def
|
|
123
|
+
# Dispatches on the RESOLVED name rather than on the node class. `::RUBY_VERSION` names the very
|
|
124
|
+
# constant `RUBY_VERSION` names — the leading `::` only makes the top-level lookup explicit — but
|
|
125
|
+
# Prism spells it as a `ConstantPathNode` with a nil parent, so a node-class dispatch sent it down
|
|
126
|
+
# the `X::VERSION` path and declined it for not being a curated qualified name. The same program
|
|
127
|
+
# then had two diagnostic sets for two spellings of one guard
|
|
128
|
+
# ([#877](https://github.com/rigortype/rigor/issues/877)).
|
|
129
|
+
#
|
|
130
|
+
# The foldable set is unchanged and stays closed: an unqualified name folds only when it is one of
|
|
131
|
+
# {PREDEFINED}, a qualified one only when it is in {VERSION_CONSTANTS}.
|
|
132
|
+
def read_constant(node)
|
|
134
133
|
path = Source::ConstantPath.qualified_name_or_nil(node)
|
|
135
|
-
return nil unless path
|
|
134
|
+
return nil unless path
|
|
135
|
+
|
|
136
|
+
kind =
|
|
137
|
+
if path.include?("::")
|
|
138
|
+
:string if VERSION_CONSTANTS.include?(path)
|
|
139
|
+
else
|
|
140
|
+
PREDEFINED[path]
|
|
141
|
+
end
|
|
142
|
+
return nil unless kind
|
|
136
143
|
|
|
137
144
|
value = runtime_value(path)
|
|
138
|
-
value && [
|
|
145
|
+
value && [kind, value]
|
|
139
146
|
end
|
|
140
|
-
private_class_method :
|
|
147
|
+
private_class_method :read_constant
|
|
141
148
|
|
|
142
149
|
# `Gem::Version.new(<readable>)`. The inner operand must be a plain version String — an engine name
|
|
143
150
|
# is not a version, and `Gem::Version.new` raises on anything `Gem::Version.correct?` rejects, so a
|
|
@@ -165,7 +172,7 @@ module Rigor
|
|
|
165
172
|
# `const_get` would execute the target file inside the analyzer. `Module#autoload?` is what
|
|
166
173
|
# separates the two ([#680](https://github.com/rigortype/rigor/issues/680)).
|
|
167
174
|
#
|
|
168
|
-
# @return
|
|
175
|
+
# @return the constant's value when it is a non-empty String
|
|
169
176
|
def runtime_value(path)
|
|
170
177
|
mod = ::Object
|
|
171
178
|
path.split("::").each do |part|
|
|
@@ -9,11 +9,11 @@ module Rigor
|
|
|
9
9
|
# consistency, normalization, or erasure.
|
|
10
10
|
#
|
|
11
11
|
# @!attribute class_name
|
|
12
|
-
# @return
|
|
12
|
+
# @return the receiver class whose method declared `-> void`.
|
|
13
13
|
# @!attribute method_name
|
|
14
|
-
# @return
|
|
14
|
+
# @return the method whose return was recovered.
|
|
15
15
|
# @!attribute kind
|
|
16
|
-
# @return
|
|
16
|
+
# @return `:instance` or `:singleton`.
|
|
17
17
|
VoidOrigin = Data.define(:class_name, :method_name, :kind) do
|
|
18
18
|
# A human-facing `Class#method` / `Class.method` label for the diagnostic message.
|
|
19
19
|
def label
|
|
@@ -75,10 +75,8 @@ module Rigor
|
|
|
75
75
|
# consult; the memo persists across consults for the run. The memo is probed before the visited
|
|
76
76
|
# set is allocated so the common post-warmup hit path allocates nothing.
|
|
77
77
|
#
|
|
78
|
-
# @param
|
|
79
|
-
# @param
|
|
80
|
-
# @param kind [Symbol] `:instance` or `:singleton`.
|
|
81
|
-
# @return [Inference::VoidOrigin, nil]
|
|
78
|
+
# @param owner — the qualified receiver class the `def` belongs to.
|
|
79
|
+
# @param kind — `:instance` or `:singleton`.
|
|
82
80
|
def origin_for(def_node, owner, kind)
|
|
83
81
|
return nil if def_node.nil?
|
|
84
82
|
|
|
@@ -51,7 +51,7 @@ module Rigor
|
|
|
51
51
|
# computed from the stale text would be wrong. Consumers check {#desynchronized?} and decline to answer
|
|
52
52
|
# rather than answering wrongly; the mark clears on the next `didOpen` or full-text change.
|
|
53
53
|
#
|
|
54
|
-
# @return
|
|
54
|
+
# @return true when the changes were applied.
|
|
55
55
|
def apply_changes(uri:, changes:, version:)
|
|
56
56
|
text = IncrementalSync.apply_all(@entries[uri]&.bytes, changes)
|
|
57
57
|
@desynchronized.delete(uri)
|
|
@@ -63,13 +63,13 @@ module Rigor
|
|
|
63
63
|
false
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
# @return
|
|
66
|
+
# @return true when the last `didChange` for `uri` could not be applied, so the held text no
|
|
67
67
|
# longer matches the editor's.
|
|
68
68
|
def desynchronized?(uri)
|
|
69
69
|
@desynchronized.key?(uri)
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
-
# @return
|
|
72
|
+
# @return why `uri` is desynchronised, for the log line; nil when it is in sync.
|
|
73
73
|
def desynchronization_reason(uri)
|
|
74
74
|
@desynchronized[uri]
|
|
75
75
|
end
|
|
@@ -91,7 +91,7 @@ module Rigor
|
|
|
91
91
|
@dirty.delete(uri)
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
-
# @return
|
|
94
|
+
# @return true when `uri` has unsaved changes. A buffer that is dirty may only be published
|
|
95
95
|
# from an analysis that bound ITS bytes — see the publish-set invariant in
|
|
96
96
|
# `docs/design/20260517-language-server.md`.
|
|
97
97
|
def dirty?(uri)
|
|
@@ -42,7 +42,7 @@ module Rigor
|
|
|
42
42
|
@project_context = project_context
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
# @return
|
|
45
|
+
# @return LSP `CompletionItem[]` or nil
|
|
46
46
|
# when the cursor isn't at a position the provider can
|
|
47
47
|
# enumerate completions for. Returning nil maps to
|
|
48
48
|
# `result: null` per the LSP spec — clients treat it as
|
|
@@ -30,13 +30,13 @@ module Rigor
|
|
|
30
30
|
hint: 4
|
|
31
31
|
}.freeze
|
|
32
32
|
|
|
33
|
-
# @param debouncer
|
|
33
|
+
# @param debouncer —
|
|
34
34
|
# when present, `publish_for` schedules its work through
|
|
35
35
|
# the debouncer (cancels prior pending task for the same
|
|
36
36
|
# URI, fires after `debounce_seconds` quiet-time). Nil
|
|
37
37
|
# keeps the slice 4-7 synchronous behaviour — primarily
|
|
38
38
|
# useful for specs.
|
|
39
|
-
# @param debounce_seconds
|
|
39
|
+
# @param debounce_seconds — quiet-time before the
|
|
40
40
|
# debounced publish fires. 0 with a debouncer means
|
|
41
41
|
# "schedule on next-tick" (still async); without a
|
|
42
42
|
# debouncer the value is unused.
|
|
@@ -212,7 +212,7 @@ module Rigor
|
|
|
212
212
|
notify(uri, diagnostics)
|
|
213
213
|
end
|
|
214
214
|
|
|
215
|
-
# @return
|
|
215
|
+
# @return `{ uri:, path:, bytes: }` when `uri` is eligible for the batch, or nil to
|
|
216
216
|
# exclude it. Mirrors `#run_and_notify`'s own guards: a buffer closed during the debounce window is
|
|
217
217
|
# dropped silently (its didClose empty publish already cleared the markers); a desynchronised buffer
|
|
218
218
|
# publishes an EMPTY set immediately (same as the single-buffer path) rather than joining the batch.
|
|
@@ -268,8 +268,9 @@ module Rigor
|
|
|
268
268
|
|
|
269
269
|
# Runs `Analysis::Runner` with a `BufferBinding` so the buffer bytes (instead of the on-disk file) drive
|
|
270
270
|
# the parse. The `Rigor::Analysis::ProjectScan` cached on the ProjectContext is passed through
|
|
271
|
-
# `prebuilt:` so plugin `#prepare`, the dependency-source walker,
|
|
272
|
-
# project-patched scanners do not re-run per publish
|
|
271
|
+
# `prebuilt:` so plugin `#prepare`, the dependency-source walker, the synthetic-method /
|
|
272
|
+
# project-patched scanners and (#1038) the template-unit transforms do not re-run per publish — the
|
|
273
|
+
# last of those is an Erubi compile of every view for an ERB project. The snapshot rebuilds only when
|
|
273
274
|
# `ProjectContext#invalidate!` fires (watched-file or configuration change). Returns the LSP-shaped
|
|
274
275
|
# Diagnostic Array, ready to serialize into the notification's `params.diagnostics` field.
|
|
275
276
|
def run_analysis(path:, bytes:)
|
|
@@ -320,7 +321,7 @@ module Rigor
|
|
|
320
321
|
end
|
|
321
322
|
end
|
|
322
323
|
|
|
323
|
-
# @return
|
|
324
|
+
# @return the LSP `Diagnostic` Hash, or nil to
|
|
324
325
|
# skip diagnostics outside the buffer's own path (e.g.
|
|
325
326
|
# `.rigor.yml`-anchored info diagnostics get filtered —
|
|
326
327
|
# they belong to the project, not the buffer).
|
|
@@ -30,7 +30,7 @@ module Rigor
|
|
|
30
30
|
@project_context = project_context
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
# @return
|
|
33
|
+
# @return LSP `DocumentSymbol[]` for the
|
|
34
34
|
# buffer at `uri`. Returns nil when the URI isn't open or
|
|
35
35
|
# doesn't parse cleanly enough to surface symbols — LSP
|
|
36
36
|
# clients fall back to no-outline in that case.
|
|
@@ -23,7 +23,7 @@ module Rigor
|
|
|
23
23
|
@project_context = project_context
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
# @return
|
|
26
|
+
# @return LSP `FoldingRange[]` for the
|
|
27
27
|
# buffer, or nil when the URI isn't open / parseable.
|
|
28
28
|
def provide(uri)
|
|
29
29
|
path, entry = buffer_for(uri)
|
|
@@ -29,7 +29,7 @@ module Rigor
|
|
|
29
29
|
@renderer = renderer
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
# @return
|
|
32
|
+
# @return an LSP `Hover` payload or nil when no
|
|
33
33
|
# expression sits at the queried position. Returning nil
|
|
34
34
|
# maps to `result: null` per the LSP spec — clients
|
|
35
35
|
# suppress the hover popup in that case.
|
|
@@ -13,7 +13,7 @@ module Rigor
|
|
|
13
13
|
# (method call, constant, local, ivar, literal) gets the most relevant type-aware presentation per
|
|
14
14
|
# `docs/design/20260517-lsp-hover-completion.md`.
|
|
15
15
|
class HoverRenderer
|
|
16
|
-
# @param node_scope_lookup
|
|
16
|
+
# @param node_scope_lookup — node-to-scope table built by `ScopeIndexer.index`. The renderer indexes
|
|
17
17
|
# into it to retrieve the receiver's narrow scope when specialising on `CallNode`. The lookup never
|
|
18
18
|
# returns nil (the indexer's Hash carries `default_scope` as its default value), so the renderer trusts
|
|
19
19
|
# the lookup result.
|
|
@@ -83,9 +83,9 @@ module Rigor
|
|
|
83
83
|
)
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
-
# @return [
|
|
87
|
-
#
|
|
88
|
-
#
|
|
86
|
+
# @return `[definition, receiver_class_name, kind]` — the resolved method definition (nil when the
|
|
87
|
+
# method doesn't resolve through the RBS env), the receiver class name, and the dispatch kind
|
|
88
|
+
# (`:instance` or `:singleton`); `[nil, nil, nil]` when the receiver shape isn't yet supported.
|
|
89
89
|
def lookup_method(receiver_type, method_name, scope)
|
|
90
90
|
case receiver_type
|
|
91
91
|
when Type::Singleton
|
|
@@ -46,10 +46,10 @@ module Rigor
|
|
|
46
46
|
# Applies every change in order, each against the result of the previous — the LSP contract for a
|
|
47
47
|
# multi-change `didChange` notification.
|
|
48
48
|
#
|
|
49
|
-
# @param text
|
|
50
|
-
# @param changes
|
|
51
|
-
# @return
|
|
52
|
-
# @raise
|
|
49
|
+
# @param text — the held buffer text; nil when no buffer is open for the URI.
|
|
50
|
+
# @param changes — the `contentChanges` array.
|
|
51
|
+
# @return the new buffer text.
|
|
52
|
+
# @raise UnappliableChange — if any change cannot be applied.
|
|
53
53
|
def apply_all(text, changes)
|
|
54
54
|
raise UnappliableChange, "contentChanges must be an Array, got #{changes.class}" unless changes.is_a?(Array)
|
|
55
55
|
|
|
@@ -64,7 +64,7 @@ module Rigor
|
|
|
64
64
|
# companion to `range` and is accepted-but-ignored: it is redundant with `range`, historically ambiguous
|
|
65
65
|
# about its units, and `range` is the authoritative field.
|
|
66
66
|
#
|
|
67
|
-
# @raise
|
|
67
|
+
# @raise UnappliableChange
|
|
68
68
|
def apply(text, change)
|
|
69
69
|
raise UnappliableChange, "contentChanges entry must be a Hash, got #{change.class}" unless change.is_a?(Hash)
|
|
70
70
|
|
|
@@ -95,7 +95,7 @@ module Rigor
|
|
|
95
95
|
raise UnappliableChange, "held text is not valid UTF-8: #{e.message}"
|
|
96
96
|
end
|
|
97
97
|
|
|
98
|
-
# @return
|
|
98
|
+
# @return one `[content_start, content_end]` pair per line, in Ruby
|
|
99
99
|
# character indices. `content_end` excludes the line terminator, so it doubles as the clamp target for
|
|
100
100
|
# an over-long `character`. A trailing terminator yields a final empty span — the virtual last line an
|
|
101
101
|
# editor puts the cursor on, and the anchor for an end-of-document insert.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../environment"
|
|
4
|
+
require_relative "../project_environment"
|
|
4
5
|
require_relative "../cache/store"
|
|
5
6
|
require_relative "../analysis/runner"
|
|
6
7
|
require_relative "../analysis/incremental_session"
|
|
@@ -11,12 +12,14 @@ module Rigor
|
|
|
11
12
|
# Per-session cache of the project-wide analyzer state the LSP reads on every request — chiefly the
|
|
12
13
|
# `Environment` (with its ~100-300ms RBS env build), a read-only `Cache::Store` that lets the runner hit
|
|
13
14
|
# the on-disk RBS cache without writing back, and (since the pre-pass cache slice) a frozen
|
|
14
|
-
# {Rigor::Analysis::ProjectScan} snapshot covering the plugin registry, dependency-source index,
|
|
15
|
-
# pre-pass scanner outputs.
|
|
15
|
+
# {Rigor::Analysis::ProjectScan} snapshot covering the plugin registry, dependency-source index,
|
|
16
|
+
# pre-pass scanner outputs and (since #1038) the compiled template-unit index.
|
|
16
17
|
#
|
|
17
18
|
# The pre-pass scan lets `DiagnosticPublisher#run_analysis` build a `Runner` with `prebuilt:` so per-buffer
|
|
18
19
|
# publishes skip plugin `#prepare`, the synthetic-method scanner, the project-patched scanner, and the
|
|
19
|
-
# dependency-source walker
|
|
20
|
+
# dependency-source walker — and, since #1038, the template-unit transform: a publish recompiles only a
|
|
21
|
+
# template that moved on disk since the scan was built (plus the buffer's own, always), rather than every
|
|
22
|
+
# view in the project. For projects with substrate plugins / opt-in dependency source / sizeable
|
|
20
23
|
# `pre_eval:` configuration this cuts publish wall time substantially — for the trivial case the savings
|
|
21
24
|
# are small (the per-publish path is already ≈2ms once Environment is warm).
|
|
22
25
|
#
|
|
@@ -74,11 +77,7 @@ module Rigor
|
|
|
74
77
|
dependency_source_index: project_scan.dependency_source_index,
|
|
75
78
|
synthetic_method_index: project_scan.synthetic_method_index,
|
|
76
79
|
project_patched_methods: project_scan.project_patched_methods,
|
|
77
|
-
|
|
78
|
-
bundler_auto_detect: @configuration.bundler_auto_detect,
|
|
79
|
-
bundler_lockfile: @configuration.bundler_lockfile,
|
|
80
|
-
rbs_collection_lockfile: @configuration.rbs_collection_lockfile,
|
|
81
|
-
rbs_collection_auto_detect: @configuration.rbs_collection_auto_detect
|
|
80
|
+
**ProjectEnvironment.dependency_discovery_options(@configuration)
|
|
82
81
|
)
|
|
83
82
|
end
|
|
84
83
|
|
|
@@ -15,10 +15,10 @@ module Rigor
|
|
|
15
15
|
# dropped. The same claim/consume shape `DiagnosticPublisher#run_project_round` already uses for the
|
|
16
16
|
# whole-project save round (#246), generalised to an arbitrary key type and an arbitrary batch action.
|
|
17
17
|
class PublishBatcher
|
|
18
|
-
# @param on_batch
|
|
18
|
+
# @param on_batch — `(keys) -> void`, called with the deduplicated Array of keys pending at the
|
|
19
19
|
# start of one round. May be called more than once in a row when keys keep arriving while a round
|
|
20
20
|
# runs.
|
|
21
|
-
# @param on_error
|
|
21
|
+
# @param on_error — `(exception) -> void`, called when `on_batch` raises. A round must
|
|
22
22
|
# never wedge the coalescing lock for every future `#enqueue` call — ownership is always released
|
|
23
23
|
# before this fires. Whatever was mid-flight when the round raised is lost; anything enqueued by a
|
|
24
24
|
# concurrent `#enqueue` call after this round's drain but before the rescue stays pending and rides
|