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
data/lib/rigor/configuration.rb
CHANGED
|
@@ -62,6 +62,10 @@ module Rigor
|
|
|
62
62
|
"disable" => [],
|
|
63
63
|
"libraries" => [],
|
|
64
64
|
"signature_paths" => nil,
|
|
65
|
+
# Issue #2517 — a core signature directory loaded IN PLACE OF the `rbs` gem's `core/` (a provider such as
|
|
66
|
+
# rbs-shifter writes one for an older CRuby). `nil` (the default) loads the gem's core. Unlike
|
|
67
|
+
# `signature_paths:`, which only adds, this replaces: the default core is not loaded beside it.
|
|
68
|
+
"core_root" => nil,
|
|
65
69
|
# The project's test roots: the directories (or files) holding its tests. `nil` (the default) auto-detects
|
|
66
70
|
# whichever of `spec/` and `test/` exist, the same way `signature_paths: nil` auto-detects `sig/`; `[]`
|
|
67
71
|
# declares that the project has none. `rigor sig-gen --params=observed` harvests call-site observations
|
|
@@ -77,10 +81,9 @@ module Rigor
|
|
|
77
81
|
"inline_declared" => "write"
|
|
78
82
|
},
|
|
79
83
|
# ADR-17 — project-side monkey-patch pre-evaluation. Empty by default; users opt in by listing explicit
|
|
80
|
-
# files that the analyzer walks before per-file inference so patched-method declarations are
|
|
81
|
-
# across the project (e.g. `lib/core_ext/string_extensions.rb`).
|
|
82
|
-
#
|
|
83
|
-
# tier consuming the registry lands in slice 2.
|
|
84
|
+
# files (or globs) that the analyzer walks before per-file inference so patched-method declarations are
|
|
85
|
+
# visible across the project (e.g. `lib/core_ext/string_extensions.rb`). A missing literal path surfaces
|
|
86
|
+
# as `pre-eval.file-not-found`.
|
|
84
87
|
"pre_eval" => [],
|
|
85
88
|
# ADR-22 — baseline file path. nil (default) means no baseline is loaded; the `false` literal is
|
|
86
89
|
# treated as the explicit-disable form for `.rigor.yml`-side override of an upstream `.rigor.dist.yml`
|
|
@@ -209,8 +212,8 @@ module Rigor
|
|
|
209
212
|
# set up with `rbs collection install`, the resulting `rbs_collection.lock.yaml` carries the resolved
|
|
210
213
|
# (gem, version, source) triples and `.gem_rbs_collection/` holds the downloaded `.rbs` files. Rigor
|
|
211
214
|
# parses the lockfile and auto-feeds each gem's `<collection_root>/<name>/<version>/` directory into
|
|
212
|
-
# `RbsLoader`'s `signature_paths:`. Sources of type `stdlib`
|
|
213
|
-
# `DEFAULT_LIBRARIES`
|
|
215
|
+
# `RbsLoader`'s `signature_paths:`. Sources of type `stdlib` install no directory; issue #2946 — they
|
|
216
|
+
# load as RBS libraries, appended to `DEFAULT_LIBRARIES + libraries:`, as the `rbs` CLI loads them.
|
|
214
217
|
#
|
|
215
218
|
# `lockfile:` (String, optional): explicit path to `rbs_collection.lock.yaml`. Resolved relative to
|
|
216
219
|
# the project root when relative.
|
|
@@ -229,6 +232,11 @@ module Rigor
|
|
|
229
232
|
PATH_KEYS = %w[paths signature_paths test_paths pre_eval].freeze
|
|
230
233
|
private_constant :PATH_KEYS
|
|
231
234
|
|
|
235
|
+
# The single-path sibling of {PATH_KEYS}: resolved the same way, but kept a String rather than wrapped in an
|
|
236
|
+
# Array.
|
|
237
|
+
SCALAR_PATH_KEYS = %w[core_root].freeze
|
|
238
|
+
private_constant :SCALAR_PATH_KEYS
|
|
239
|
+
|
|
232
240
|
# Top-level keys this implementation DECLARES but never reads — see `docs/internal-spec/config.md`
|
|
233
241
|
# § "Reserved namespaces" and ADR-99.
|
|
234
242
|
#
|
|
@@ -271,7 +279,8 @@ module Rigor
|
|
|
271
279
|
|
|
272
280
|
attr_reader :target_ruby, :paths, :exclude_patterns, :plugins, :cache_path, :cache_max_bytes,
|
|
273
281
|
:cache_validation, :disabled_rules,
|
|
274
|
-
:libraries, :signature_paths, :
|
|
282
|
+
:libraries, :signature_paths, :core_root, :test_paths, :fold_platform_specific_paths,
|
|
283
|
+
:parameter_inference,
|
|
275
284
|
:plugins_isolation,
|
|
276
285
|
:plugins_io_network, :plugins_io_allowed_paths,
|
|
277
286
|
:plugins_io_allowed_url_hosts,
|
|
@@ -352,7 +361,8 @@ module Rigor
|
|
|
352
361
|
# [PHPStan](https://phpstan.org/config-reference#paths).
|
|
353
362
|
def self.load(path = nil)
|
|
354
363
|
resolved = path || discover
|
|
355
|
-
|
|
364
|
+
loaded = resolved && File.exist?(resolved) ? resolved : nil
|
|
365
|
+
if loaded.nil?
|
|
356
366
|
data = DEFAULTS
|
|
357
367
|
effects_key_present = false
|
|
358
368
|
target_ruby_key_present = false
|
|
@@ -361,13 +371,14 @@ module Rigor
|
|
|
361
371
|
# `DEFAULTS` itself carries an `"effects" => false` entry: once merged, "the file never wrote
|
|
362
372
|
# `effects:`" and "the file wrote `effects: false`" collapse to the same value and become
|
|
363
373
|
# indistinguishable to {#initialize}. This is the only place that distinction still exists.
|
|
364
|
-
raw = load_with_includes(
|
|
374
|
+
raw = load_with_includes(loaded)
|
|
365
375
|
effects_key_present = raw.key?("effects")
|
|
366
376
|
target_ruby_key_present = raw.key?("target_ruby")
|
|
367
377
|
data = DEFAULTS.merge(raw)
|
|
368
378
|
end
|
|
369
379
|
new(autowire_default_plugins(data), effects_key_present, target_ruby_key_present || false)
|
|
370
|
-
.with_exclude_base(
|
|
380
|
+
.with_exclude_base(loaded && File.dirname(loaded))
|
|
381
|
+
.with_config_path(loaded)
|
|
371
382
|
end
|
|
372
383
|
|
|
373
384
|
# #1576 — the directory relative `exclude:` patterns are matched against: the directory of the config file in
|
|
@@ -376,6 +387,17 @@ module Rigor
|
|
|
376
387
|
@base_dir || Dir.pwd
|
|
377
388
|
end
|
|
378
389
|
|
|
390
|
+
# Issue #2742 — the path config-anchored diagnostics are positioned at: the loaded config file's path
|
|
391
|
+
# relative to the working directory (the project-root convention {Analysis::Baseline} uses), or
|
|
392
|
+
# `.rigor.yml` when no file was loaded. A `--config=config/rigor.yml` run names `config/rigor.yml`.
|
|
393
|
+
def config_display_path
|
|
394
|
+
return DEFAULT_PATH if @config_path.nil?
|
|
395
|
+
|
|
396
|
+
Pathname.new(@config_path).relative_path_from(Pathname.new(Dir.pwd)).to_s
|
|
397
|
+
rescue ArgumentError
|
|
398
|
+
@config_path
|
|
399
|
+
end
|
|
400
|
+
|
|
379
401
|
# ADR-93 WD2 — the one bundled plugin default-wired without a `plugins:` entry. `rigor-rbs-inline` is the
|
|
380
402
|
# gem; `rbs-inline` is its manifest id (the loader raises on a duplicate id, so both forms count as
|
|
381
403
|
# "already listed").
|
|
@@ -490,6 +512,7 @@ module Rigor
|
|
|
490
512
|
|
|
491
513
|
out = data.dup
|
|
492
514
|
PATH_KEYS.each { |key| resolve_path_key!(out, key, base_dir) }
|
|
515
|
+
SCALAR_PATH_KEYS.each { |key| resolve_scalar_path_key!(out, key, base_dir) }
|
|
493
516
|
resolve_plugins_io_paths!(out, base_dir)
|
|
494
517
|
out
|
|
495
518
|
end
|
|
@@ -500,6 +523,12 @@ module Rigor
|
|
|
500
523
|
out[key] = Array(out[key]).map { |p| ConfigPath.absolute(p.to_s, base_dir) }
|
|
501
524
|
end
|
|
502
525
|
|
|
526
|
+
def self.resolve_scalar_path_key!(out, key, base_dir)
|
|
527
|
+
return unless out.key?(key) && out[key].is_a?(String)
|
|
528
|
+
|
|
529
|
+
out[key] = ConfigPath.absolute(out[key], base_dir)
|
|
530
|
+
end
|
|
531
|
+
|
|
503
532
|
def self.resolve_plugins_io_paths!(out, base_dir)
|
|
504
533
|
plugins_io = out["plugins_io"]
|
|
505
534
|
return unless plugins_io.is_a?(Hash) && plugins_io["allowed_paths"]
|
|
@@ -582,6 +611,7 @@ module Rigor
|
|
|
582
611
|
@libraries = Array(data.fetch("libraries", DEFAULTS.fetch("libraries"))).map(&:to_s).freeze
|
|
583
612
|
sig_paths = data.fetch("signature_paths", DEFAULTS.fetch("signature_paths"))
|
|
584
613
|
@signature_paths = sig_paths.nil? ? nil : Array(sig_paths).map(&:to_s).freeze
|
|
614
|
+
@core_root = coerce_core_root(data.fetch("core_root", DEFAULTS.fetch("core_root")))
|
|
585
615
|
test_paths = data.fetch("test_paths", DEFAULTS.fetch("test_paths"))
|
|
586
616
|
@test_paths = test_paths.nil? ? nil : Array(test_paths).map(&:to_s).freeze
|
|
587
617
|
@sig_gen_inline_declared = coerce_sig_gen_inline_declared(data.fetch("sig_gen", nil))
|
|
@@ -595,18 +625,13 @@ module Rigor
|
|
|
595
625
|
# ADR-67 WD6a — resolve to a strict Boolean so a truthy non-`true` value (e.g. a stray String) does not
|
|
596
626
|
# silently enable the gate; only the literal `true` activates the check-walk collector pre-pass.
|
|
597
627
|
@parameter_inference = data.fetch("parameter_inference", DEFAULTS.fetch("parameter_inference")) == true
|
|
598
|
-
# Resolved before `@effects` — ADR-103 WD15's "effects-on-by-default" reads {#bleeding_edge_active?},
|
|
599
|
-
# which reads `@bleeding_edge_active_ids`.
|
|
600
|
-
@bleeding_edge = coerce_bleeding_edge(
|
|
601
|
-
data.fetch("bleeding_edge", DEFAULTS.fetch("bleeding_edge"))
|
|
602
|
-
)
|
|
603
|
-
@bleeding_edge_severity_overrides = BleedingEdge.severity_overrides_for(@bleeding_edge)
|
|
604
|
-
@bleeding_edge_active_ids = BleedingEdge.active_ids_for(@bleeding_edge)
|
|
605
628
|
# ADR-103 WD13 — presence, not truthiness: `effects:` written with no body parses as nil and still
|
|
606
|
-
# means "on", so the key's presence in the loaded data is what {#effects_enabled?} reads.
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
629
|
+
# means "on", so the key's presence in the loaded data is what {#effects_enabled?} reads. The raw
|
|
630
|
+
# input is kept so {#with_bleeding_edge} re-derives the effects block from the same source.
|
|
631
|
+
@effects_key_present = effects_key_present
|
|
632
|
+
@effects_written = Ractor.make_shareable(data.fetch("effects", false))
|
|
633
|
+
derive_bleeding_edge(data.fetch("bleeding_edge", DEFAULTS.fetch("bleeding_edge")))
|
|
634
|
+
derive_effects
|
|
610
635
|
@cache_path = cache.fetch("path").to_s
|
|
611
636
|
raw_max = cache.fetch("max_bytes")
|
|
612
637
|
@cache_max_bytes = raw_max.nil? ? nil : Integer(raw_max)
|
|
@@ -663,6 +688,10 @@ module Rigor
|
|
|
663
688
|
def to_h # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
|
664
689
|
{
|
|
665
690
|
"target_ruby" => target_ruby,
|
|
691
|
+
# Issue #2742 — the loaded file's display path is part of the resolved configuration: two files
|
|
692
|
+
# with identical bodies still anchor their config diagnostics differently, so the run-cache and
|
|
693
|
+
# incremental fingerprints must tell them apart.
|
|
694
|
+
"config_path" => config_display_path,
|
|
666
695
|
# A stated `target_ruby` turns on the deprecation rules a defaulted one leaves off (#1692), so the run cache
|
|
667
696
|
# key and the incremental fingerprint must tell the two apart even where the value matches.
|
|
668
697
|
"target_ruby_explicit" => target_ruby_explicit?,
|
|
@@ -672,6 +701,7 @@ module Rigor
|
|
|
672
701
|
"disable" => disabled_rules,
|
|
673
702
|
"libraries" => libraries,
|
|
674
703
|
"signature_paths" => signature_paths,
|
|
704
|
+
"core_root" => core_root,
|
|
675
705
|
# `test_paths:` is deliberately absent, as `effects:` is: `to_h` feeds the run cache key and the
|
|
676
706
|
# incremental-snapshot fingerprint, and the test roots change no diagnostic, so editing them must not
|
|
677
707
|
# invalidate either. `sig_gen:` is absent for the same reason: only `rigor sig-gen` reads it.
|
|
@@ -752,15 +782,14 @@ module Rigor
|
|
|
752
782
|
# It is a frozen `dup` with the bleeding-edge ivars re-set: `dup` returns an unfrozen shallow copy
|
|
753
783
|
# (every other ivar is the receiver's deeply-frozen value, safe to share read-only), the replacements
|
|
754
784
|
# are themselves deeply frozen, and the re-`freeze` keeps the result `Ractor.shareable?` for the worker
|
|
755
|
-
# path. **Every ivar `#initialize` derives from
|
|
756
|
-
#
|
|
785
|
+
# path. **Every ivar `#initialize` derives from the bleeding-edge selector has to be re-derived here** — it
|
|
786
|
+
# calls the same {#derive_bleeding_edge} and {#derive_effects} that `#initialize` does, so the selector,
|
|
787
|
+
# the severity map, the active-id set and the `effects:` default (ADR-103 WD15's "effects-on-by-default")
|
|
788
|
+
# all follow the new value, and a derived ivar added to `#initialize` is picked up without a second edit.
|
|
757
789
|
def with_bleeding_edge(value)
|
|
758
|
-
selector = coerce_bleeding_edge(value)
|
|
759
790
|
copy = dup
|
|
760
|
-
copy.
|
|
761
|
-
copy.
|
|
762
|
-
BleedingEdge.severity_overrides_for(selector))
|
|
763
|
-
copy.instance_variable_set(:@bleeding_edge_active_ids, BleedingEdge.active_ids_for(selector))
|
|
791
|
+
copy.send(:derive_bleeding_edge, value)
|
|
792
|
+
copy.send(:derive_effects)
|
|
764
793
|
copy.freeze
|
|
765
794
|
end
|
|
766
795
|
|
|
@@ -772,6 +801,16 @@ module Rigor
|
|
|
772
801
|
copy.freeze
|
|
773
802
|
end
|
|
774
803
|
|
|
804
|
+
# Issue #2742 — a sibling Configuration recording which file {Configuration.load} read, so the
|
|
805
|
+
# diagnostics anchored at "the configuration" name it ({#config_display_path}). `path` is the resolved
|
|
806
|
+
# config path; nil — no file loaded, or a bare {Configuration.new} — keeps the `.rigor.yml` default.
|
|
807
|
+
# Same frozen `dup` shape as {#with_exclude_base}.
|
|
808
|
+
def with_config_path(path)
|
|
809
|
+
copy = dup
|
|
810
|
+
copy.instance_variable_set(:@config_path, path && File.absolute_path(path).freeze)
|
|
811
|
+
copy.freeze
|
|
812
|
+
end
|
|
813
|
+
|
|
775
814
|
# #1777 — a sibling Configuration with only `baseline:` unset, every other field (including the
|
|
776
815
|
# explicitness of `target_ruby`) shared with the receiver. `rigor baseline generate` analyses with
|
|
777
816
|
# this so the generating run sees exactly the configuration `rigor check` will, minus the filter it
|
|
@@ -794,19 +833,38 @@ module Rigor
|
|
|
794
833
|
#
|
|
795
834
|
# Same `dup` + re-`freeze` shape as {#with_bleeding_edge}: every other ivar is the receiver's deeply
|
|
796
835
|
# frozen value, safe to share read-only, and the result stays `Ractor.shareable?` for the worker path.
|
|
836
|
+
# The implicit block is recorded as a written empty one, so a later {#with_bleeding_edge} re-derives
|
|
837
|
+
# it instead of falling back to the absent-key default and dropping it.
|
|
797
838
|
def with_effects_enabled
|
|
798
839
|
return self if effects_enabled?
|
|
799
840
|
|
|
800
841
|
copy = dup
|
|
801
|
-
copy.instance_variable_set(:@
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
copy.instance_variable_set(:@effects_check, true)
|
|
842
|
+
copy.instance_variable_set(:@effects_key_present, true)
|
|
843
|
+
copy.instance_variable_set(:@effects_written, {}.freeze)
|
|
844
|
+
copy.send(:derive_effects)
|
|
805
845
|
copy.freeze
|
|
806
846
|
end
|
|
807
847
|
|
|
808
848
|
private
|
|
809
849
|
|
|
850
|
+
# ADR-50 § WD2 / ADR-103 WD15 — derives the bleeding-edge selector and the two ivars the
|
|
851
|
+
# {#bleeding_edge_active?} predicate and the severity resolution read. `#initialize` and
|
|
852
|
+
# {#with_bleeding_edge} both call this, so there is one derivation to keep in step.
|
|
853
|
+
def derive_bleeding_edge(value)
|
|
854
|
+
@bleeding_edge = coerce_bleeding_edge(value)
|
|
855
|
+
@bleeding_edge_severity_overrides = BleedingEdge.severity_overrides_for(@bleeding_edge)
|
|
856
|
+
@bleeding_edge_active_ids = BleedingEdge.active_ids_for(@bleeding_edge)
|
|
857
|
+
end
|
|
858
|
+
|
|
859
|
+
# ADR-103 WD13 / WD15 — derives the `effects:` block and its snapshot and policy ivars from the raw
|
|
860
|
+
# `effects:` input the instance kept (`@effects_written`, `@effects_key_present`). The effects-on-by-default
|
|
861
|
+
# default reads the active-id set, so {#derive_bleeding_edge} must run first.
|
|
862
|
+
def derive_effects
|
|
863
|
+
@effects = coerce_effects(@effects_written, effects_key_present: @effects_key_present)
|
|
864
|
+
coerce_effects_snapshot(@effects)
|
|
865
|
+
coerce_effects_policy(@effects)
|
|
866
|
+
end
|
|
867
|
+
|
|
810
868
|
# ADR-103 WD13 — the `effects:` block, or nil when the key is absent or explicitly `false`. A
|
|
811
869
|
# present-but-empty key (`effects:` alone, which YAML parses as nil) and `effects: {}` are the same
|
|
812
870
|
# thing: collection on, every sub-key defaulted. A non-Hash body is normalised to `{}` rather than
|
|
@@ -818,8 +876,7 @@ module Rigor
|
|
|
818
876
|
# as `effects: {}` when the "effects-on-by-default" bleeding-edge feature is adopted. An explicit
|
|
819
877
|
# `effects: false` — `effects_key_present == true` — always stays off regardless of the feature, and a
|
|
820
878
|
# written block always wins outright: the feature can only fill an *absence*.
|
|
821
|
-
def coerce_effects(
|
|
822
|
-
value = data.fetch("effects", false)
|
|
879
|
+
def coerce_effects(value, effects_key_present:)
|
|
823
880
|
return {}.freeze if value == false && !effects_key_present && effects_forced_on_by_bleeding_edge?
|
|
824
881
|
return nil if value == false
|
|
825
882
|
|
|
@@ -1054,7 +1111,7 @@ module Rigor
|
|
|
1054
1111
|
end
|
|
1055
1112
|
end
|
|
1056
1113
|
|
|
1057
|
-
# `target_ruby` is passed
|
|
1114
|
+
# `target_ruby` is passed as Prism's `version:` option at the analyser's parse sites
|
|
1058
1115
|
# (`Analysis::Runner`, `CLI::TypeOfCommand`, `CLI::TypeScanCommand`) so projects that target an older
|
|
1059
1116
|
# Ruby get parse errors for syntax their target doesn't support. Format validation here is loose —
|
|
1060
1117
|
# accepts any `<major>.<minor>` or `<major>.<minor>.<patch>` form, plus the literal `"latest"`. Prism
|
|
@@ -1097,6 +1154,18 @@ module Rigor
|
|
|
1097
1154
|
# ADR-22 WD2 (b) — `baseline: <path>` activates the file; `baseline: false` is the explicit-disable form
|
|
1098
1155
|
# (useful in `.rigor.yml` to override an upstream `.rigor.dist.yml` that names a baseline). `nil`
|
|
1099
1156
|
# (default / absent key) is also "no baseline".
|
|
1157
|
+
# Issue #2517 — the directory's existence and contents are checked where the environment is built
|
|
1158
|
+
# ({Environment::CoreRoot.validate!}), not here: `rigor init`, `rigor doctor` and the language server's reload
|
|
1159
|
+
# load a configuration without building an environment, and must still be able to read one naming a core that
|
|
1160
|
+
# is not generated yet.
|
|
1161
|
+
def coerce_core_root(value)
|
|
1162
|
+
return nil if value.nil?
|
|
1163
|
+
raise ConfigurationError, "core_root must be a directory path, got #{value.inspect}" unless value.is_a?(String)
|
|
1164
|
+
raise ConfigurationError, "core_root must not be empty" if value.strip.empty?
|
|
1165
|
+
|
|
1166
|
+
value.dup.freeze
|
|
1167
|
+
end
|
|
1168
|
+
|
|
1100
1169
|
def coerce_baseline_path(value)
|
|
1101
1170
|
return nil if value.nil? || value == false
|
|
1102
1171
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "../type"
|
|
4
4
|
require_relative "../inference/origin_lookup"
|
|
5
|
+
require_relative "../typing_state"
|
|
5
6
|
require_relative "attribution"
|
|
6
7
|
require_relative "envelope_index"
|
|
7
8
|
require_relative "file_collection"
|
|
@@ -24,8 +25,26 @@ module Rigor
|
|
|
24
25
|
# `dynamic_origins` cause the typer already recorded. With no `effects:` block nothing calls
|
|
25
26
|
# {collect_for}, `@active_count` stays zero, and `rigor check` is byte-identical.
|
|
26
27
|
module Collector
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
# ADR-129 WD1 — the installed accumulator is a Frame whose contents are a Resource; only {record_root} touches it
|
|
29
|
+
# now, and the call-site decisions are the bound Table {CALLS}. The activation count below is a process switch,
|
|
30
|
+
# outside the typing state (WD5).
|
|
31
|
+
ACCUMULATOR = TypingState.frame(
|
|
32
|
+
:effects_collector,
|
|
33
|
+
contents: :resource, reason: "the accumulator a file's collection installs; each call site records into it",
|
|
34
|
+
# G1′ witness: `Collector.record_call` reads it during typing, so it is not a never-reads switch.
|
|
35
|
+
witness: "spec/fixtures/typing_state_witnesses/effects_collector.rb"
|
|
36
|
+
)
|
|
37
|
+
private_constant :ACCUMULATOR
|
|
38
|
+
|
|
39
|
+
# ADR-129 WD1 — the per-file call-site decisions are a bound Table: each installed accumulator's `calls` is one of
|
|
40
|
+
# its stores, journaled while a capture is open so a later `type_of` memo can validate the reads and replay the
|
|
41
|
+
# writes. The root ({record_root}) stays a touch of the accumulator: it is set once per file, before typing.
|
|
42
|
+
CALLS = TypingState.table(
|
|
43
|
+
:effects_calls,
|
|
44
|
+
bound: true,
|
|
45
|
+
reason: "node => CallRecord per file accumulator; first write wins, and a re-query reads the pinned decision"
|
|
46
|
+
)
|
|
47
|
+
private_constant :CALLS
|
|
29
48
|
|
|
30
49
|
# What one call site decided, as the typer saw it.
|
|
31
50
|
#
|
|
@@ -97,17 +116,18 @@ module Rigor
|
|
|
97
116
|
# site imports must not depend on which process typed the file.
|
|
98
117
|
def collect_for(path, attribution: Attribution.empty, envelopes: EnvelopeIndex.empty,
|
|
99
118
|
plugin_facts: PluginFacts.empty, unit_key: nil, unit_owner: nil)
|
|
100
|
-
previous = Thread.current[KEY]
|
|
101
119
|
accumulator = Accumulator.new(path.to_s, attribution: attribution, envelopes: envelopes,
|
|
102
120
|
plugin_facts: plugin_facts, unit_key: unit_key,
|
|
103
121
|
unit_owner: unit_owner)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
122
|
+
TypingState.current.with(ACCUMULATOR, accumulator) do
|
|
123
|
+
@mutex.synchronize { @active_count += 1 }
|
|
124
|
+
begin
|
|
125
|
+
yield
|
|
126
|
+
build(accumulator)
|
|
127
|
+
ensure
|
|
128
|
+
@mutex.synchronize { @active_count -= 1 }
|
|
129
|
+
end
|
|
130
|
+
end
|
|
111
131
|
end
|
|
112
132
|
|
|
113
133
|
def active?
|
|
@@ -117,8 +137,12 @@ module Rigor
|
|
|
117
137
|
# The parsed root of the file being analyzed. Recorded by the analysis body once the parse is known
|
|
118
138
|
# to have succeeded, so the scan runs over exactly what the typer saw.
|
|
119
139
|
def record_root(root)
|
|
120
|
-
|
|
121
|
-
accumulator
|
|
140
|
+
state = TypingState.current
|
|
141
|
+
accumulator = state[ACCUMULATOR]
|
|
142
|
+
return unless accumulator
|
|
143
|
+
|
|
144
|
+
state.touch(ACCUMULATOR)
|
|
145
|
+
accumulator.root = root if accumulator.root.nil?
|
|
122
146
|
end
|
|
123
147
|
|
|
124
148
|
# One call site's dispatch decision. `receiver` is the type the typer computed for the receiver and
|
|
@@ -127,30 +151,38 @@ module Rigor
|
|
|
127
151
|
# The `scope.source_path` guard matters: inter-procedural inference re-types a *callee's* body while
|
|
128
152
|
# the caller's file is being analyzed, and those nodes belong to the callee's own file collection.
|
|
129
153
|
def record_call(node, receiver, scope)
|
|
130
|
-
|
|
154
|
+
state = TypingState.current
|
|
155
|
+
accumulator = state[ACCUMULATOR]
|
|
131
156
|
return if accumulator.nil? || accumulator.path != scope.source_path
|
|
157
|
+
|
|
132
158
|
# First write wins, so a re-query has nothing to add — and asking here rather than inside
|
|
133
159
|
# {Accumulator#record} is what stops it paying for the `Data` and the origin lookup below.
|
|
160
|
+
calls = accumulator.calls
|
|
161
|
+
state.log_bound_read(CALLS, calls, node, calls.fetch(node, TypingState::ABSENT)) if state.capturing?
|
|
134
162
|
return if accumulator.recorded?(node)
|
|
135
163
|
|
|
136
164
|
dynamic = dynamic_receiver?(receiver)
|
|
137
165
|
class_name, kind = descriptor_for(receiver)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
cause: dynamic ? Inference::OriginLookup.origin_for(scope, node.receiver)&.to_s : nil,
|
|
143
|
-
resolved: true
|
|
144
|
-
)
|
|
166
|
+
call_record = CallRecord.new(
|
|
167
|
+
receiver_class: class_name, kind: kind, dynamic: dynamic,
|
|
168
|
+
cause: dynamic ? Inference::OriginLookup.origin_for(scope, node.receiver)&.to_s : nil,
|
|
169
|
+
resolved: true
|
|
145
170
|
)
|
|
171
|
+
accumulator.record(node, call_record)
|
|
172
|
+
state.log_bound_write(CALLS, calls, node, call_record, TypingState::ABSENT)
|
|
146
173
|
end
|
|
147
174
|
|
|
148
175
|
# The typer's fallback verdict: no tier resolved this call.
|
|
149
176
|
def record_unresolved(node, source_path)
|
|
150
|
-
|
|
177
|
+
state = TypingState.current
|
|
178
|
+
accumulator = state[ACCUMULATOR]
|
|
151
179
|
return if accumulator.nil? || accumulator.path != source_path
|
|
152
180
|
|
|
181
|
+
calls = accumulator.calls
|
|
182
|
+
existing = calls.fetch(node, TypingState::ABSENT)
|
|
183
|
+
state.log_bound_read(CALLS, calls, node, existing)
|
|
153
184
|
accumulator.mark_unresolved(node)
|
|
185
|
+
state.log_bound_write(CALLS, calls, node, calls[node], existing) unless TypingState::ABSENT.equal?(existing)
|
|
154
186
|
end
|
|
155
187
|
|
|
156
188
|
# The class a receiver type projects to, as `[class_name, kind]`. A deliberately small mirror of the
|
|
@@ -58,11 +58,9 @@ module Rigor
|
|
|
58
58
|
node.is_a?(Prism::SingletonClassNode) || (node.is_a?(Prism::CallNode) && node.block.is_a?(Prism::BlockNode))
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
-
# A class body.
|
|
62
61
|
CLASS_BODY = of(:class, false)
|
|
63
62
|
# A `class << self` body: `self` and the definee are both the singleton class.
|
|
64
63
|
SINGLETON_CLASS_BODY = of(:singleton_class, true)
|
|
65
|
-
# The body of an instance method of the class.
|
|
66
64
|
INSTANCE_METHOD_BODY = of(:instance, false)
|
|
67
65
|
# The top level, where `self` is `main`, an instance of `Object`, and a `def` defines `Object#m`.
|
|
68
66
|
TOP_LEVEL = INSTANCE_METHOD_BODY
|
|
@@ -8,7 +8,7 @@ module Rigor
|
|
|
8
8
|
# An author-declared upper bound on one method's effect labels (ADR-103 WD1; normative in
|
|
9
9
|
# `docs/type-specification/effect-labels.md` § Effect envelopes).
|
|
10
10
|
#
|
|
11
|
-
#
|
|
11
|
+
# Its facts:
|
|
12
12
|
#
|
|
13
13
|
# - {#owner_key} — the method key the envelope binds (`Class#m` / `Class.m`). A class-level
|
|
14
14
|
# envelope is read once and {#rebind}ed onto each method of that class discovery knows.
|
|
@@ -69,7 +69,7 @@ module Rigor
|
|
|
69
69
|
VALIDATION_TRIGGERS = %w[valid? invalid? validate!].freeze
|
|
70
70
|
SINGLETON_SAVE_TRIGGERS = %w[create create!].freeze
|
|
71
71
|
|
|
72
|
-
# `initialize` is not a mailer action
|
|
72
|
+
# `initialize` is not a mailer action.
|
|
73
73
|
NON_ACTION_METHODS = %w[initialize].to_set.freeze
|
|
74
74
|
|
|
75
75
|
module_function
|
|
@@ -32,13 +32,10 @@ module Rigor
|
|
|
32
32
|
# `foo=`, and deliberately not `==` / `<=` / `!=` / `===`.
|
|
33
33
|
ATTRIBUTE_WRITER = /\A[a-z_][A-Za-z0-9_]*=\z/
|
|
34
34
|
|
|
35
|
-
# Every Hash method that mutates its receiver
|
|
36
|
-
# questions of a `HashShape`
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
# `h.clear` does (unless the hash already compares by identity, when it returns before the check), and a
|
|
40
|
-
# method whose one effect was `@h.compare_by_identity` did not read as mutating while this cited the first
|
|
41
|
-
# table alone.
|
|
35
|
+
# Every Hash method that mutates its receiver: the union of two widening tables that answer different
|
|
36
|
+
# questions of a `HashShape` (the pair set; what a read of the pairs answers). Either is a write to the
|
|
37
|
+
# receiver, so either counts. `compare_by_identity` lives only in the second, and it raises `FrozenError`
|
|
38
|
+
# on a frozen hash as `h.clear` does, so a method whose one effect is that call must still read as mutating.
|
|
42
39
|
#
|
|
43
40
|
# `rehash` is in neither table because it changes no read a shape can state: a shape's keys are literals, which
|
|
44
41
|
# a rebuild never merges. It still rebuilds the receiver's table in place, and it is the one name spelt here.
|
|
@@ -363,7 +363,7 @@ module Rigor
|
|
|
363
363
|
# dispatch target is spelled under a different key, and this is the rewrite: the same ancestor walk
|
|
364
364
|
# {#resolve_owner} performs, on the instance side, for `initialize`.
|
|
365
365
|
#
|
|
366
|
-
#
|
|
366
|
+
# Four boundaries, and each of them can only narrow:
|
|
367
367
|
#
|
|
368
368
|
# - a project `def self.new` **wins**. It is the definition `Const.new` actually reaches, and a
|
|
369
369
|
# constructor that overrides `new` is the case where `#initialize` is not the answer. Resolved
|
|
@@ -35,8 +35,8 @@ module Rigor
|
|
|
35
35
|
# without proposing an unrelated label for a genuinely new spelling.
|
|
36
36
|
SUGGESTION_DISTANCE_CAP = 2
|
|
37
37
|
|
|
38
|
-
# The shared registry as shipped. Memoised: the YAML parse is a once-per-process cost,
|
|
39
|
-
#
|
|
38
|
+
# The shared registry as shipped. Memoised: the YAML parse is a once-per-process cost, so it stays
|
|
39
|
+
# lazy rather than paying at `require "rigor"`.
|
|
40
40
|
def self.default
|
|
41
41
|
@default ||= load_file(DATA_PATH)
|
|
42
42
|
end
|
|
@@ -55,7 +55,7 @@ module Rigor
|
|
|
55
55
|
#
|
|
56
56
|
# A count rather than the causes themselves (#434). On redmine the cause renderings were 144,771 of
|
|
57
57
|
# the file's 293,276 bytes, on lines up to 821 characters — simultaneously the half a reviewer
|
|
58
|
-
# cannot read and the half that churns when an unrelated call moves.
|
|
58
|
+
# cannot read and the half that churns when an unrelated call moves. {SnapshotDiff} already makes that
|
|
59
59
|
# argument about origins ("a record keyed by them would churn on every refactor"); the causes are
|
|
60
60
|
# the same class of thing and were the one place it was not applied. What a reviewer needs from the
|
|
61
61
|
# record is the stable fact that the row is not exhaustive and by how much, which is what a count
|
|
@@ -8,8 +8,6 @@ module Rigor
|
|
|
8
8
|
# A tainted summary reads "these effects, and possibly more": the cause says which knowledge the
|
|
9
9
|
# analyzer could not have. Taint never produces a finding — diagnostics read the proven lane
|
|
10
10
|
# only — so this enum is a reporting vocabulary, not a diagnostic one.
|
|
11
|
-
#
|
|
12
|
-
# Nothing produces causes yet; the collector of #379 is the first writer.
|
|
13
11
|
module TaintCause
|
|
14
12
|
# Closed and ordered by how it arises: dispatch the analyzer could not resolve first, then
|
|
15
13
|
# attribution it had to take on trust, then the collector's own limits.
|
|
@@ -586,9 +586,9 @@ module Rigor
|
|
|
586
586
|
# #1071 widens the answer per `respond_to` arm: a unit that spelled a dispatcher renders one
|
|
587
587
|
# conventional `<action>.<fmt>` template per arm rather than one `<action>.html`, so the row is
|
|
588
588
|
# applied once per arm the walk read, each answered outright (a `responds:` row at the arm's own
|
|
589
|
-
# top level) or degenerate (`any` / `all`) standing its arm's edge down. The `<action>.html`
|
|
590
|
-
#
|
|
591
|
-
#
|
|
589
|
+
# top level) or degenerate (`any` / `all`) standing its arm's edge down. The `<action>.html` answer
|
|
590
|
+
# is kept where no arm was read, and beside the arms when the dispatcher is nested under a branch,
|
|
591
|
+
# because the plain fall-through still exists on the path that skips it.
|
|
592
592
|
def apply_unit_callees
|
|
593
593
|
return if @responded || @singleton || @non_public
|
|
594
594
|
return if @owner_class.nil? || @method_name.nil?
|
|
@@ -811,11 +811,9 @@ module Rigor
|
|
|
811
811
|
#
|
|
812
812
|
# **Unless the syntax named the class** (#463). The `Dynamic` exclusion exists because a projected
|
|
813
813
|
# receiver class is a guess; a written constant path is not one, and {#catalog_target} already
|
|
814
|
-
# trusts it for the row lookup. Refusing the posture there made the catalogue's reach
|
|
815
|
-
# whether the bundled rbs
|
|
816
|
-
#
|
|
817
|
-
# `net-imap`. That is why the `Net::FTP` row has never once fired, and why Redmine's IMAP and POP3
|
|
818
|
-
# pollers proved no network at all.
|
|
814
|
+
# trusts it for the row lookup. Refusing the posture there made the catalogue's reach depend on
|
|
815
|
+
# whether the bundled rbs ships a signature for the constant: `Net::HTTP.get` proved `io.net.http`,
|
|
816
|
+
# while `Net::IMAP.new` proved nothing, because rbs ships `net-http` and not `net-imap`.
|
|
819
817
|
#
|
|
820
818
|
# The site is then **claimed**, and so discharges, like every other catalogue answer. That is not a
|
|
821
819
|
# softening of the `dynamic-receiver` rule but its premise failing: the taint says the class the
|
|
@@ -974,12 +972,10 @@ module Rigor
|
|
|
974
972
|
positional_arity(node).positive?
|
|
975
973
|
end
|
|
976
974
|
|
|
977
|
-
# The class the catalogue would look this call up under, as `[owner, singleton,
|
|
975
|
+
# The class the catalogue would look this call up under, as `[owner, singleton, implicit, named]`.
|
|
978
976
|
# Spelled from the syntax where the syntax settles it and from the typer's receiver otherwise;
|
|
979
|
-
# `[nil, …]` when neither does.
|
|
980
|
-
#
|
|
981
|
-
# author wrote**, which is what licenses the posture on a receiver the typer could not resolve
|
|
982
|
-
# ({#posture_allowed?}).
|
|
977
|
+
# `[nil, …]` when neither does. `named` says the owner came from a **constant path the author wrote**,
|
|
978
|
+
# which is what licenses the posture on a receiver the typer could not resolve ({#posture_allowed?}).
|
|
983
979
|
def catalog_target(node, record)
|
|
984
980
|
receiver = node.receiver
|
|
985
981
|
return ["Kernel", false, true, false] if receiver.nil? || receiver.is_a?(Prism::SelfNode)
|