rigortype 0.4.6 → 0.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +4 -2
- data/data/core_overlay/bigdecimal.rbs +33 -0
- data/data/core_overlay/resolv.rbs +30 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +45 -13
- data/data/gem_overlay/addressable/addressable.rbs +40 -0
- data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
- data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
- data/docs/handbook/01-getting-started.md +6 -3
- data/docs/handbook/11-sig-gen.md +85 -3
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/install.md +20 -0
- data/docs/manual/01-installation.md +67 -26
- data/docs/manual/02-cli-reference.md +455 -113
- data/docs/manual/03-configuration.md +76 -5
- data/docs/manual/04-diagnostics.md +5 -4
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/08-skills.md +11 -0
- data/docs/manual/09-editor-integration.md +21 -2
- data/docs/manual/10-mcp-server.md +38 -2
- data/docs/manual/12-caching.md +4 -3
- data/docs/manual/14-rails-quickstart.md +1 -1
- data/docs/manual/19-effect-labels.md +3 -4
- data/docs/manual/plugins/rigor-actionpack.md +32 -15
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +23 -1
- data/docs/manual/plugins/rigor-sorbet.md +17 -3
- data/lib/rigor/analysis/baseline.rb +24 -27
- data/lib/rigor/analysis/check_rules/always_raises_shield.rb +147 -0
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
- data/lib/rigor/analysis/check_rules/call_facts.rb +58 -0
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +12 -4
- data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +85 -13
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +16 -4
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +6 -1
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
- data/lib/rigor/analysis/check_rules/plugin_api_surface.rb +195 -0
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +41 -12
- data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +27 -1
- data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +3 -3
- data/lib/rigor/analysis/check_rules/replaced_singleton_defs.rb +96 -0
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- data/lib/rigor/analysis/check_rules/rule_ids.rb +9 -2
- data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules.rb +720 -141
- data/lib/rigor/analysis/crash_signature.rb +45 -3
- data/lib/rigor/analysis/dependency_recorder.rb +237 -80
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/edit_replay.rb +93 -6
- data/lib/rigor/analysis/fact_store.rb +3 -3
- data/lib/rigor/analysis/incremental.rb +11 -1
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +4 -3
- data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
- data/lib/rigor/analysis/incremental_session.rb +306 -53
- data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -3
- data/lib/rigor/analysis/rule_catalog.rb +39 -3
- data/lib/rigor/analysis/run_cache_key.rb +27 -3
- data/lib/rigor/analysis/run_disclosure_credit.rb +26 -6
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -2
- data/lib/rigor/analysis/runner/declaration_position.rb +8 -8
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +264 -112
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +4 -9
- data/lib/rigor/analysis/runner/pool_coordinator.rb +173 -328
- data/lib/rigor/analysis/runner/project_pre_passes.rb +4 -37
- data/lib/rigor/analysis/runner/run_level_facts.rb +304 -0
- data/lib/rigor/analysis/runner/run_snapshots.rb +44 -54
- data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
- data/lib/rigor/analysis/runner.rb +298 -109
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +62 -23
- data/lib/rigor/analysis/template_unit_positions.rb +2 -3
- data/lib/rigor/analysis/template_units.rb +9 -1
- data/lib/rigor/analysis/worker_session.rb +65 -71
- data/lib/rigor/bleeding_edge.rb +22 -10
- data/lib/rigor/builtins/imported_refinements.rb +6 -8
- data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
- data/lib/rigor/builtins/regex_refinement.rb +4 -6
- data/lib/rigor/cache/descriptor.rb +6 -5
- data/lib/rigor/cache/file_digest.rb +39 -31
- data/lib/rigor/cache/incremental_snapshot.rb +95 -17
- data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
- data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
- data/lib/rigor/cache/rbs_constant_table.rb +1 -1
- data/lib/rigor/cache/rbs_descriptor.rb +90 -4
- data/lib/rigor/cache/rbs_environment.rb +8 -1
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
- data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
- data/lib/rigor/cache/store.rb +11 -10
- data/lib/rigor/ci_detector.rb +1 -1
- data/lib/rigor/cli/annotate_command.rb +24 -19
- data/lib/rigor/cli/baseline_command.rb +160 -57
- data/lib/rigor/cli/catalogue.rb +256 -0
- data/lib/rigor/cli/check_command.rb +112 -104
- data/lib/rigor/cli/check_runner_factory.rb +2 -2
- data/lib/rigor/cli/command.rb +15 -1
- data/lib/rigor/cli/coverage_command.rb +21 -8
- data/lib/rigor/cli/coverage_mutation.rb +5 -7
- data/lib/rigor/cli/coverage_renderer.rb +2 -1
- data/lib/rigor/cli/coverage_scan.rb +3 -5
- data/lib/rigor/cli/diff_command.rb +25 -20
- data/lib/rigor/cli/docs_command.rb +1 -2
- data/lib/rigor/cli/doctor_command.rb +12 -12
- data/lib/rigor/cli/effects_command.rb +50 -40
- data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
- data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
- data/lib/rigor/cli/effects_renderer.rb +2 -1
- data/lib/rigor/cli/effects_snapshot_command.rb +26 -18
- data/lib/rigor/cli/explain_command.rb +18 -8
- data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
- data/lib/rigor/cli/json_document.rb +41 -0
- data/lib/rigor/cli/lens_command.rb +29 -10
- data/lib/rigor/cli/lsp_command.rb +250 -23
- data/lib/rigor/cli/mcp_command.rb +12 -7
- data/lib/rigor/cli/mutation_fork_scan.rb +1 -1
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -2
- data/lib/rigor/cli/options.rb +40 -1
- data/lib/rigor/cli/plugin_command.rb +93 -46
- data/lib/rigor/cli/plugins_command.rb +17 -18
- data/lib/rigor/cli/plugins_renderer.rb +10 -6
- data/lib/rigor/cli/protection_fork_scan.rb +2 -2
- data/lib/rigor/cli/protection_renderer.rb +2 -1
- data/lib/rigor/cli/show_bleedingedge_command.rb +21 -15
- data/lib/rigor/cli/sig_gen_command.rb +92 -27
- data/lib/rigor/cli/skill_command.rb +32 -3
- data/lib/rigor/cli/skill_deep_probe.rb +1 -1
- data/lib/rigor/cli/skill_describe.rb +2 -1
- data/lib/rigor/cli/trace_command.rb +14 -9
- data/lib/rigor/cli/triage_command.rb +10 -11
- data/lib/rigor/cli/triage_renderer.rb +2 -1
- data/lib/rigor/cli/type_of_command.rb +16 -11
- data/lib/rigor/cli/type_of_project_scope.rb +1 -4
- data/lib/rigor/cli/type_of_renderer.rb +4 -1
- data/lib/rigor/cli/type_scan_command.rb +14 -9
- data/lib/rigor/cli/type_scan_renderer.rb +2 -1
- data/lib/rigor/cli/unused_command.rb +27 -17
- data/lib/rigor/cli/upgrade_command.rb +5 -0
- data/lib/rigor/cli.rb +214 -56
- data/lib/rigor/config_audit.rb +33 -19
- data/lib/rigor/configuration/severity_profile.rb +14 -5
- data/lib/rigor/configuration.rb +104 -35
- data/lib/rigor/effects/collector.rb +53 -21
- data/lib/rigor/effects/definition_context.rb +0 -2
- data/lib/rigor/effects/envelope.rb +1 -1
- data/lib/rigor/effects/framework_units.rb +1 -1
- data/lib/rigor/effects/mutation_classifier.rb +4 -7
- data/lib/rigor/effects/propagator.rb +1 -1
- data/lib/rigor/effects/registry.rb +2 -2
- data/lib/rigor/effects/snapshot.rb +1 -1
- data/lib/rigor/effects/taint_cause.rb +0 -2
- data/lib/rigor/effects/unit_scan.rb +9 -13
- data/lib/rigor/environment/bundle_sig_discovery.rb +64 -33
- data/lib/rigor/environment/class_registry.rb +3 -3
- data/lib/rigor/environment/constant_type_cache_holder.rb +5 -0
- data/lib/rigor/environment/core_root.rb +77 -0
- data/lib/rigor/environment/default_libraries.rb +18 -1
- data/lib/rigor/environment/deferred_signature_plan.rb +381 -0
- data/lib/rigor/environment/gated_signature_guard.rb +1 -1
- data/lib/rigor/environment/gated_signature_plan.rb +5 -2
- data/lib/rigor/environment/hkt_registry_holder.rb +6 -0
- data/lib/rigor/environment/lockfile_resolver.rb +28 -12
- data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
- data/lib/rigor/environment/member_consistency.rb +56 -7
- data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
- data/lib/rigor/environment/project_path_gems.rb +124 -0
- data/lib/rigor/environment/rbs_collection_discovery.rb +123 -8
- data/lib/rigor/environment/rbs_coverage_report.rb +12 -11
- data/lib/rigor/environment/rbs_hierarchy.rb +12 -15
- data/lib/rigor/environment/rbs_loader.rb +772 -110
- data/lib/rigor/environment/reflection.rb +7 -7
- data/lib/rigor/environment/required_features.rb +0 -3
- data/lib/rigor/environment/ruby_library_roots.rb +1 -1
- data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
- data/lib/rigor/environment.rb +169 -42
- data/lib/rigor/flow_contribution/merger.rb +4 -4
- data/lib/rigor/inference/acceptance.rb +136 -40
- data/lib/rigor/inference/alias_mutation_floor/foreign_locals.rb +117 -0
- data/lib/rigor/inference/alias_mutation_floor/memos.rb +85 -0
- data/lib/rigor/inference/alias_mutation_floor/reach.rb +119 -0
- data/lib/rigor/inference/alias_mutation_floor/site_collector.rb +252 -0
- data/lib/rigor/inference/alias_mutation_floor.rb +339 -0
- data/lib/rigor/inference/anonymous_meta_class.rb +18 -0
- data/lib/rigor/inference/block_auto_splat.rb +1 -1
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/block_run_count.rb +185 -0
- data/lib/rigor/inference/budget_trace.rb +8 -2
- data/lib/rigor/inference/builtins/array_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
- data/lib/rigor/inference/callee_mutation_chain.rb +88 -77
- data/lib/rigor/inference/callee_write_census.rb +815 -0
- data/lib/rigor/inference/captured_locals.rb +2 -0
- data/lib/rigor/inference/census_foreign_block.rb +354 -0
- data/lib/rigor/inference/clause_widening.rb +1 -1
- data/lib/rigor/inference/closure_escape_analyzer.rb +11 -4
- data/lib/rigor/inference/construction_escape.rb +258 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
- data/lib/rigor/inference/declaration_walk.rb +4 -0
- data/lib/rigor/inference/deep_erasure.rb +39 -7
- data/lib/rigor/inference/def_handle.rb +1 -1
- data/lib/rigor/inference/def_node_resolver.rb +33 -15
- data/lib/rigor/inference/def_return_typer.rb +47 -5
- data/lib/rigor/inference/definer_resolution.rb +19 -9
- data/lib/rigor/inference/definers_index.rb +162 -0
- data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
- data/lib/rigor/inference/element_read_widening.rb +49 -3
- data/lib/rigor/inference/error_info.rb +16 -0
- data/lib/rigor/inference/expression_typer.rb +1559 -320
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/flow_tracer.rb +31 -14
- data/lib/rigor/inference/fork_map.rb +3 -0
- data/lib/rigor/inference/fresh_frame_blocks.rb +52 -1
- data/lib/rigor/inference/global_write_census.rb +81 -5
- data/lib/rigor/inference/global_writer_reach.rb +260 -0
- data/lib/rigor/inference/hkt_body.rb +1 -2
- data/lib/rigor/inference/hkt_body_parser.rb +3 -5
- data/lib/rigor/inference/hkt_reducer.rb +2 -2
- data/lib/rigor/inference/hkt_registry.rb +3 -5
- data/lib/rigor/inference/in_effect_refinements.rb +7 -0
- data/lib/rigor/inference/indexed_narrowing.rb +61 -15
- data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
- data/lib/rigor/inference/ivar_writer_census.rb +154 -8
- data/lib/rigor/inference/key_presence_guard.rb +51 -24
- data/lib/rigor/inference/last_line/self_evidence.rb +30 -0
- data/lib/rigor/inference/last_line.rb +5 -2
- data/lib/rigor/inference/loop_value_record.rb +66 -14
- data/lib/rigor/inference/match_rebinding/frame.rb +57 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +101 -0
- data/lib/rigor/inference/match_rebinding.rb +28 -5
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +44 -30
- data/lib/rigor/inference/method_dispatcher/class_keyed_block_params.rb +65 -0
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +3 -3
- data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
- data/lib/rigor/inference/method_dispatcher/core_enumerable_receiver.rb +44 -0
- data/lib/rigor/inference/method_dispatcher/declared_constructor.rb +151 -0
- data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/interface_shape.rb +181 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +51 -27
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +606 -78
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +3 -3
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +9 -7
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +467 -84
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
- data/lib/rigor/inference/method_dispatcher/singleton_type_params.rb +104 -0
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +123 -25
- data/lib/rigor/inference/method_parameter_binder.rb +136 -56
- data/lib/rigor/inference/module_function_state/spellings.rb +2 -2
- data/lib/rigor/inference/module_function_state.rb +24 -3
- data/lib/rigor/inference/multi_target_binder.rb +2 -2
- data/lib/rigor/inference/mutation_rejoin.rb +240 -4
- data/lib/rigor/inference/mutation_widening.rb +199 -29
- data/lib/rigor/inference/narrowing.rb +238 -50
- data/lib/rigor/inference/operand_effects.rb +43 -2
- data/lib/rigor/inference/optimistic_origin.rb +134 -9
- data/lib/rigor/inference/origin_lookup.rb +1 -1
- data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +1 -1
- data/lib/rigor/inference/precision_scanner.rb +1 -1
- data/lib/rigor/inference/project_method_ownership.rb +30 -9
- data/lib/rigor/inference/project_patched_methods.rb +65 -13
- data/lib/rigor/inference/project_patched_scanner.rb +120 -25
- data/lib/rigor/inference/project_patched_visibility.rb +39 -3
- data/lib/rigor/inference/project_yielder.rb +11 -17
- data/lib/rigor/inference/rbs_type_translator.rb +68 -15
- data/lib/rigor/inference/receiver_alias.rb +20 -0
- data/lib/rigor/inference/receiver_writer_call.rb +67 -0
- data/lib/rigor/inference/recursion_rebinding.rb +157 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +57 -17
- data/lib/rigor/inference/refined_dispatch/own_method.rb +51 -16
- data/lib/rigor/inference/refined_dispatch.rb +13 -0
- data/lib/rigor/inference/reflective_ivar_write.rb +19 -11
- data/lib/rigor/inference/remover_holder_scan.rb +287 -0
- data/lib/rigor/inference/remover_narrowing.rb +224 -0
- data/lib/rigor/inference/rewrite_mutation.rb +53 -2
- data/lib/rigor/inference/scope_indexer/certainty.rb +0 -1
- data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +13 -6
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +70 -26
- data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
- data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
- data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +295 -0
- data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +136 -9
- data/lib/rigor/inference/scope_indexer.rb +2213 -226
- data/lib/rigor/inference/self_call_ivar_writes.rb +103 -12
- data/lib/rigor/inference/self_preserving_block.rb +87 -0
- data/lib/rigor/inference/set_mutation.rb +189 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +42 -8
- data/lib/rigor/inference/singleton_object_constant.rb +32 -2
- data/lib/rigor/inference/statement_evaluator.rb +3268 -358
- data/lib/rigor/inference/string_mutation.rb +14 -0
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_scanner.rb +40 -17
- data/lib/rigor/inference/toplevel_def_census.rb +67 -0
- data/lib/rigor/inference/unknown_store_widening.rb +103 -4
- data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
- data/lib/rigor/inference/untyped_rejoin.rb +1 -1
- data/lib/rigor/inference/void_tail_summary.rb +29 -9
- data/lib/rigor/language_server/buffer_table.rb +3 -3
- data/lib/rigor/language_server/completion_provider.rb +26 -20
- data/lib/rigor/language_server/config_warning_announcer.rb +28 -0
- data/lib/rigor/language_server/diagnostic_publisher.rb +8 -3
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -2
- data/lib/rigor/language_server/hover_provider.rb +1 -7
- data/lib/rigor/language_server/hover_renderer.rb +6 -8
- data/lib/rigor/language_server/project_context.rb +39 -1
- data/lib/rigor/language_server/publish_batcher.rb +3 -4
- data/lib/rigor/language_server/selection_range_provider.rb +2 -4
- data/lib/rigor/language_server/server.rb +7 -3
- data/lib/rigor/language_server/signature_help_provider.rb +4 -4
- data/lib/rigor/lens/claims/comparison.rb +78 -0
- data/lib/rigor/lens/claims/request.rb +131 -0
- data/lib/rigor/lens/claims/type_reader.rb +216 -0
- data/lib/rigor/lens/claims.rb +208 -0
- data/lib/rigor/lens/contract_reader.rb +5 -0
- data/lib/rigor/lens/document.rb +1 -1
- data/lib/rigor/lens/file_reader.rb +21 -8
- data/lib/rigor/lens/plugins.rb +1 -1
- data/lib/rigor/lens/run.rb +8 -0
- data/lib/rigor/lens.rb +1 -1
- data/lib/rigor/library_probe.rb +68 -0
- data/lib/rigor/mcp/server.rb +73 -2
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/memo_audit.rb +282 -0
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- data/lib/rigor/plugin/base.rb +15 -2
- data/lib/rigor/plugin/bundled_catalog.rb +1 -12
- data/lib/rigor/plugin/io_boundary.rb +26 -4
- data/lib/rigor/plugin/loader.rb +25 -8
- data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
- data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
- data/lib/rigor/plugin/manifest.rb +16 -16
- data/lib/rigor/plugin/registry.rb +16 -5
- data/lib/rigor/plugin/services.rb +2 -3
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
- data/lib/rigor/plugin/trust_policy.rb +48 -2
- data/lib/rigor/plugin/type_node_resolver.rb +2 -4
- data/lib/rigor/plugin.rb +6 -8
- data/lib/rigor/plugin_gap_advisory.rb +5 -1
- data/lib/rigor/project_environment.rb +16 -2
- data/lib/rigor/protection/analysis_guard.rb +1 -1
- data/lib/rigor/protection/discovery_seed.rb +44 -5
- data/lib/rigor/protection/mutation_cache.rb +4 -1
- data/lib/rigor/protection/mutator.rb +1 -1
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
- data/lib/rigor/rbs_extended/reporter.rb +27 -3
- data/lib/rigor/rbs_extended.rb +2 -3
- data/lib/rigor/reflection/constant_ancestors.rb +81 -22
- data/lib/rigor/reflection/declaration_collectors.rb +55 -0
- data/lib/rigor/reflection/declarations.rb +58 -0
- data/lib/rigor/reflection/project_method.rb +177 -0
- data/lib/rigor/reflection.rb +2 -0
- data/lib/rigor/rooted_glob.rb +32 -1
- data/lib/rigor/run_scoped_slots.rb +38 -0
- data/lib/rigor/runtime/jit.rb +1 -1
- data/lib/rigor/scope/discovery_index.rb +168 -8
- data/lib/rigor/scope/resolution_chain/relevance.rb +14 -1
- data/lib/rigor/scope/resolution_chain.rb +901 -54
- data/lib/rigor/scope.rb +644 -37
- data/lib/rigor/sig_gen/bot_proposal_guard.rb +306 -0
- data/lib/rigor/sig_gen/classification.rb +18 -1
- data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
- data/lib/rigor/sig_gen/generator.rb +588 -66
- data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
- data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
- data/lib/rigor/sig_gen/layout_index.rb +3 -3
- data/lib/rigor/sig_gen/method_candidate.rb +37 -4
- data/lib/rigor/sig_gen/observation_collector.rb +14 -7
- data/lib/rigor/sig_gen/observed_call.rb +1 -1
- data/lib/rigor/sig_gen/path_mapper.rb +6 -0
- data/lib/rigor/sig_gen/platform_guarded_files.rb +184 -0
- data/lib/rigor/sig_gen/renderer.rb +15 -9
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +62 -2
- data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
- data/lib/rigor/sig_gen/write_reporter.rb +63 -14
- data/lib/rigor/sig_gen/write_result.rb +23 -12
- data/lib/rigor/sig_gen/writer.rb +238 -37
- data/lib/rigor/sig_gen.rb +1 -0
- data/lib/rigor/source/forwardable_delegators.rb +122 -0
- data/lib/rigor/source/node_children.rb +13 -0
- data/lib/rigor/type/acceptance_router.rb +2 -2
- data/lib/rigor/type/accepts_result.rb +1 -1
- data/lib/rigor/type/app.rb +4 -6
- data/lib/rigor/type/combinator.rb +173 -21
- data/lib/rigor/type/constant.rb +4 -5
- data/lib/rigor/type/hash_shape.rb +21 -40
- data/lib/rigor/type/interface.rb +60 -0
- data/lib/rigor/type/union.rb +1 -1
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/identifier.rb +1 -2
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node/string_literal.rb +1 -2
- data/lib/rigor/type_node/symbol_literal.rb +1 -2
- data/lib/rigor/type_node/union.rb +3 -6
- data/lib/rigor/type_of_memo/serving.rb +99 -0
- data/lib/rigor/type_of_memo/stats.rb +43 -0
- data/lib/rigor/type_of_memo.rb +337 -0
- data/lib/rigor/typing_state.rb +1094 -0
- data/lib/rigor/value_semantics.rb +1 -10
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +38 -36
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +3 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +115 -36
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +3 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +25 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +16 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +4 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +132 -33
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +20 -6
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +7 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +54 -17
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +19 -5
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +11 -14
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +30 -9
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +55 -13
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +15 -21
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -6
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +1 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +6 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +5 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +1 -1
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +8 -7
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +12 -2
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +8 -2
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +200 -4
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +4 -3
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +5 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -2
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +3 -4
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +6 -8
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +3 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +210 -3
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +146 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +52 -34
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +97 -24
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +16 -16
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +4 -8
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
- data/sig/rigor/analysis/baseline.rbs +0 -3
- data/sig/rigor/analysis/check_rules/rule_walk.rbs +6 -0
- data/sig/rigor/analysis/fact_store.rbs +2 -2
- data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
- data/sig/rigor/cache/descriptor.rbs +14 -0
- data/sig/rigor/cli/diff_command.rbs +1 -1
- data/sig/rigor/cli/explain_command.rbs +1 -1
- data/sig/rigor/effects/registry.rbs +2 -2
- data/sig/rigor/environment.rbs +4 -1
- data/sig/rigor/inference.rbs +4 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +53 -18
- data/sig/rigor/plugin/effect_attribution.rbs +3 -3
- data/sig/rigor/plugin/effect_edge.rbs +1 -1
- data/sig/rigor/plugin/effect_entry_points.rbs +3 -3
- data/sig/rigor/plugin/interfaces.rbs +51 -0
- data/sig/rigor/plugin/io_boundary.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +9 -9
- data/sig/rigor/plugin/node_context.rbs +21 -0
- data/sig/rigor/plugin/protocol_contract.rbs +3 -7
- data/sig/rigor/reflection.rbs +37 -0
- data/sig/rigor/scope.rbs +43 -4
- data/sig/rigor/trinary.rbs +1 -0
- data/sig/rigor/type.rbs +9 -3
- data/sig/rigor.rbs +3 -2
- data/skills/rigor-ask/SKILL.md +1 -0
- data/skills/rigor-plugin-author/SKILL.md +4 -4
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
- data/skills/rigor-plugin-review/SKILL.md +3 -3
- data/skills/rigor-project-init/references/01-detect.md +1 -0
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +17 -2
- data/skills/rigor-project-init/references/06-agent-contract.md +4 -4
- data/skills/rigor-rbs-contract/SKILL.md +147 -0
- data/skills/rigor-rbs-contract/references/01-finding-candidates.md +212 -0
- data/skills/rigor-rbs-contract/references/02-contract-rules.md +157 -0
- data/skills/rigor-rbs-contract/references/03-target-own-code.md +90 -0
- data/skills/rigor-rbs-contract/references/04-target-gem-rbs-collection.md +210 -0
- data/skills/rigor-rbs-contract/references/05-declaration-mutants.md +182 -0
- data/skills/rigor-rbs-setup/SKILL.md +3 -1
- data/skills/rigor-type-oracle/SKILL.md +26 -1
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +4 -3
- metadata +82 -3
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rbs"
|
|
4
|
+
|
|
5
|
+
require_relative "../environment/rbs_loader"
|
|
6
|
+
require_relative "../rooted_glob"
|
|
7
|
+
|
|
8
|
+
module Rigor
|
|
9
|
+
module SigGen
|
|
10
|
+
# Issues #2603 and #2605 — the run-wide view of the parameterless headers of generic classes (`class Array`, as
|
|
11
|
+
# sig-gen spelled it before #2436) that `sig/` holds, read before the writer changes anything.
|
|
12
|
+
#
|
|
13
|
+
# rbs rejects a class with `GenericParameterMismatchError` while any one of its declarations opens it bare, so
|
|
14
|
+
# the repair is decided per class, across every file the project's RBS env loads from `signature_paths:`: either
|
|
15
|
+
# every bare opening of the class is repaired, or none is. A class is held back ({#left_bare}) when the body of
|
|
16
|
+
# any of its bare openings names one of the parameters as a relative constant ({.shadowing_names}), because the
|
|
17
|
+
# inserted parameter would capture that name, or when one of its bare openings lies outside the sig root, which
|
|
18
|
+
# the writer never rewrites. Every file under the sig root is read, not only those a candidate of the run routes
|
|
19
|
+
# to, so a bare header whose source file is gone is repaired too (#2603); the other signature paths are read
|
|
20
|
+
# and never written.
|
|
21
|
+
#
|
|
22
|
+
# A file that is not valid UTF-8, or does not parse, is not read: rbs never loads it, and the writer refuses it.
|
|
23
|
+
class GenericHeaderScan
|
|
24
|
+
# One bare opening: the file it is in, the class it opens, the parameter names its body spells, and whether
|
|
25
|
+
# the file is under the sig root (the writer may rewrite it).
|
|
26
|
+
Opening = Data.define(:target, :class_name, :names, :writable)
|
|
27
|
+
|
|
28
|
+
# @param sig_root — the directory the writer writes to; the only files it may rewrite.
|
|
29
|
+
# @param signature_dirs — the other directories the RBS env loads (`signature_paths:`), read only. A file
|
|
30
|
+
# under the sig root is read once, as a writable one.
|
|
31
|
+
# @param spelling_for — called with a class name, returns the header spelling to insert
|
|
32
|
+
# (`"[unchecked out E]"`), or nil for a class that is not generic with plain parameters only.
|
|
33
|
+
def self.scan(sig_root:, spelling_for:, signature_dirs: [])
|
|
34
|
+
new(spelling_for).tap do |scan|
|
|
35
|
+
scan.read_tree(sig_root, writable: true)
|
|
36
|
+
signature_dirs.each { |dir| scan.read_tree(dir, writable: false) }
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# The names in `names` that the declaration's text after its name (superclass, members, nested declarations)
|
|
41
|
+
# spells as a relative constant (`E`, `E::X`, not `::E` or `Foo::E`). A method type that declares a name as
|
|
42
|
+
# its own type parameter (`def m: [E] (E) -> E`) binds every `E` inside it, so its text does not count for
|
|
43
|
+
# that name. The rest is matched as text, so a comment that spells the name counts.
|
|
44
|
+
def self.shadowing_names(source, decl, names)
|
|
45
|
+
start = decl.location[:name].end_pos
|
|
46
|
+
body = source[start...decl.location.end_pos]
|
|
47
|
+
names.select do |name|
|
|
48
|
+
masked = mask_binders(body, binder_ranges(decl, name), start)
|
|
49
|
+
masked.match?(/(?<![\w:])#{Regexp.escape(name)}(?!\w)/)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# The source ranges of the method types under `decl` (nested declarations included) that declare `name` as a
|
|
54
|
+
# type parameter of their own.
|
|
55
|
+
def self.binder_ranges(decl, name, ranges = [])
|
|
56
|
+
(decl.respond_to?(:members) ? decl.members : []).each do |member|
|
|
57
|
+
if member.is_a?(RBS::AST::Members::MethodDefinition)
|
|
58
|
+
member.overloads.each do |overload|
|
|
59
|
+
type = overload.method_type
|
|
60
|
+
next unless type.location && type.type_params.any? { |param| param.name.to_s == name }
|
|
61
|
+
|
|
62
|
+
ranges << (type.location.start_pos...type.location.end_pos)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
binder_ranges(member, name, ranges)
|
|
66
|
+
end
|
|
67
|
+
ranges
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def self.mask_binders(body, ranges, offset)
|
|
71
|
+
masked = body.dup
|
|
72
|
+
ranges.each do |range|
|
|
73
|
+
from = range.begin - offset
|
|
74
|
+
masked[from, range.size] = " " * range.size if from >= 0 && from + range.size <= masked.size
|
|
75
|
+
end
|
|
76
|
+
masked
|
|
77
|
+
end
|
|
78
|
+
private_class_method :mask_binders
|
|
79
|
+
|
|
80
|
+
# The parameter names `[unchecked out K, unchecked out V]` declares; nil when it does not parse.
|
|
81
|
+
def self.parameter_names(spelling)
|
|
82
|
+
_, _, decls = RBS::Parser.parse_signature("class RigorSigGenProbe#{spelling}\nend\n")
|
|
83
|
+
decls.first&.type_params&.map { |param| param.name.to_s }
|
|
84
|
+
rescue RBS::ParsingError
|
|
85
|
+
nil
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def initialize(spelling_for)
|
|
89
|
+
@spelling_for = spelling_for
|
|
90
|
+
@openings = []
|
|
91
|
+
@seen = Set.new
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Every class to repair, with the spelling to insert: each bare opening of it, in every file read, is clear.
|
|
95
|
+
def repairs
|
|
96
|
+
@repairs ||= by_class.reject { |_, openings| openings.any? { |o| blocking?(o) } }
|
|
97
|
+
.to_h { |name, _| [name, spelling(name)] }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# The files that hold a bare opening of a class in {#repairs}, as their `to_s`.
|
|
101
|
+
def repair_targets
|
|
102
|
+
@openings.select { |o| repairs.key?(o.class_name) }.map(&:target).uniq
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# One entry per class held back: `{ class:, targets:, blocking: [...] }`, where `targets` is every file with a
|
|
106
|
+
# bare opening of it and `blocking` the openings that hold it back, each `{ target:, reason:
|
|
107
|
+
# "outside_sig_root" }` (a file the writer does not rewrite) or `{ target:, reason: "names_parameter", names: }`.
|
|
108
|
+
def left_bare
|
|
109
|
+
by_class.filter_map do |name, openings|
|
|
110
|
+
blocking = openings.select { |o| blocking?(o) }
|
|
111
|
+
next if blocking.empty?
|
|
112
|
+
|
|
113
|
+
{ class: name, targets: openings.map(&:target).uniq, blocking: blocking.map { |o| blocking_entry(o) } }
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def read_tree(dir, writable:)
|
|
118
|
+
return unless dir.directory?
|
|
119
|
+
|
|
120
|
+
RootedGlob.glob(dir.to_s, "**/*.rbs").sort.each do |path|
|
|
121
|
+
key = realpath(path)
|
|
122
|
+
read_file(Pathname(path), writable) if @seen.add?(key)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
private
|
|
127
|
+
|
|
128
|
+
def blocking?(opening)
|
|
129
|
+
!opening.writable || !opening.names.empty?
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def blocking_entry(opening)
|
|
133
|
+
return { target: opening.target, reason: "outside_sig_root" } unless opening.writable
|
|
134
|
+
|
|
135
|
+
{ target: opening.target, reason: "names_parameter", names: opening.names }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def realpath(path)
|
|
139
|
+
File.realpath(path)
|
|
140
|
+
rescue SystemCallError
|
|
141
|
+
File.expand_path(path)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def by_class
|
|
145
|
+
@by_class ||= @openings.group_by(&:class_name).sort.to_h
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def spelling(name)
|
|
149
|
+
@spellings ||= {}
|
|
150
|
+
return @spellings[name] if @spellings.key?(name)
|
|
151
|
+
|
|
152
|
+
@spellings[name] = @spelling_for.call(name)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def read_file(path, writable)
|
|
156
|
+
source = path.read
|
|
157
|
+
return unless source.valid_encoding?
|
|
158
|
+
|
|
159
|
+
_, _, decls = RBS::Parser.parse_signature(source)
|
|
160
|
+
collect([path.to_s, writable], source, decls, [])
|
|
161
|
+
rescue RBS::ParsingError, SystemCallError
|
|
162
|
+
nil
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def collect(target, source, decls, prefix)
|
|
166
|
+
decls.each do |decl|
|
|
167
|
+
next unless decl.is_a?(RBS::AST::Declarations::Class) || decl.is_a?(RBS::AST::Declarations::Module)
|
|
168
|
+
|
|
169
|
+
# RBS resolves `module Foo; class ::Set` to the top-level `Set`, and so does this key (#2654).
|
|
170
|
+
path = Environment::RbsLoader.declared_class_path(decl, prefix)
|
|
171
|
+
record(target, source, decl, path.join("::"))
|
|
172
|
+
collect(target, source, decl.members, path)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def record(target, source, decl, name)
|
|
177
|
+
return unless decl.type_params.empty? && decl.location&.[](:name)
|
|
178
|
+
|
|
179
|
+
spelled = spelling(name)
|
|
180
|
+
params = spelled && self.class.parameter_names(spelled)
|
|
181
|
+
return if params.nil?
|
|
182
|
+
|
|
183
|
+
file, writable = target
|
|
184
|
+
@openings << Opening.new(target: file, class_name: name, writable: writable,
|
|
185
|
+
names: self.class.shadowing_names(source, decl, params))
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
@@ -23,7 +23,7 @@ module Rigor
|
|
|
23
23
|
class InlineDeclarations
|
|
24
24
|
# One inline-declared method.
|
|
25
25
|
#
|
|
26
|
-
# - `method_types` — the declared overloads
|
|
26
|
+
# - `method_types` — the declared overloads, names as written and unresolved.
|
|
27
27
|
# - `annotations` — the annotation strings the author wrote on the member (`deprecated` for
|
|
28
28
|
# `%a{deprecated}`), without the synthesizer's own `rigor:v1:inferred-*` markers.
|
|
29
29
|
# - `return_inferred` — the author declared the parameters and not the return: rbs-inline defaulted it,
|
|
@@ -112,7 +112,7 @@ module Rigor
|
|
|
112
112
|
decls.each do |decl|
|
|
113
113
|
next unless decl.is_a?(::RBS::AST::Declarations::Class) || decl.is_a?(::RBS::AST::Declarations::Module)
|
|
114
114
|
|
|
115
|
-
inner =
|
|
115
|
+
inner = ::Rigor::Environment::RbsLoader.declared_class_path(decl, prefix)
|
|
116
116
|
@generic_classes[inner.join("::")] ||= decl.type_params.map(&:name) unless decl.type_params.empty?
|
|
117
117
|
decl.members.each { |member| block.call(inner.join("::"), member) }
|
|
118
118
|
each_member(decl.members, inner, &block)
|
|
@@ -126,8 +126,8 @@ module Rigor
|
|
|
126
126
|
return unless decl.is_a?(RBS::AST::Declarations::Class) ||
|
|
127
127
|
decl.is_a?(RBS::AST::Declarations::Module)
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
full =
|
|
129
|
+
path = ::Rigor::Environment::RbsLoader.declared_class_path(decl, prefix)
|
|
130
|
+
full = path.join("::")
|
|
131
131
|
accumulator[full] ||= rbs_path
|
|
132
132
|
decl.members.each do |member|
|
|
133
133
|
# An `overloading?` member (`def f: ... | ...`) adds to a declaration elsewhere; routing a candidate to
|
|
@@ -137,7 +137,7 @@ module Rigor
|
|
|
137
137
|
self.class.member_keys(member).each { |name, kind| @members[[full, name, kind]] ||= rbs_path }
|
|
138
138
|
end
|
|
139
139
|
|
|
140
|
-
record_decls(decl.members,
|
|
140
|
+
record_decls(decl.members, path, rbs_path, accumulator)
|
|
141
141
|
end
|
|
142
142
|
end
|
|
143
143
|
end
|
|
@@ -15,7 +15,7 @@ module Rigor
|
|
|
15
15
|
# ever `skipped` (`sig.skipped.top-level-def`, #1676).
|
|
16
16
|
# - `method_name` — the def's `Symbol` name.
|
|
17
17
|
# - `kind` — `:instance` or `:singleton`.
|
|
18
|
-
# - `inferred_return` —
|
|
18
|
+
# - `inferred_return` — the inferred return type (or `nil` when the inference pass disqualified the def).
|
|
19
19
|
# - `declared_return_rbs` — the existing RBS-declared return spelling, or `nil` when no RBS declares the
|
|
20
20
|
# method.
|
|
21
21
|
# - `declared_rbs` — for an `inline_overwrite`, or a `tighter_return` over the class's own declaration, the
|
|
@@ -33,13 +33,14 @@ module Rigor
|
|
|
33
33
|
class MethodCandidate
|
|
34
34
|
attr_reader :path, :class_name, :method_name, :kind, :classification,
|
|
35
35
|
:inferred_return, :declared_return_rbs, :rbs, :skip_reason,
|
|
36
|
-
:namespace_kinds, :class_shells, :class_superclasses,
|
|
36
|
+
:namespace_kinds, :class_shells, :class_superclasses, :class_type_params,
|
|
37
37
|
:annotations, :effect_reason, :declared_rbs, :declared_annotations
|
|
38
38
|
|
|
39
39
|
def initialize(path:, class_name:, method_name:, kind:, classification:, # rubocop:disable Metrics/ParameterLists
|
|
40
40
|
inferred_return: nil, declared_return_rbs: nil, rbs: nil, skip_reason: nil,
|
|
41
41
|
namespace_kinds: {}, class_shells: [], class_superclasses: {},
|
|
42
|
-
annotations: [], effect_reason: nil, declared_rbs: nil, declared_annotations: []
|
|
42
|
+
annotations: [], effect_reason: nil, declared_rbs: nil, declared_annotations: [],
|
|
43
|
+
class_type_params: {})
|
|
43
44
|
@path = path
|
|
44
45
|
@class_name = class_name
|
|
45
46
|
@method_name = method_name
|
|
@@ -55,6 +56,10 @@ module Rigor
|
|
|
55
56
|
# superclasses appear; computed ones are absent. The Writer emits `class Bar < Base` for the leaf when
|
|
56
57
|
# present.
|
|
57
58
|
@class_superclasses = class_superclasses.freeze
|
|
59
|
+
# Issue #2436 — qualified class name => the type-parameter list a loaded declaration gives it, spelled for
|
|
60
|
+
# a header (`{ "Array" => "[unchecked out E]" }`). A header the writer opens for one of these classes
|
|
61
|
+
# carries the list; a class absent from the map is written without one.
|
|
62
|
+
@class_type_params = class_type_params.freeze
|
|
58
63
|
@annotations = annotations.freeze
|
|
59
64
|
@effect_reason = effect_reason
|
|
60
65
|
@declared_rbs = declared_rbs
|
|
@@ -85,7 +90,35 @@ module Rigor
|
|
|
85
90
|
declared_return_rbs: @declared_return_rbs, rbs: @rbs, skip_reason: @skip_reason,
|
|
86
91
|
namespace_kinds: @namespace_kinds, class_shells: @class_shells,
|
|
87
92
|
class_superclasses: @class_superclasses, annotations: annotations, effect_reason: reason,
|
|
88
|
-
declared_rbs: @declared_rbs, declared_annotations: @declared_annotations
|
|
93
|
+
declared_rbs: @declared_rbs, declared_annotations: @declared_annotations,
|
|
94
|
+
class_type_params: @class_type_params
|
|
95
|
+
)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# A copy carrying the run's reopened-class type parameters (#2436).
|
|
99
|
+
def with_class_type_params(class_type_params)
|
|
100
|
+
self.class.new(
|
|
101
|
+
path: @path, class_name: @class_name, method_name: @method_name, kind: @kind,
|
|
102
|
+
classification: @classification, inferred_return: @inferred_return,
|
|
103
|
+
declared_return_rbs: @declared_return_rbs, rbs: @rbs, skip_reason: @skip_reason,
|
|
104
|
+
namespace_kinds: @namespace_kinds, class_shells: @class_shells,
|
|
105
|
+
class_superclasses: @class_superclasses, annotations: @annotations, effect_reason: @effect_reason,
|
|
106
|
+
declared_rbs: @declared_rbs, declared_annotations: @declared_annotations,
|
|
107
|
+
class_type_params: class_type_params
|
|
108
|
+
)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# A copy carrying more class shells: a class whose every member a later pass withheld still has to be
|
|
112
|
+
# declared for its subclasses' headers (#3029).
|
|
113
|
+
def with_class_shells(class_shells)
|
|
114
|
+
self.class.new(
|
|
115
|
+
path: @path, class_name: @class_name, method_name: @method_name, kind: @kind,
|
|
116
|
+
classification: @classification, inferred_return: @inferred_return,
|
|
117
|
+
declared_return_rbs: @declared_return_rbs, rbs: @rbs, skip_reason: @skip_reason,
|
|
118
|
+
namespace_kinds: @namespace_kinds, class_shells: class_shells,
|
|
119
|
+
class_superclasses: @class_superclasses, annotations: @annotations, effect_reason: @effect_reason,
|
|
120
|
+
declared_rbs: @declared_rbs, declared_annotations: @declared_annotations,
|
|
121
|
+
class_type_params: @class_type_params
|
|
89
122
|
)
|
|
90
123
|
end
|
|
91
124
|
|
|
@@ -4,6 +4,7 @@ require "prism"
|
|
|
4
4
|
|
|
5
5
|
require_relative "../environment"
|
|
6
6
|
require_relative "../project_environment"
|
|
7
|
+
require_relative "../run_scoped_slots"
|
|
7
8
|
require_relative "../scope"
|
|
8
9
|
require_relative "../type"
|
|
9
10
|
require_relative "../source/constant_path"
|
|
@@ -19,9 +20,7 @@ module Rigor
|
|
|
19
20
|
# Walks the `--observe=PATH...` tree (default: the project's test roots), parses every `.rb` file with `Prism`,
|
|
20
21
|
# scope- indexes it the same way the main generator does, and records the per-call-site argument-type tuples
|
|
21
22
|
# for every `Prism::CallNode` whose receiver types as a `Type::Nominal`. The {Generator} consumes the
|
|
22
|
-
# resulting map to render `--params=observed` RBS
|
|
23
|
-
#
|
|
24
|
-
# @return => Array<Array<Rigor::Type>>}]
|
|
23
|
+
# resulting map to render `--params=observed` RBS.
|
|
25
24
|
#
|
|
26
25
|
# ADR-5 clause 2 compliance: the observed union is the MOST PERMISSIVE parameter contract the existing
|
|
27
26
|
# callers prove sufficient — by construction it accepts every type any caller has already passed. The
|
|
@@ -29,8 +28,8 @@ module Rigor
|
|
|
29
28
|
# user opts in.
|
|
30
29
|
#
|
|
31
30
|
# MVP scope:
|
|
32
|
-
# - Explicit-receiver calls only (`foo.bar(args)`). Implicit-self calls inside class bodies
|
|
33
|
-
# `let` / `subject`
|
|
31
|
+
# - Explicit-receiver calls only (`foo.bar(args)`). Implicit-self calls inside class bodies are skipped;
|
|
32
|
+
# RSpec `let` / `subject` receivers are resolved from the spec file (ADR-14 slice 5).
|
|
34
33
|
# - Calls whose receiver does not type as a `Type::Nominal` (e.g. `(some_dynamic).bar(...)`) are skipped —
|
|
35
34
|
# the collector cannot attribute them to a specific class.
|
|
36
35
|
# - A `T | nil` receiver with one `Nominal` / `Singleton` non-nil member counts as that member (#1389).
|
|
@@ -58,10 +57,18 @@ module Rigor
|
|
|
58
57
|
environment = build_environment(resolved)
|
|
59
58
|
discovered_classes = preindex_source_classes
|
|
60
59
|
observations = Hash.new { |h, k| h[k] = [] }
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
# ADR-130 WD5 — with interface dispatch off, as {Generator#run} analyses: an argument read through a
|
|
61
|
+
# `Rigor::Plugin` interface member is not written into a parameter type either.
|
|
62
|
+
Inference::MethodDispatcher::RbsDispatch.without_interface_dispatch do
|
|
63
|
+
resolved.each do |path|
|
|
64
|
+
collect_from_file(path, environment, discovered_classes, observations)
|
|
65
|
+
end
|
|
63
66
|
end
|
|
64
67
|
observations.transform_values(&:freeze).freeze
|
|
68
|
+
ensure
|
|
69
|
+
# Issue #2869 — release this analysis's run-scoped slots, as every other entry point that runs one does, so
|
|
70
|
+
# a later run (the generator's, under its own interface-dispatch install) starts from none of them.
|
|
71
|
+
RunScopedSlots.release
|
|
65
72
|
end
|
|
66
73
|
|
|
67
74
|
# Whether `arg` names something {#collect} reads: a directory, or a `.rb` file. The CLI's
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module SigGen
|
|
5
5
|
# Per-call-site argument observation produced by {ObservationCollector}. ADR-14 follow-up: the earlier MVP
|
|
6
|
-
# shape (
|
|
6
|
+
# shape (positional types only) could not represent keyword arguments — keyword calls like
|
|
7
7
|
# `MethodCatalog.new(path: ..., mutating_selectors: ...)` were silently skipped in that shape. The new shape
|
|
8
8
|
# carries positional and keyword arg types in parallel so the per-position / per-keyword unions can each be
|
|
9
9
|
# reconstructed independently.
|
|
@@ -74,6 +74,12 @@ module Rigor
|
|
|
74
74
|
@sig_root_dir ||= @project_root / sig_root_name
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
+
# Every directory the project's RBS env loads signatures from (`signature_paths:`), resolved against the
|
|
78
|
+
# project root. The writer reads them to decide a repair (#2605) and writes only under {#sig_root_dir}.
|
|
79
|
+
def signature_dirs
|
|
80
|
+
Array(@configuration.signature_paths).map { |path| @project_root / path }
|
|
81
|
+
end
|
|
82
|
+
|
|
77
83
|
private
|
|
78
84
|
|
|
79
85
|
def source_relative_to_root(source_path)
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
|
|
5
|
+
require_relative "../source/node_children"
|
|
6
|
+
|
|
7
|
+
module Rigor
|
|
8
|
+
module SigGen
|
|
9
|
+
# Issue #3035 — the files a sig-gen run must not read as ordinary definitions: every scanned `.rb`
|
|
10
|
+
# file Ruby loads ONLY through a platform-conditional `require` / `require_relative`.
|
|
11
|
+
#
|
|
12
|
+
# `stackprof.rb` opens with `if RUBY_ENGINE == 'truffleruby'` → `require "stackprof/truffleruby"`,
|
|
13
|
+
# and `truffleruby.rb` re-implements `StackProf.run` / `.results` as raising stubs — on MRI the C
|
|
14
|
+
# extension defines them. Scanning every `.rb` under `lib/` reads that stub file as an ordinary
|
|
15
|
+
# definition and proposes `StackProf.run -> bot` against a method whose real signature returns
|
|
16
|
+
# `Hash[Symbol, untyped]`.
|
|
17
|
+
#
|
|
18
|
+
# {.call} answers the guarded set from the resolved file list alone: it parses each file once,
|
|
19
|
+
# collects the literal `require` / `require_relative` sites, marks a site guarded when it sits
|
|
20
|
+
# inside a conditional whose predicate reads `RUBY_ENGINE`, `RUBY_PLATFORM` or `defined?(…)`,
|
|
21
|
+
# resolves each feature against the scanned set (`require_relative` against the file's own
|
|
22
|
+
# directory; `require` against the file's ancestor directories — the load-path approximation a
|
|
23
|
+
# gem's `lib/` root satisfies), and keeps the files whose EVERY incoming edge is guarded —
|
|
24
|
+
# directly, or through a file already known guarded (a helper `truffleruby.rb` requires
|
|
25
|
+
# unconditionally is still truffleruby-only). A file with no scanned require site is never
|
|
26
|
+
# guarded: an entry point, an autoload target or a `load`ed file cannot be told apart from a
|
|
27
|
+
# guarded one, and the unguarded reading is the one that emits.
|
|
28
|
+
#
|
|
29
|
+
# Limits, deliberately: require sites in files OUTSIDE the scanned set are invisible (a sibling
|
|
30
|
+
# the run did not read may require the file unguarded); `load`, `autoload` and computed features
|
|
31
|
+
# are not edges; a `require` sitting in the guard's own predicate — an `&&` / `||` operand, say —
|
|
32
|
+
# inherits the branch's guarded mark without the walk checking whether Ruby reaches it before the
|
|
33
|
+
# platform test decides; the `begin … rescue LoadError` fallback (`require "json"` else
|
|
34
|
+
# `require "json/pure"`) is not a platform guard, so both of its edges read unguarded; and a file
|
|
35
|
+
# that fails to parse poisons the whole answer — its require edges are unknowable, so {.call}
|
|
36
|
+
# returns an empty set rather than risk a false guarded verdict.
|
|
37
|
+
module PlatformGuardedFiles
|
|
38
|
+
# The constants a conditional predicate may read to make a require platform-dependent.
|
|
39
|
+
GUARD_CONSTANTS = %i[RUBY_ENGINE RUBY_PLATFORM].freeze
|
|
40
|
+
private_constant :GUARD_CONSTANTS
|
|
41
|
+
|
|
42
|
+
# The conditional forms whose whole subtree a platform predicate guards — `if` / `unless` in
|
|
43
|
+
# statement, modifier and ternary spelling, `case` and `case/in` on a guarded subject. Both
|
|
44
|
+
# branches count: `else` of `RUBY_ENGINE == 'x'` is `RUBY_ENGINE != 'x'`, still conditional.
|
|
45
|
+
CONDITIONALS = [Prism::IfNode, Prism::UnlessNode, Prism::CaseNode, Prism::CaseMatchNode].freeze
|
|
46
|
+
private_constant :CONDITIONALS
|
|
47
|
+
|
|
48
|
+
REQUIRE_CALLS = %i[require require_relative].freeze
|
|
49
|
+
private_constant :REQUIRE_CALLS
|
|
50
|
+
|
|
51
|
+
module_function
|
|
52
|
+
|
|
53
|
+
# @param paths — the `.rb` files the run will analyse, as spelled by the run.
|
|
54
|
+
# @param ruby_version — the Prism version to parse with (the configuration's `target_ruby`).
|
|
55
|
+
# @return a Set of `File.expand_path`'d paths that are required only under platform guards.
|
|
56
|
+
def call(paths, ruby_version: nil)
|
|
57
|
+
files = paths.map { |path| File.expand_path(path) }.uniq
|
|
58
|
+
known = files.to_set
|
|
59
|
+
edges = Hash.new { |hash, key| hash[key] = [] }
|
|
60
|
+
files.each do |file|
|
|
61
|
+
sites = require_sites(file, ruby_version, known)
|
|
62
|
+
return Set.new if sites.nil?
|
|
63
|
+
|
|
64
|
+
sites.each { |target, guarded| edges[target] << { from: file, guarded: guarded } }
|
|
65
|
+
end
|
|
66
|
+
guarded_closure(files, edges)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Every `[target, guarded]` pair `file`'s literal require sites resolve to inside `known`;
|
|
70
|
+
# `nil` when the file does not parse (see the class comment for why that voids the answer).
|
|
71
|
+
def require_sites(file, ruby_version, known)
|
|
72
|
+
result = Prism.parse(File.read(file), filepath: file, version: ruby_version)
|
|
73
|
+
return nil if result.errors.any?
|
|
74
|
+
|
|
75
|
+
sites = []
|
|
76
|
+
walk_require_sites(result.value) do |node, guarded|
|
|
77
|
+
target = require_target(file, node, known)
|
|
78
|
+
sites << [target, guarded] if target
|
|
79
|
+
end
|
|
80
|
+
sites
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# An explicit stack, not recursion: a require site deep in a generated file must not exhaust it.
|
|
84
|
+
def walk_require_sites(root, &block)
|
|
85
|
+
stack = [[root, false]]
|
|
86
|
+
until stack.empty?
|
|
87
|
+
node, guarded = stack.pop
|
|
88
|
+
next unless node.is_a?(Prism::Node)
|
|
89
|
+
|
|
90
|
+
block.call(node, guarded) if require_call?(node)
|
|
91
|
+
child_guarded = guarded || platform_conditional?(node)
|
|
92
|
+
node.rigor_each_child { |child| stack << [child, child_guarded] }
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# A `require "feature"` / `require_relative "feature"` with one string literal and no receiver —
|
|
97
|
+
# the same strictness {Analysis::RequireRelativeClosure} reads `require_relative` with.
|
|
98
|
+
def require_call?(node)
|
|
99
|
+
return false unless node.is_a?(Prism::CallNode) && node.receiver.nil?
|
|
100
|
+
return false unless REQUIRE_CALLS.include?(node.name)
|
|
101
|
+
|
|
102
|
+
arguments = node.arguments&.arguments
|
|
103
|
+
arguments&.size == 1 && arguments.first.is_a?(Prism::StringNode)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Whether `node` is a conditional whose predicate makes what it guards platform-dependent: a
|
|
107
|
+
# `RUBY_ENGINE` / `RUBY_PLATFORM` read or a `defined?(…)` anywhere in it.
|
|
108
|
+
def platform_conditional?(node)
|
|
109
|
+
return false unless CONDITIONALS.any? { |klass| node.is_a?(klass) }
|
|
110
|
+
|
|
111
|
+
predicate = node.predicate
|
|
112
|
+
!predicate.nil? && platform_expression?(predicate)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Iterative like {#walk_require_sites}: a predicate can chain thousands of `||` operands. A hit is
|
|
116
|
+
# the only answer — a constant that is NOT a guard (`RUBY_VERSION`, `ENV`, a namespaced path) does
|
|
117
|
+
# not end the walk, or `RUBY_ENGINE == "x" && RUBY_VERSION >= "y"` would read unguarded on the
|
|
118
|
+
# strength of which operand the stack pops first.
|
|
119
|
+
def platform_expression?(root)
|
|
120
|
+
stack = [root]
|
|
121
|
+
until stack.empty?
|
|
122
|
+
node = stack.pop
|
|
123
|
+
next unless node.is_a?(Prism::Node)
|
|
124
|
+
return true if node.is_a?(Prism::DefinedNode)
|
|
125
|
+
return true if node.is_a?(Prism::ConstantReadNode) && GUARD_CONSTANTS.include?(node.name)
|
|
126
|
+
# `::RUBY_ENGINE` names the same constant; `Foo::RUBY_ENGINE` is a different one.
|
|
127
|
+
return true if node.is_a?(Prism::ConstantPathNode) && node.parent.nil? &&
|
|
128
|
+
GUARD_CONSTANTS.include?(node.name)
|
|
129
|
+
|
|
130
|
+
node.rigor_each_child { |child| stack << child }
|
|
131
|
+
end
|
|
132
|
+
false
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# The scanned file `node`'s feature resolves to, or nil when it names nothing the run reads
|
|
136
|
+
# (a gem, a `.so`, a computed path's sibling).
|
|
137
|
+
def require_target(site_file, node, known)
|
|
138
|
+
feature = node.arguments.arguments.first.unescaped
|
|
139
|
+
feature += ".rb" unless feature.end_with?(".rb")
|
|
140
|
+
if node.name == :require_relative
|
|
141
|
+
candidate = File.expand_path(feature, File.dirname(site_file))
|
|
142
|
+
return candidate if known.include?(candidate)
|
|
143
|
+
|
|
144
|
+
return nil
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# `require` resolves through the load path; the file's own directory upward is the
|
|
148
|
+
# approximation that finds `lib/stackprof/truffleruby.rb` for `require "stackprof/truffleruby"`
|
|
149
|
+
# in `lib/stackprof.rb`, nearest ancestor first.
|
|
150
|
+
dir = File.dirname(site_file)
|
|
151
|
+
loop do
|
|
152
|
+
candidate = File.expand_path(feature, dir)
|
|
153
|
+
return candidate if known.include?(candidate)
|
|
154
|
+
|
|
155
|
+
parent = File.dirname(dir)
|
|
156
|
+
return nil if parent == dir
|
|
157
|
+
|
|
158
|
+
dir = parent
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# The fixpoint: a file is guarded-only when it has at least one scanned require edge and every
|
|
163
|
+
# edge reaching it is either syntactically guarded or issued from a file already guarded-only.
|
|
164
|
+
def guarded_closure(files, edges)
|
|
165
|
+
guarded = Set.new
|
|
166
|
+
loop do
|
|
167
|
+
added = false
|
|
168
|
+
files.each do |file|
|
|
169
|
+
next if guarded.include?(file)
|
|
170
|
+
|
|
171
|
+
incoming = edges[file]
|
|
172
|
+
next if incoming.empty?
|
|
173
|
+
next unless incoming.all? { |edge| edge[:guarded] || guarded.include?(edge[:from]) }
|
|
174
|
+
|
|
175
|
+
guarded << file
|
|
176
|
+
added = true
|
|
177
|
+
end
|
|
178
|
+
break unless added
|
|
179
|
+
end
|
|
180
|
+
guarded
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require "json"
|
|
4
4
|
|
|
5
|
+
require_relative "../cli/json_document"
|
|
6
|
+
|
|
5
7
|
require_relative "classification"
|
|
6
8
|
require_relative "superclass_spelling"
|
|
7
9
|
require_relative "write_reporter"
|
|
@@ -14,8 +16,7 @@ module Rigor
|
|
|
14
16
|
# - `:print` (default) — RBS skeletons grouped by source file and class declaration, ready for the user to
|
|
15
17
|
# paste into `sig/<path>.rbs`.
|
|
16
18
|
# - `:diff` — a unified-style diff comparing the existing RBS spelling (if any) against the inferred
|
|
17
|
-
# spelling
|
|
18
|
-
# slice 2's `--write` merge.
|
|
19
|
+
# spelling, as a minimal "- declared / + inferred" block.
|
|
19
20
|
# - `:json` — machine-readable payload with the same classification table as `:print`, plus every `skipped`
|
|
20
21
|
# row with its `skip_reason` (#778).
|
|
21
22
|
class Renderer
|
|
@@ -95,11 +96,14 @@ module Rigor
|
|
|
95
96
|
# mode used to hard-code `class`, which turned a module into a class the moment it held an emittable method —
|
|
96
97
|
# output that raises `RBS::DuplicatedDeclarationError` on load if the real `module` is declared anywhere else
|
|
97
98
|
# (#227). Defaulting to `class` when the map has no entry keeps the pre-existing spelling for a leaf class.
|
|
99
|
+
# A generic class the run reopens carries its declaration's type parameters, as the written header does (#2436).
|
|
98
100
|
def declaration_header(candidate, class_name)
|
|
99
|
-
|
|
101
|
+
params = candidate.class_type_params[class_name]
|
|
102
|
+
return "module #{class_name}#{params}" if candidate.namespace_kinds[class_name] == :module
|
|
100
103
|
|
|
101
104
|
superclass = candidate.class_superclasses[class_name]
|
|
102
|
-
|
|
105
|
+
head = "class #{class_name}#{params}"
|
|
106
|
+
superclass ? "#{head} < #{SuperclassSpelling.absolute(superclass)}" : head
|
|
103
107
|
end
|
|
104
108
|
|
|
105
109
|
def render_diff(candidates)
|
|
@@ -130,19 +134,21 @@ module Rigor
|
|
|
130
134
|
payload = { candidates: candidates.map(&:to_h) }
|
|
131
135
|
payload[:observation] = observation if observation
|
|
132
136
|
payload[:diagnostics] = diagnostics.map(&:to_h) if diagnostics.any?
|
|
133
|
-
@out.puts(JSON.pretty_generate(payload))
|
|
137
|
+
@out.puts(JSON.pretty_generate(CLI::JsonDocument.stamp(payload)))
|
|
134
138
|
end
|
|
135
139
|
|
|
136
140
|
public
|
|
137
141
|
|
|
138
142
|
# The per-source-file outcomes of a `--write` run, and the `--check` verdict, are reported by
|
|
139
143
|
# {WriteReporter}; these delegate so the command keeps one renderer.
|
|
140
|
-
def render_write(results:, format:, refused: [])
|
|
141
|
-
WriteReporter.new(out: @out).render_write(results: results, format: format, refused: refused
|
|
144
|
+
def render_write(results:, format:, refused: [], left_bare: [])
|
|
145
|
+
WriteReporter.new(out: @out).render_write(results: results, format: format, refused: refused,
|
|
146
|
+
left_bare: left_bare)
|
|
142
147
|
end
|
|
143
148
|
|
|
144
|
-
def render_check(results:, format:, refused: [])
|
|
145
|
-
WriteReporter.new(out: @out).render_check(results: results, format: format, refused: refused
|
|
149
|
+
def render_check(results:, format:, refused: [], left_bare: [])
|
|
150
|
+
WriteReporter.new(out: @out).render_check(results: results, format: format, refused: refused,
|
|
151
|
+
left_bare: left_bare)
|
|
146
152
|
end
|
|
147
153
|
|
|
148
154
|
def self.refusal_lines(refused)
|