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,306 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
require_relative "../type"
|
|
5
|
+
|
|
6
|
+
module Rigor
|
|
7
|
+
module SigGen
|
|
8
|
+
# Issue #3029 — decides which emittable `-> bot` proposals are skipped instead.
|
|
9
|
+
#
|
|
10
|
+
# Its discovery-table reads are recorded in `spec/rigor/declaration_facts/admission_census.yml` as
|
|
11
|
+
# `existence` / `identity` (#3037). The justification belongs here rather than at those rows, because that
|
|
12
|
+
# file is re-dumped from the index and keeps no hand-written reason: a contested slot this guard does not
|
|
13
|
+
# see only means a `bot` proposal is kept, which is what master already does, so the guard never reads a
|
|
14
|
+
# fact the census could let it get wrong in the unsafe direction.
|
|
15
|
+
#
|
|
16
|
+
# A `bot` return is right when it is the method's final behaviour: a `throw`, or a deliberate error nothing
|
|
17
|
+
# overrides. It is wrong when the raising body is a base the program replaces. Every override returns a value,
|
|
18
|
+
# so a `bot` declaration breaks Liskov compatibility with each of them, and a collection author reading the
|
|
19
|
+
# proposal cannot see an override outside the analysed code. Three readings skip a `bot` proposal, checked in
|
|
20
|
+
# this order:
|
|
21
|
+
#
|
|
22
|
+
# - `:overridden_bot` — the method is overridden somewhere in the analysed code, by a `def` or a definer the
|
|
23
|
+
# methods table records (`define_method`): by a subclass, signed or not, by a class that includes the method's
|
|
24
|
+
# module, by a module included later or prepended, or, for a module method, by an extending class's own
|
|
25
|
+
# singleton method.
|
|
26
|
+
# - `:abstract_method` — the body ends in an abstract-style raise: `NotImplementedError`, an error class named
|
|
27
|
+
# `Abstract…` / `NotImplemented…`, or a message that asks for the method to be implemented, defined or
|
|
28
|
+
# overridden.
|
|
29
|
+
# - `:template_method` — the body does not end in a direct `raise` / `fail` / `throw` / `exit` / `abort` on every
|
|
30
|
+
# branch, and it calls a method that is overridden or abstract in the analysed code, directly or through a
|
|
31
|
+
# chain of self-dispatched calls. Its `bot` may come from the base body of that call.
|
|
32
|
+
#
|
|
33
|
+
# The reading is over the project's discovery tables, by name. The ancestor names are not resolved precisely: a
|
|
34
|
+
# `superclass`, `include`, `prepend` or `extend` token stands for every class its lexical nesting could mean,
|
|
35
|
+
# and a call matches any method of its name whatever the receiver. Both over-approximations only skip more `bot`
|
|
36
|
+
# proposals, which is the safe direction for a proposal (ADR-5).
|
|
37
|
+
class BotProposalGuard
|
|
38
|
+
TERMINATORS = %i[raise fail throw exit exit! abort].freeze
|
|
39
|
+
RAISERS = %i[raise fail].freeze
|
|
40
|
+
ABSTRACT_ERROR = /NotImplemented|\AAbstract/
|
|
41
|
+
# "not implemented", "please implement", "abstract method", "subclasses must define", "must be overridden", and
|
|
42
|
+
# "define" with a method-name-shaped object: "please define about", "must define #call", "define `rules`". Not
|
|
43
|
+
# "subclass of Foo expected", "cannot override a frozen config", "you must define a block" or "please define
|
|
44
|
+
# FOO env var".
|
|
45
|
+
ABSTRACT_MESSAGE = /
|
|
46
|
+
implement | \babstract\b
|
|
47
|
+
| \bsubclass(?:es)?\s+(?:must|should|need|to|define|implement|override)
|
|
48
|
+
| \b(?:must|should|please|need\s+to|needs\s+to)\s+(?:be\s+)?overrid
|
|
49
|
+
| \b(?:must|should|please|need\s+to|needs\s+to)\s+define\s+
|
|
50
|
+
(?:[#`'"]\w | (?-i:[a-z_]\w*[?!=]?)(?:\s*[`'"(]|\s*\z|\s+(?:method|in|on)\b))
|
|
51
|
+
/ix
|
|
52
|
+
private_constant :TERMINATORS, :RAISERS, :ABSTRACT_ERROR, :ABSTRACT_MESSAGE
|
|
53
|
+
|
|
54
|
+
# @param tables — the project's discovery tables ({Inference::ProjectSeedTables}).
|
|
55
|
+
def initialize(tables)
|
|
56
|
+
@instance_defs = tables[:discovered_def_nodes] || {}
|
|
57
|
+
@singleton_defs = tables[:discovered_singleton_def_nodes] || {}
|
|
58
|
+
@method_definers = tables[:discovered_methods] || {}
|
|
59
|
+
@superclasses = tables[:discovered_superclasses] || {}
|
|
60
|
+
@includes = tables[:discovered_includes] || {}
|
|
61
|
+
@prepends = tables[:discovered_prepends] || {}
|
|
62
|
+
@extends = tables[:discovered_extends] || {}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# The skip reason for `candidate`, or nil when its proposal stands. Only an emittable `bot` return is read.
|
|
66
|
+
def reason_for(candidate)
|
|
67
|
+
return nil unless candidate.inferred_return.is_a?(Type::Bot)
|
|
68
|
+
return nil if candidate.class_name.nil?
|
|
69
|
+
|
|
70
|
+
kind = candidate.kind == :singleton ? :singleton : :instance
|
|
71
|
+
return :overridden_bot if overridden.include?([candidate.class_name, candidate.method_name, kind])
|
|
72
|
+
|
|
73
|
+
node = defs_for(kind).dig(candidate.class_name, candidate.method_name)
|
|
74
|
+
return nil unless node.is_a?(Prism::DefNode)
|
|
75
|
+
return :abstract_method if abstract_body?(node)
|
|
76
|
+
return :template_method if !ends_in_terminator?(node) && calls_dispatch_open?(node)
|
|
77
|
+
|
|
78
|
+
nil
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def defs_for(kind) = kind == :singleton ? @singleton_defs : @instance_defs
|
|
84
|
+
|
|
85
|
+
# `[owner, method name, kind]` for every definition another one shadows in some class's ancestors, on the
|
|
86
|
+
# instance side and on the singleton side.
|
|
87
|
+
def overridden
|
|
88
|
+
@overridden ||= Set.new.tap do |acc|
|
|
89
|
+
class_names.each do |class_name|
|
|
90
|
+
collect_overridden(instance_chain(class_name), acc)
|
|
91
|
+
collect_overridden(singleton_chain(class_name), acc)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# `chain` is `[owner, kind]` pairs, nearest first. The first owner defining a name wins; every later one is
|
|
97
|
+
# overridden.
|
|
98
|
+
def collect_overridden(chain, acc)
|
|
99
|
+
seen = Set.new
|
|
100
|
+
chain.each do |owner, kind|
|
|
101
|
+
method_names(owner, kind).each do |method_name|
|
|
102
|
+
acc << [owner, method_name, kind] unless seen.add?(method_name)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# The names `owner` defines on `kind`'s side: by `def`, and by the definers the methods table records
|
|
108
|
+
# (`define_method`, `attr_*`, ...).
|
|
109
|
+
def method_names(owner, kind)
|
|
110
|
+
return singleton_method_names(owner) if kind == :singleton
|
|
111
|
+
|
|
112
|
+
own_method_names(owner, :instance, @instance_defs.fetch(owner, {}))
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Discovery copies an extended module's methods into the extender's singleton tables. A copy is the module's
|
|
116
|
+
# method, not the class's own, so it is left out: the def node is the module's own node, and a methods-table
|
|
117
|
+
# entry of a name an extended module defines cannot be told apart from its copy.
|
|
118
|
+
def singleton_method_names(owner)
|
|
119
|
+
modules = mixin_resolutions(@extends, owner)
|
|
120
|
+
copied = modules.flat_map { |mod| method_names(mod, :instance) }.to_set
|
|
121
|
+
defs = @singleton_defs.fetch(owner, {}).reject do |method_name, node|
|
|
122
|
+
modules.any? { |mod| node.equal?(@instance_defs.dig(mod, method_name)) }
|
|
123
|
+
end
|
|
124
|
+
own_method_names(owner, :singleton, defs, except: copied)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def own_method_names(owner, kind, defs, except: Set.new)
|
|
128
|
+
names = defs.keys
|
|
129
|
+
(@method_definers[owner] || {}).each do |method_name, definer_kind|
|
|
130
|
+
next if except.include?(method_name)
|
|
131
|
+
|
|
132
|
+
names << method_name if definer_kind == kind || !%i[instance singleton].include?(definer_kind)
|
|
133
|
+
end
|
|
134
|
+
names.uniq
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def class_names
|
|
138
|
+
@class_names ||= [@instance_defs, @singleton_defs, @method_definers, @superclasses, @includes, @prepends,
|
|
139
|
+
@extends].flat_map(&:keys).uniq
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def instance_chain(class_name) = instance_ancestors(class_name).map { |owner| [owner, :instance] }
|
|
143
|
+
|
|
144
|
+
# The singleton class's ancestors, nearest first: the class's own singleton methods, the instance methods of
|
|
145
|
+
# the modules it `extend`s, then its superclass's singleton chain.
|
|
146
|
+
def singleton_chain(class_name, visiting = Set.new)
|
|
147
|
+
return [] unless visiting.add?(class_name)
|
|
148
|
+
|
|
149
|
+
extended = mixin_resolutions(@extends, class_name).flat_map { |mod| instance_chain(mod) }
|
|
150
|
+
own = [[class_name, :singleton]] + extended
|
|
151
|
+
(own + superclass_resolutions(class_name).flat_map { |parent| singleton_chain(parent, visiting) }).uniq
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# The class's ancestors, nearest first: its prepended modules, itself, its included modules, then its
|
|
155
|
+
# superclass's. The discovery tables keep each mixin list last-mixed-in first, as Ruby's ancestors do.
|
|
156
|
+
def instance_ancestors(class_name, visiting = Set.new)
|
|
157
|
+
@instance_ancestors ||= {}
|
|
158
|
+
return @instance_ancestors[class_name] if @instance_ancestors.key?(class_name)
|
|
159
|
+
return [] unless visiting.add?(class_name)
|
|
160
|
+
|
|
161
|
+
prepended = mixin_resolutions(@prepends, class_name)
|
|
162
|
+
parents = mixin_resolutions(@includes, class_name) + superclass_resolutions(class_name)
|
|
163
|
+
own = prepended.flat_map { |mod| instance_ancestors(mod, visiting) } + [class_name]
|
|
164
|
+
@instance_ancestors[class_name] =
|
|
165
|
+
(own + parents.flat_map { |parent| instance_ancestors(parent, visiting) }).uniq
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def mixin_resolutions(table, class_name)
|
|
169
|
+
Array(table[class_name]).flat_map { |token| resolutions(token, class_name) }
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def superclass_resolutions(class_name)
|
|
173
|
+
token = @superclasses[class_name]
|
|
174
|
+
token ? resolutions(token, class_name) : []
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Every class a constant token written in `class_name`'s body or header could name: the token under each of
|
|
178
|
+
# the class's enclosing namespaces, innermost first, and at the top level. The class itself is left out.
|
|
179
|
+
def resolutions(token, class_name)
|
|
180
|
+
bare = token.to_s.sub(/\[.*\z/m, "")
|
|
181
|
+
return [bare.delete_prefix("::")] if bare.start_with?("::")
|
|
182
|
+
|
|
183
|
+
segments = class_name.split("::")
|
|
184
|
+
names = segments.size.downto(0).map { |n| (segments.first(n) + [bare]).join("::") }
|
|
185
|
+
names.uniq - [class_name]
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Method names whose base body a call may not run: an overridden or abstract method, and every method that
|
|
189
|
+
# reaches one through self-dispatched calls.
|
|
190
|
+
def dispatch_open_names
|
|
191
|
+
@dispatch_open_names ||= begin
|
|
192
|
+
open = Set.new(overridden.map { |_, method_name, _| method_name })
|
|
193
|
+
each_def { |method_name, node| open << method_name if abstract_body?(node) }
|
|
194
|
+
close_over_self_calls(open)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def close_over_self_calls(open)
|
|
199
|
+
callers = Hash.new { |h, k| h[k] = Set.new }
|
|
200
|
+
each_def { |method_name, node| self_called_names(node).each { |callee| callers[callee] << method_name } }
|
|
201
|
+
queue = open.to_a
|
|
202
|
+
callers.fetch(queue.shift, []).each { |caller| queue << caller if open.add?(caller) } until queue.empty?
|
|
203
|
+
open
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def each_def(&)
|
|
207
|
+
[@instance_defs, @singleton_defs].each do |defs|
|
|
208
|
+
defs.each_value do |by_name|
|
|
209
|
+
by_name.each { |method_name, node| yield(method_name, node) if node.is_a?(Prism::DefNode) }
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def statements(node)
|
|
215
|
+
body = node.body
|
|
216
|
+
case body
|
|
217
|
+
when nil then []
|
|
218
|
+
when Prism::StatementsNode then body.body
|
|
219
|
+
else [body]
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def ends_in_terminator?(node) = terminates?(statements(node).last)
|
|
224
|
+
|
|
225
|
+
# Whether `node` ends in a receiverless terminator on every path through it: the call itself, or an
|
|
226
|
+
# `if` / `unless` / `case` whose every branch, the `else` included, ends in one.
|
|
227
|
+
def terminates?(node)
|
|
228
|
+
case node
|
|
229
|
+
when Prism::CallNode then node.receiver.nil? && TERMINATORS.include?(node.name)
|
|
230
|
+
when Prism::StatementsNode then terminates?(node.body.last)
|
|
231
|
+
when Prism::ParenthesesNode then terminates?(node.body)
|
|
232
|
+
when Prism::ElseNode then terminates?(node.statements)
|
|
233
|
+
when Prism::IfNode then terminates?(node.statements) && terminates?(node.subsequent)
|
|
234
|
+
when Prism::UnlessNode then terminates?(node.statements) && terminates?(node.else_clause)
|
|
235
|
+
when Prism::CaseNode then case_terminates?(node)
|
|
236
|
+
else false
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def case_terminates?(node)
|
|
241
|
+
node.conditions.all? { |branch| terminates?(branch.statements) } && terminates?(node.else_clause)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def abstract_body?(node)
|
|
245
|
+
last = statements(node).last
|
|
246
|
+
return false unless last.is_a?(Prism::CallNode) && last.receiver.nil? && RAISERS.include?(last.name)
|
|
247
|
+
|
|
248
|
+
args = last.arguments&.arguments || []
|
|
249
|
+
abstract_raise_arguments?(args)
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# `raise NotImplementedError`, `raise Error, "please implement"`, `raise Error.new("subclasses override")`,
|
|
253
|
+
# `raise "abstract"`.
|
|
254
|
+
def abstract_raise_arguments?(args)
|
|
255
|
+
first = args.first
|
|
256
|
+
if first.is_a?(Prism::CallNode) && first.name == :new
|
|
257
|
+
error = first.receiver
|
|
258
|
+
message = first.arguments&.arguments&.first
|
|
259
|
+
else
|
|
260
|
+
error = first
|
|
261
|
+
message = args[1]
|
|
262
|
+
end
|
|
263
|
+
return abstract_message?(error) unless constant?(error)
|
|
264
|
+
|
|
265
|
+
ABSTRACT_ERROR.match?(constant_name(error)) || abstract_message?(message)
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def constant?(node) = node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode)
|
|
269
|
+
|
|
270
|
+
# The last segment: `Foo::NotImplemented` reads as `NotImplemented`.
|
|
271
|
+
def constant_name(node) = node.name.to_s
|
|
272
|
+
|
|
273
|
+
def abstract_message?(node)
|
|
274
|
+
text =
|
|
275
|
+
case node
|
|
276
|
+
when Prism::StringNode then node.unescaped
|
|
277
|
+
when Prism::InterpolatedStringNode
|
|
278
|
+
node.parts.grep(Prism::StringNode).map(&:unescaped).join
|
|
279
|
+
end
|
|
280
|
+
!text.nil? && ABSTRACT_MESSAGE.match?(text)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def calls_dispatch_open?(node) = calls(node).any? { |call| dispatch_open_names.include?(call.name) }
|
|
284
|
+
|
|
285
|
+
def self_called_names(node)
|
|
286
|
+
calls(node).select { |call| call.receiver.nil? || call.receiver.is_a?(Prism::SelfNode) }.map(&:name)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# The calls in the def's body, not those of a nested `def`, whose body belongs to another method.
|
|
290
|
+
def calls(node)
|
|
291
|
+
@calls ||= {}.compare_by_identity
|
|
292
|
+
@calls[node] ||= begin
|
|
293
|
+
out = []
|
|
294
|
+
stack = node.body ? [node.body] : []
|
|
295
|
+
while (current = stack.pop)
|
|
296
|
+
next if current.is_a?(Prism::DefNode)
|
|
297
|
+
|
|
298
|
+
out << current if current.is_a?(Prism::CallNode)
|
|
299
|
+
stack.concat(current.compact_child_nodes)
|
|
300
|
+
end
|
|
301
|
+
out
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|
|
@@ -48,6 +48,15 @@ module Rigor
|
|
|
48
48
|
# Issue #744 — a project subclass overrides this method and its override is NOT emitted, so the
|
|
49
49
|
# declaration would be inherited by a subclass it does not describe.
|
|
50
50
|
overridden_by_unsigned_subclass: "sig.skipped.overridden-by-unsigned-subclass",
|
|
51
|
+
# #3029 — the inferred return is `bot` and the analysed code overrides the method (a subclass, signed or
|
|
52
|
+
# not, or a class or module that includes it), so `bot` describes only the base body the overrides replace.
|
|
53
|
+
overridden_bot: "sig.skipped.overridden-bot",
|
|
54
|
+
# #3029 — the inferred return is `bot` from an abstract-style raise (`NotImplementedError`, "please
|
|
55
|
+
# implement"), with no override in the analysed code. Implementations may live outside it.
|
|
56
|
+
abstract_method: "sig.skipped.abstract-method",
|
|
57
|
+
# #3029 — the inferred return is `bot` and the body calls an overridden or abstract method, directly or
|
|
58
|
+
# through self-dispatch, so the `bot` may be only that method's base body.
|
|
59
|
+
template_method: "sig.skipped.template-method",
|
|
51
60
|
# ADR-112 WD4 — `sig_gen.inline_declared: skip` is set and the inline reader declares this member, so
|
|
52
61
|
# a `sig/` copy would be a second declaration of it for a Steep that reads the inline annotations too.
|
|
53
62
|
inline_declared: "sig.skipped.inline-declared",
|
|
@@ -55,6 +64,10 @@ module Rigor
|
|
|
55
64
|
# generic T`) and `sig/` does not declare it yet. sig-gen writes no class type parameters, and a header
|
|
56
65
|
# without them fails the class's definition build.
|
|
57
66
|
inline_generic_class: "sig.skipped.inline-generic-class",
|
|
67
|
+
# #2436 — the member's class, or one it is nested in, is declared generic by loaded RBS with a parameter
|
|
68
|
+
# that has a bound or a default, and `sig/` does not declare it. A header copied from that declaration
|
|
69
|
+
# could resolve the bound differently, and one without parameters fails the class's definition build.
|
|
70
|
+
generic_reopening: "sig.skipped.generic-reopening",
|
|
58
71
|
# ADR-112 WD4 — the member is declared inline and in `sig/`, and the two disagree. A refusal, not a
|
|
59
72
|
# skip: `--write` and `--check` exit 1 until a person reconciles them or passes `--overwrite`.
|
|
60
73
|
inline_differs: "sig.skipped.inline-differs",
|
|
@@ -63,7 +76,11 @@ module Rigor
|
|
|
63
76
|
top_level_def: "sig.skipped.top-level-def",
|
|
64
77
|
# #1968 — a bare `public` / `private` / `protected` that may not run (in control flow or a block) sits
|
|
65
78
|
# between a bare `module_function` and the def, so whether the def is a module function is unknown.
|
|
66
|
-
uncertain_module_function: "sig.skipped.uncertain-module-function"
|
|
79
|
+
uncertain_module_function: "sig.skipped.uncertain-module-function",
|
|
80
|
+
# #3035 — the def lives in a file Ruby loads only under a `RUBY_ENGINE` / `RUBY_PLATFORM` /
|
|
81
|
+
# `defined?(…)` require guard, and the proposal would either duplicate a method another file
|
|
82
|
+
# provides or read `bot` off a raising stub the current platform never loads.
|
|
83
|
+
platform_guarded: "sig.skipped.platform-guarded"
|
|
67
84
|
}.freeze
|
|
68
85
|
end
|
|
69
86
|
end
|
|
@@ -82,8 +82,8 @@ module Rigor
|
|
|
82
82
|
#
|
|
83
83
|
# @param declared — whether the method already carries an authored bound of its own
|
|
84
84
|
# ({Effects::EnvelopeIndex}); see gate 0.
|
|
85
|
-
# @return
|
|
86
|
-
#
|
|
85
|
+
# @return the annotation lines to render, then the reason to report. An empty list with a `nil` reason
|
|
86
|
+
# is "nothing to say about this method".
|
|
87
87
|
def decide(entry, envelopes: false, declared: false)
|
|
88
88
|
return [[], :withheld_declared] if declared
|
|
89
89
|
return [[], nil] if entry.nil?
|
|
@@ -133,7 +133,7 @@ module Rigor
|
|
|
133
133
|
|
|
134
134
|
# @param path — the `.rb` file the def came from, for a `match:`-selected
|
|
135
135
|
# `effects.envelopes:` entry.
|
|
136
|
-
# @return
|
|
136
|
+
# @return the annotation lines and the reason, as {EffectAnnotation.decide} gives them.
|
|
137
137
|
def annotate(class_name:, method_name:, kind:, path: nil)
|
|
138
138
|
key = EffectAnnotation.key_for(class_name, method_name, kind)
|
|
139
139
|
return [[], nil] if key.nil?
|