rigortype 0.4.6 → 0.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/data/gem_overlay/activesupport/core_ext.rbs +16 -13
- 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 +5 -2
- data/docs/handbook/11-sig-gen.md +50 -3
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/manual/02-cli-reference.md +152 -26
- data/docs/manual/03-configuration.md +36 -3
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/09-editor-integration.md +13 -2
- data/docs/manual/10-mcp-server.md +38 -2
- data/docs/manual/12-caching.md +4 -3
- data/docs/manual/plugins/rigor-actionpack.md +19 -11
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +20 -1
- data/lib/rigor/analysis/baseline.rb +24 -27
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +4 -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 +3 -2
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +0 -1
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +1 -1
- data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +3 -3
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
- 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 +1 -1
- data/lib/rigor/analysis/check_rules.rb +124 -44
- data/lib/rigor/analysis/crash_signature.rb +2 -2
- data/lib/rigor/analysis/dependency_recorder.rb +4 -4
- 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_run_slot/write_guard.rb +2 -2
- data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
- data/lib/rigor/analysis/incremental_session.rb +27 -16
- data/lib/rigor/analysis/rule_catalog.rb +2 -2
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +2 -2
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -1
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +193 -93
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +1 -1
- data/lib/rigor/analysis/runner/pool_coordinator.rb +147 -323
- data/lib/rigor/analysis/runner/project_pre_passes.rb +3 -3
- 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 +77 -71
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +1 -1
- 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 +24 -31
- data/lib/rigor/bleeding_edge.rb +3 -6
- 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/incremental_snapshot.rb +31 -12
- 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 +54 -3
- data/lib/rigor/cache/rbs_environment.rb +4 -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 +145 -55
- data/lib/rigor/cli/catalogue.rb +256 -0
- data/lib/rigor/cli/check_command.rb +108 -102
- 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 +18 -17
- 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 +12 -8
- 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 +84 -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 +1 -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 +209 -54
- data/lib/rigor/config_audit.rb +1 -1
- data/lib/rigor/configuration/severity_profile.rb +5 -5
- data/lib/rigor/configuration.rb +72 -30
- 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 +5 -9
- data/lib/rigor/environment/class_registry.rb +3 -3
- data/lib/rigor/environment/core_root.rb +77 -0
- data/lib/rigor/environment/default_libraries.rb +1 -1
- data/lib/rigor/environment/deferred_signature_plan.rb +283 -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/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_hierarchy.rb +6 -15
- data/lib/rigor/environment/rbs_loader.rb +480 -67
- 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 +41 -10
- data/lib/rigor/flow_contribution/merger.rb +4 -4
- data/lib/rigor/inference/acceptance.rb +93 -32
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/block_run_count.rb +178 -0
- data/lib/rigor/inference/budget_trace.rb +8 -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/construction_escape.rb +217 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
- 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 +1 -1
- data/lib/rigor/inference/def_return_typer.rb +46 -4
- data/lib/rigor/inference/definer_resolution.rb +4 -2
- data/lib/rigor/inference/definers_index.rb +106 -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/expression_typer.rb +116 -49
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/global_write_census.rb +76 -2
- 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/indexed_narrowing.rb +4 -7
- data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
- data/lib/rigor/inference/ivar_writer_census.rb +51 -0
- data/lib/rigor/inference/key_presence_guard.rb +2 -1
- data/lib/rigor/inference/loop_value_record.rb +8 -4
- data/lib/rigor/inference/match_rebinding/frame.rb +46 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +45 -0
- data/lib/rigor/inference/match_rebinding.rb +11 -1
- data/lib/rigor/inference/memo_audit.rb +55 -0
- 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 +1 -1
- 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/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 +39 -27
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +203 -17
- 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 +2 -2
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +9 -7
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +83 -20
- 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/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 +64 -14
- data/lib/rigor/inference/module_function_state.rb +11 -1
- data/lib/rigor/inference/multi_target_binder.rb +1 -1
- data/lib/rigor/inference/mutation_rejoin.rb +240 -4
- data/lib/rigor/inference/mutation_widening.rb +157 -27
- data/lib/rigor/inference/narrowing.rb +47 -33
- data/lib/rigor/inference/optimistic_origin.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/project_method_ownership.rb +6 -3
- data/lib/rigor/inference/project_yielder.rb +11 -17
- data/lib/rigor/inference/receiver_alias.rb +20 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +3 -1
- data/lib/rigor/inference/refined_dispatch/own_method.rb +2 -1
- data/lib/rigor/inference/remover_holder_scan.rb +233 -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/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +58 -15
- 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 +135 -0
- data/lib/rigor/inference/scope_indexer.rb +137 -46
- data/lib/rigor/inference/self_call_ivar_writes.rb +84 -12
- data/lib/rigor/inference/set_mutation.rb +189 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +1 -2
- data/lib/rigor/inference/statement_evaluator.rb +2328 -138
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_scanner.rb +5 -6
- data/lib/rigor/inference/toplevel_def_census.rb +59 -0
- data/lib/rigor/inference/unknown_store_widening.rb +103 -4
- data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -1
- 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/plugins.rb +1 -1
- data/lib/rigor/mcp/server.rb +73 -2
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- data/lib/rigor/plugin/bundled_catalog.rb +1 -12
- data/lib/rigor/plugin/io_boundary.rb +1 -1
- 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 +9 -13
- data/lib/rigor/plugin/registry.rb +3 -3
- 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 +2 -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/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 +5 -3
- data/lib/rigor/rooted_glob.rb +15 -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 +19 -6
- data/lib/rigor/scope/resolution_chain.rb +3 -1
- data/lib/rigor/scope.rb +365 -31
- data/lib/rigor/sig_gen/classification.rb +4 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
- data/lib/rigor/sig_gen/generator.rb +371 -58
- 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 +23 -4
- data/lib/rigor/sig_gen/observation_collector.rb +3 -5
- 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/renderer.rb +15 -9
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +15 -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 +217 -37
- 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 +9 -13
- data/lib/rigor/type/constant.rb +4 -5
- data/lib/rigor/type/hash_shape.rb +21 -40
- data/lib/rigor/type/union.rb +1 -1
- 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/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 +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +66 -19
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -3
- 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 +2 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +0 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +5 -4
- 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 +7 -12
- 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 +19 -6
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +26 -13
- 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 +4 -5
- 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-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 +2 -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 +1 -3
- 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 +1 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +1 -1
- 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 +1 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +17 -21
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +3 -2
- 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/fact_store.rbs +1 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
- 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/inference.rbs +4 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +1 -0
- 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/io_boundary.rbs +2 -1
- data/sig/rigor/plugin/manifest.rbs +9 -9
- data/sig/rigor/plugin/protocol_contract.rbs +3 -7
- data/sig/rigor/scope.rbs +23 -3
- 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/03-baseline-and-bugs.md +12 -1
- metadata +36 -3
|
@@ -178,7 +178,7 @@ module Rigor
|
|
|
178
178
|
|
|
179
179
|
# Resolves a user-supplied rule token (`undefined-method`, `call.undefined-method`, or the family wildcard
|
|
180
180
|
# `call`) to the set of canonical rule identifiers it disables. Returns `nil` for `"all"` (the existing
|
|
181
|
-
# wildcard meaning "every rule"),
|
|
181
|
+
# wildcard meaning "every rule"), and an empty array for an unknown token.
|
|
182
182
|
def self.resolve_rule_token(token)
|
|
183
183
|
return nil if token == "all"
|
|
184
184
|
return [LEGACY_RULE_ALIASES.fetch(token)] if LEGACY_RULE_ALIASES.key?(token)
|
|
@@ -10,7 +10,7 @@ module Rigor
|
|
|
10
10
|
module CheckRules
|
|
11
11
|
# ADR-53 Track B — the engine-owned AST walk that hosts CheckRules' per-node collectors, so a file is
|
|
12
12
|
# traversed once for all of them instead of once per collector (the ADR-52 WD4 model applied to the
|
|
13
|
-
# built-in rules;
|
|
13
|
+
# built-in rules; {Plugin::NodeRuleWalk} drives the same collectors, ADR-53 B4).
|
|
14
14
|
#
|
|
15
15
|
# The walk is a single visit-before-descend DFS over `compact_child_nodes`. It threads the union of the
|
|
16
16
|
# context the hosted collectors need — the per-collector hand-rolled walks each tracked some subset of
|
|
@@ -146,12 +146,14 @@ module Rigor
|
|
|
146
146
|
qualified_prefix = extend_prefix(node, context.qualified_prefix)
|
|
147
147
|
detached_ivar_facet = context.detached_ivar_facet || detached_ivar_facet?(node)
|
|
148
148
|
|
|
149
|
-
Context
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
149
|
+
# Nearly every node leaves all four fields as they were, and a {Context} is immutable, so the parent's
|
|
150
|
+
# is the child's. This runs once per node and was a top allocation site (a Data per node).
|
|
151
|
+
if in_loop_or_block == context.in_loop_or_block && inside_def == context.inside_def &&
|
|
152
|
+
qualified_prefix.equal?(context.qualified_prefix) && detached_ivar_facet == context.detached_ivar_facet
|
|
153
|
+
return context
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
Context.new(in_loop_or_block, qualified_prefix, inside_def, detached_ivar_facet)
|
|
155
157
|
end
|
|
156
158
|
|
|
157
159
|
# True for a node whose descendants write ivars to a store the enclosing class name does not name:
|
|
@@ -123,10 +123,9 @@ module Rigor
|
|
|
123
123
|
@scope_index[node] || @scope_index[node.statements] || @scope_index[node.rescue_clause]
|
|
124
124
|
end
|
|
125
125
|
|
|
126
|
-
# Resolves one clause to `{clause:, names:
|
|
127
|
-
#
|
|
128
|
-
#
|
|
129
|
-
# `StandardError`.
|
|
126
|
+
# Resolves one clause to `{clause:, names:}`, where `names` are the certified exception CLASS names it
|
|
127
|
+
# rescues, or nil when the clause is opaque (any entry unresolved / dynamic / splat / module — the
|
|
128
|
+
# comparisons involving it stay silent). A bare `rescue` names the implicit `StandardError`.
|
|
130
129
|
def clause_info(clause, prefix, scope)
|
|
131
130
|
exceptions = clause.exceptions
|
|
132
131
|
if exceptions.empty?
|
|
@@ -9,9 +9,9 @@ module Rigor
|
|
|
9
9
|
# the envelope `global.write-type-mismatch` and `global.readonly-write` judge a write against.
|
|
10
10
|
#
|
|
11
11
|
# The setter is the authority, not the RBS declaration of the global. Where the two differ, the setter wins:
|
|
12
|
-
# `$;` is declared
|
|
13
|
-
# declared
|
|
14
|
-
# that enforces it and was confirmed on Ruby 4.0.5, whose error the comment quotes.
|
|
12
|
+
# `$;` is declared as a Regexp, a String or nil, but its setter also takes any object with `to_str`, and
|
|
13
|
+
# `$stdout` is declared as an IO, but its setter takes anything that responds to `write`. Each entry names
|
|
14
|
+
# the CRuby function that enforces it and was confirmed on Ruby 4.0.5, whose error the comment quotes.
|
|
15
15
|
#
|
|
16
16
|
# A special whose setter accepts every value is absent, so a write to it is never checked: `$stdin`
|
|
17
17
|
# (ADR-117 WD2), `$_`, `$VERBOSE` / `$-v` / `$-w`, `$DEBUG` / `$-d` and `$=`. So is `$@`, whose setter raises
|
|
@@ -280,8 +280,8 @@ module Rigor
|
|
|
280
280
|
|
|
281
281
|
# The trailing `else` is dead when every subject value is covered by the `when` clauses — the final
|
|
282
282
|
# falsey scope (recorded on the `else` node) narrows the subject to `bot`. Defensive `else` bodies (a
|
|
283
|
-
# bare `raise` / `fail` / `throw`) are deliberate guards, not removable dead code, so
|
|
284
|
-
# skipped.
|
|
283
|
+
# bare `raise` / `fail` / `throw` / `abort` / `exit`) are deliberate guards, not removable dead code, so
|
|
284
|
+
# they are skipped.
|
|
285
285
|
def collect_else(else_clause, subject_name, keyword)
|
|
286
286
|
return if else_clause.nil? || else_clause.statements.nil?
|
|
287
287
|
return if defensive_else?(else_clause)
|
|
@@ -29,7 +29,7 @@ module Rigor
|
|
|
29
29
|
class VoidValueUseCollector
|
|
30
30
|
# The assignment forms whose `value` slot is a value position. `MultiWriteNode` (destructuring) is
|
|
31
31
|
# intentionally excluded — its `value` is a container the void call would have to be spread through,
|
|
32
|
-
# not a direct use, so it is
|
|
32
|
+
# not a direct use, so it is outside the FP-narrow scope.
|
|
33
33
|
WRITE_NODE_CLASSES = [
|
|
34
34
|
Prism::LocalVariableWriteNode, Prism::InstanceVariableWriteNode,
|
|
35
35
|
Prism::ClassVariableWriteNode, Prism::GlobalVariableWriteNode,
|
|
@@ -33,6 +33,7 @@ require_relative "check_rules/rebinding_block_callees"
|
|
|
33
33
|
require_relative "check_rules/rbs_receiver_surface"
|
|
34
34
|
require_relative "check_rules/special_global_setters"
|
|
35
35
|
require_relative "check_rules/ruby_deprecations"
|
|
36
|
+
require_relative "../run_scoped_slots"
|
|
36
37
|
|
|
37
38
|
module Rigor
|
|
38
39
|
module Analysis
|
|
@@ -53,7 +54,7 @@ module Rigor
|
|
|
53
54
|
#
|
|
54
55
|
# - implicit-self calls (no `node.receiver`) — too noisy
|
|
55
56
|
# without per-method RBS for every helper in the class.
|
|
56
|
-
# - dynamic / unknown receivers (`Dynamic[T]`, `Top
|
|
57
|
+
# - dynamic / unknown receivers (`Dynamic[T]`, `Top`)
|
|
57
58
|
# — by definition we cannot enumerate the method set.
|
|
58
59
|
# - shape carriers: `Tuple` → "Array", `HashShape` → "Hash",
|
|
59
60
|
# `Constant` → the constant's class — `concrete_class_name`
|
|
@@ -86,9 +87,8 @@ module Rigor
|
|
|
86
87
|
|
|
87
88
|
module_function
|
|
88
89
|
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
# have already produced `scope_index` through
|
|
90
|
+
# Returns the diagnostics for `root`'s subtree across the rule
|
|
91
|
+
# catalogue. The caller MUST have already produced `scope_index` through
|
|
92
92
|
# `Rigor::Inference::ScopeIndexer.index(root, default_scope:)`.
|
|
93
93
|
#
|
|
94
94
|
# @param path — used to populate
|
|
@@ -750,7 +750,7 @@ module Rigor
|
|
|
750
750
|
# branch below because a plugin is consulted ONCE for the whole receiver, union included, so the
|
|
751
751
|
# gate belongs where that one consult is rather than duplicated into each shape's path.
|
|
752
752
|
#
|
|
753
|
-
# The union side of that placement
|
|
753
|
+
# The union side of that placement is reachable. The
|
|
754
754
|
# shape needs a non-nil union of at least two RBS-DECLARED classes — an undeclared arm is an
|
|
755
755
|
# ADR-26 open receiver and `union_arm_blocks_undefined_fire?` bails before the method lookup,
|
|
756
756
|
# which is why the earlier probes read silent and were misread as the gate being unreachable.
|
|
@@ -797,13 +797,6 @@ module Rigor
|
|
|
797
797
|
return union_undefined_method_diagnostic(path, call_node, receiver_type, scope, lexical_sites)
|
|
798
798
|
end
|
|
799
799
|
|
|
800
|
-
# ADR-26 — a plugin may declare a class "open": one
|
|
801
|
-
# known to respond beyond its RBS-declared method
|
|
802
|
-
# surface (e.g. `ActiveRecord::Relation`, which
|
|
803
|
-
# delegates an unbounded set of user-defined scopes to
|
|
804
|
-
# its model). Flagging an undefined method on a class
|
|
805
|
-
# with an open dynamic surface is unsound, so the rule
|
|
806
|
-
# skips it.
|
|
807
800
|
# An unbounded receiver surface: either a plugin-declared
|
|
808
801
|
# open receiver (ADR-26 — e.g. `ActiveRecord::Relation`), or
|
|
809
802
|
# a type Rigor synthesized (a missing-namespace module / a
|
|
@@ -839,6 +832,7 @@ module Rigor
|
|
|
839
832
|
return nil if method_def
|
|
840
833
|
|
|
841
834
|
return nil if last_resort_surface_answers?(receiver_type, class_name, call_node, scope, kind, lexical_sites)
|
|
835
|
+
return nil if open_project_singleton?(receiver_type, class_name, call_node, scope)
|
|
842
836
|
return nil if subclass_instance_may_answer?(rendered_receiver, class_name, call_node.name, scope,
|
|
843
837
|
call_node.receiver)
|
|
844
838
|
|
|
@@ -1342,14 +1336,14 @@ module Rigor
|
|
|
1342
1336
|
# (`METACLASS_ARMS`, "a `plugin_class : Class` really holds a `Plugin` subclass with `.manifest`");
|
|
1343
1337
|
# the scalar rule enumerated `Module`'s own RBS instead and reported them.
|
|
1344
1338
|
#
|
|
1345
|
-
# Kept beside `unbounded_receiver_surface?` rather than folded into it: that predicate has
|
|
1346
|
-
#
|
|
1339
|
+
# Kept beside `unbounded_receiver_surface?` rather than folded into it: that predicate has other
|
|
1340
|
+
# callers, and #742 had no evidence that any of them spoke on these receivers.
|
|
1347
1341
|
#
|
|
1348
1342
|
# Issue #879 supplied it for exactly one — `call.wrong-arity`, which decided `v.hexdigest(1, 2, 3)`
|
|
1349
1343
|
# against `Digest::Instance`'s declaration on the same run where `v.no_such_method_zzz` was declined
|
|
1350
1344
|
# as unenumerable. One receiver, one run, two answers about whether that surface can be read. The
|
|
1351
1345
|
# reporter could make no other caller speak: `raise` on a module- or `Class`-typed operand is silent,
|
|
1352
|
-
# and `Class`-typed `.new` with a wrong arity is silent too, so the other
|
|
1346
|
+
# and `Class`-typed `.new` with a wrong arity is silent too, so the other callers stay on
|
|
1353
1347
|
# `unbounded_receiver_surface?` until something demonstrates otherwise.
|
|
1354
1348
|
#
|
|
1355
1349
|
# Arity takes {#unenumerable_receiver_shape?}, not this whole predicate: ADR-26's open receivers are
|
|
@@ -1451,14 +1445,16 @@ module Rigor
|
|
|
1451
1445
|
end
|
|
1452
1446
|
|
|
1453
1447
|
# ADR-67 WD6b — the argument-position analogue: true when `arg` is rooted at an inferred-parameter
|
|
1454
|
-
# local. The argument-type-mismatch rule declines on it for the same lower-bound reason.
|
|
1448
|
+
# local. The argument-type-mismatch rule declines on it for the same lower-bound reason. `scope` is the
|
|
1449
|
+
# call's, and the receiver and any earlier argument run before `arg` and may rebind what a conditional's
|
|
1450
|
+
# predicate in it reads (`m((c = false; 1), c ? param : "x")`), so a predicate there is never folded.
|
|
1455
1451
|
def inferred_param_argument?(arg, scope)
|
|
1456
|
-
inferred_param_rooted?(arg, scope)
|
|
1452
|
+
inferred_param_rooted?(arg, scope, fold: false)
|
|
1457
1453
|
end
|
|
1458
1454
|
|
|
1459
1455
|
# ADR-67 WD6b — see {CheckRules::InferredParamGuard} for the shared root-walk contract.
|
|
1460
|
-
def inferred_param_rooted?(node, scope)
|
|
1461
|
-
InferredParamGuard.rooted?(node, scope)
|
|
1456
|
+
def inferred_param_rooted?(node, scope, fold: true)
|
|
1457
|
+
InferredParamGuard.rooted?(node, scope, fold: fold)
|
|
1462
1458
|
end
|
|
1463
1459
|
|
|
1464
1460
|
def constant_class_name(value)
|
|
@@ -1530,6 +1526,97 @@ module Rigor
|
|
|
1530
1526
|
ruby_library_reopen?(class_name, scope)
|
|
1531
1527
|
end
|
|
1532
1528
|
|
|
1529
|
+
# Issue #2438 — a class-method call on a class the project's own signatures name but whose source leaves
|
|
1530
|
+
# the singleton surface open. Before `rigor sig-gen --write` such a class was source-only and an
|
|
1531
|
+
# unresolved call on it was silent; the sidecar must not turn the same call into a finding. Two shapes:
|
|
1532
|
+
#
|
|
1533
|
+
# - `C.instance` where `C`, or a project class up its superclass chain, `include`s the stdlib `Singleton`:
|
|
1534
|
+
# the answer is `SingletonMixinDispatch.answers?`, the predicate the typer itself uses, so the rule never
|
|
1535
|
+
# reports a call the engine typed. The class's other members are irrelevant: the mixin's hook defines
|
|
1536
|
+
# `instance`.
|
|
1537
|
+
# - every project declaration of `C` is member-less (`RbsLoader#memberless_project_class?`) AND the source
|
|
1538
|
+
# leaves `C` open ({#open_source_class?}). A member-less declaration does not describe the class's
|
|
1539
|
+
# surface, but a class whose whole definition Rigor can see in `paths:` is still closed, so a typo on it
|
|
1540
|
+
# keeps firing. Instance-side calls keep being checked; declaring any member re-closes the class.
|
|
1541
|
+
def open_project_singleton?(receiver_type, class_name, call_node, scope)
|
|
1542
|
+
return false unless receiver_type.is_a?(Type::Singleton)
|
|
1543
|
+
return true if Inference::MethodDispatcher::SingletonMixinDispatch.answers?(
|
|
1544
|
+
scope, receiver_type, call_node.name, call_node.arguments.nil?
|
|
1545
|
+
)
|
|
1546
|
+
|
|
1547
|
+
loader = scope.environment&.rbs_loader
|
|
1548
|
+
return false if loader.nil? || !loader.respond_to?(:memberless_project_class?)
|
|
1549
|
+
|
|
1550
|
+
loader.memberless_project_class?(class_name) && open_source_class?(class_name, scope, loader)
|
|
1551
|
+
end
|
|
1552
|
+
|
|
1553
|
+
OPENING_HOOKS = %i[inherited included extended prepended method_missing respond_to_missing?].freeze
|
|
1554
|
+
private_constant :OPENING_HOOKS
|
|
1555
|
+
|
|
1556
|
+
CLOSED_ROOTS = Set["Object", "BasicObject"].freeze
|
|
1557
|
+
private_constant :CLOSED_ROOTS
|
|
1558
|
+
|
|
1559
|
+
# Issue #2438 — whether a class's singleton surface may hold methods its source in `paths:` does not show,
|
|
1560
|
+
# read off `Scope::ResolutionChain` (`:constants` flavor, so a project module that holds only constants is
|
|
1561
|
+
# still a project entry). Open when the class, or a project class up its superclass chain:
|
|
1562
|
+
#
|
|
1563
|
+
# 1. `extend`s a module (any singleton-side entry beyond the classes' own singletons), or records an
|
|
1564
|
+
# `inherited` / `included` / `extended` / `prepended` hook or `method_missing` / `respond_to_missing?`.
|
|
1565
|
+
# A plain `include` or `prepend` adds instance methods only, so it leaves the singleton side closed;
|
|
1566
|
+
# 2. has no `class` / `module` body of its own inside `paths:` (`class Ext::Foo` names `Ext` without one);
|
|
1567
|
+
# 3. or the chain reaches a class the project does not define, other than `Object` / `BasicObject`: an
|
|
1568
|
+
# external superclass (`ApplicationRecord < ActiveRecord::Base`, `MyError < StandardError`), a
|
|
1569
|
+
# superclass written as an expression, or — where the source writes no superclass for the last class —
|
|
1570
|
+
# the superclass its member-less declaration writes, unless that is a project class.
|
|
1571
|
+
#
|
|
1572
|
+
# A definition or reopening in a file outside `paths:` cannot be seen, so a class whose body is inside
|
|
1573
|
+
# `paths:` and whose methods live only outside it counts as closed.
|
|
1574
|
+
def open_source_class?(class_name, scope, loader)
|
|
1575
|
+
name = class_name.to_s.delete_prefix("::")
|
|
1576
|
+
instance = Scope::ResolutionChain.for(scope, name, :instance, :constants)
|
|
1577
|
+
singleton = Scope::ResolutionChain.for(scope, name, :singleton, :constants)
|
|
1578
|
+
[instance, singleton].each { |chain| chain.record(scope) }
|
|
1579
|
+
classes = instance.level_classes.compact
|
|
1580
|
+
return true if classes.empty? || instance.truncated?
|
|
1581
|
+
|
|
1582
|
+
sources = scope.discovered_class_sources
|
|
1583
|
+
return true if classes.any? { |klass| (sources[klass] || []).empty? || opening_hook?(klass, scope) }
|
|
1584
|
+
return true if opening_entry?(instance, classes, mixins: false) ||
|
|
1585
|
+
opening_entry?(singleton, classes, mixins: true)
|
|
1586
|
+
|
|
1587
|
+
open_tail?(instance, classes.last, scope, loader)
|
|
1588
|
+
end
|
|
1589
|
+
|
|
1590
|
+
# An external superclass other than `Object` / `BasicObject` opens either side; a module entry opens the
|
|
1591
|
+
# chain only where `mixins` is set (the singleton side, where it is an `extend`).
|
|
1592
|
+
def opening_entry?(chain, classes, mixins:)
|
|
1593
|
+
chain.entries.any? do |entry|
|
|
1594
|
+
if entry.external? && entry.superclass_edge
|
|
1595
|
+
!CLOSED_ROOTS.include?(entry.raw.to_s.delete_prefix("::"))
|
|
1596
|
+
else
|
|
1597
|
+
mixins && !classes.include?(entry.name)
|
|
1598
|
+
end
|
|
1599
|
+
end
|
|
1600
|
+
end
|
|
1601
|
+
|
|
1602
|
+
def opening_hook?(klass, scope)
|
|
1603
|
+
OPENING_HOOKS.any? do |hook|
|
|
1604
|
+
scope.discovered_method?(klass, hook, :singleton) || scope.discovered_method?(klass, hook, :instance)
|
|
1605
|
+
end
|
|
1606
|
+
end
|
|
1607
|
+
|
|
1608
|
+
# The last class's own superclass, which the chain does not name: an expression the tables cannot resolve,
|
|
1609
|
+
# or one only the member-less declaration writes (the chain ends at the implicit `Object` exactly when
|
|
1610
|
+
# the source writes none).
|
|
1611
|
+
def open_tail?(chain, last, scope, loader)
|
|
1612
|
+
return true if chain.opaque_tail?(scope)
|
|
1613
|
+
|
|
1614
|
+
raw = loader.memberless_project_superclass(last)
|
|
1615
|
+
return false if raw.nil? || CLOSED_ROOTS.include?(raw.delete_prefix("::"))
|
|
1616
|
+
|
|
1617
|
+
!scope.discovered_classes.key?(raw.delete_prefix("::"))
|
|
1618
|
+
end
|
|
1619
|
+
|
|
1533
1620
|
# Issue #2174 — `class Matrix` reopened with one `#:`-annotated method, and no `matrix` signatures
|
|
1534
1621
|
# loaded, gives Matrix a declaration holding only that method ({RbsLoader#ruby_library_reopen?}).
|
|
1535
1622
|
# `Matrix.identity(2)` is correct Ruby, so the class's surface is treated as open: `call.undefined-method`
|
|
@@ -1845,16 +1932,15 @@ module Rigor
|
|
|
1845
1932
|
# Slice 7 phase 11 — wrong-arity diagnostic. Fires when
|
|
1846
1933
|
# an explicit-receiver `Prism::CallNode` resolves to a
|
|
1847
1934
|
# method whose declared overloads do not admit the
|
|
1848
|
-
# supplied positional argument count
|
|
1849
|
-
#
|
|
1850
|
-
#
|
|
1851
|
-
#
|
|
1852
|
-
# arguments
|
|
1853
|
-
#
|
|
1854
|
-
#
|
|
1855
|
-
#
|
|
1856
|
-
#
|
|
1857
|
-
# disqualifies the rule.
|
|
1935
|
+
# supplied positional argument count (across all
|
|
1936
|
+
# overloads, see {#compute_arity_envelope}). One overload
|
|
1937
|
+
# with required keyword arguments or trailing positionals
|
|
1938
|
+
# leaves the whole method unchecked; calls with `*splat`, keyword, or
|
|
1939
|
+
# block-pass arguments are silently skipped to avoid
|
|
1940
|
+
# false positives. The check piggybacks on the same
|
|
1941
|
+
# scope-index lookup used by `undefined_method_diagnostic`;
|
|
1942
|
+
# it returns nil when the call's receiver / RBS coverage /
|
|
1943
|
+
# call shape disqualifies the rule.
|
|
1858
1944
|
# rubocop:disable-next Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
1859
1945
|
def wrong_arity_diagnostic(path, call_node, scope_index, lexical_sites = nil)
|
|
1860
1946
|
return nil if call_node.receiver.nil?
|
|
@@ -2990,8 +3076,7 @@ module Rigor
|
|
|
2990
3076
|
# - Elsif chains (`subsequent` is itself an `IfNode`) ARE flagged — the entire downstream chain is
|
|
2991
3077
|
# unreachable when the outer predicate is a constant literal.
|
|
2992
3078
|
#
|
|
2993
|
-
#
|
|
2994
|
-
# mutation gaps named above are closed.
|
|
3079
|
+
# Inferred-constant predicates are the `flow.always-truthy-condition` rule's ({AlwaysTruthyConditionCollector}).
|
|
2995
3080
|
def unreachable_branch_diagnostic(path, node, scope_index)
|
|
2996
3081
|
scope = scope_index[node]
|
|
2997
3082
|
return nil if scope.nil?
|
|
@@ -3102,7 +3187,7 @@ module Rigor
|
|
|
3102
3187
|
build_visibility_mismatch_diagnostic(path, call_node, "singleton(#{class_name})")
|
|
3103
3188
|
end
|
|
3104
3189
|
|
|
3105
|
-
PRIVATE_NAMES_MEMO = :rigor_check_rules_private_singleton_names
|
|
3190
|
+
PRIVATE_NAMES_MEMO = RunScopedSlots.register(:rigor_check_rules_private_singleton_names)
|
|
3106
3191
|
private_constant :PRIVATE_NAMES_MEMO
|
|
3107
3192
|
|
|
3108
3193
|
# Issue #1984 — the table-only gate the singleton side asks before it reads a chain, as the instance side asks
|
|
@@ -3381,12 +3466,9 @@ module Rigor
|
|
|
3381
3466
|
# - Receiver must be Nominal / Singleton / Constant
|
|
3382
3467
|
# (the same `concrete_class_name` test).
|
|
3383
3468
|
# - Method must be in RBS.
|
|
3384
|
-
# -
|
|
3385
|
-
#
|
|
3386
|
-
#
|
|
3387
|
-
# overload requires the dispatcher's full
|
|
3388
|
-
# acceptance plumbing.
|
|
3389
|
-
# - The selected overload must have NO
|
|
3469
|
+
# - Overloads: a single method type is checked exactly; with several, an argument is a mismatch only
|
|
3470
|
+
# when every overload rejects it ({#multi_overload_argument_mismatch}).
|
|
3471
|
+
# - The overload checked must have NO
|
|
3390
3472
|
# rest_positionals and NO trailing positionals —
|
|
3391
3473
|
# the two shapes under which argument N no longer
|
|
3392
3474
|
# binds to positional parameter N. Keyword
|
|
@@ -4096,16 +4178,14 @@ module Rigor
|
|
|
4096
4178
|
# OWN declared return, and the sibling override rules gate on `defined_on?` — this one did not.
|
|
4097
4179
|
# The inherited-declaration signal belongs to the ADR-35 family, which requires both sides
|
|
4098
4180
|
# authored. Decided in ADR-110 WD3.
|
|
4099
|
-
# - Skips methods whose enclosing
|
|
4100
|
-
# (top-level / module-level methods land outside the rule).
|
|
4181
|
+
# - Skips methods whose enclosing `self` names no class (top-level methods land outside the rule).
|
|
4101
4182
|
# - Skips methods whose body's last expression is absent or types as `Dynamic[top]` (the
|
|
4102
4183
|
# analyzer's fail-soft fallback) — emitting on `Dynamic[top]` would be noise.
|
|
4103
4184
|
# - Compares the inferred body type against the declared return via `accepts?`:
|
|
4104
4185
|
# :yes → silent
|
|
4105
4186
|
# :no → emit at :error (severity_profile may re-stamp; default `balanced` keeps the
|
|
4106
4187
|
# authored severity).
|
|
4107
|
-
# :maybe →
|
|
4108
|
-
# "Severity profile".
|
|
4188
|
+
# :maybe → silent (see {#compare_return}; ADR-8's `strict` promotion is deferred).
|
|
4109
4189
|
def return_type_mismatch_diagnostic(path, def_node, scope_index)
|
|
4110
4190
|
return nil if def_node.body.nil?
|
|
4111
4191
|
|
|
@@ -4241,9 +4321,9 @@ module Rigor
|
|
|
4241
4321
|
# precision improvements.
|
|
4242
4322
|
def compare_return(declared, inferred)
|
|
4243
4323
|
result = declared.accepts(inferred)
|
|
4244
|
-
return
|
|
4324
|
+
return nil unless result.no?
|
|
4245
4325
|
|
|
4246
|
-
|
|
4326
|
+
:error
|
|
4247
4327
|
end
|
|
4248
4328
|
|
|
4249
4329
|
def build_return_type_mismatch_diagnostic(path, def_node, declared, inferred, severity)
|
|
@@ -149,8 +149,8 @@ module Rigor
|
|
|
149
149
|
|
|
150
150
|
# True when `diagnostic` reports a failure inside Rigor that left the run readable but invalid as a
|
|
151
151
|
# measurement of Rigor — see {ANALYZER_DEFECT_RULES}. Orthogonal to {.discards_file_analysis?}: the
|
|
152
|
-
# user-facing tier reads the run; the Rigor-measuring tier should refuse it (the mutation fuzz
|
|
153
|
-
#
|
|
152
|
+
# user-facing tier reads the run; the Rigor-measuring tier should refuse it (the mutation fuzz and the
|
|
153
|
+
# ADR-69 kill oracle both do, #790).
|
|
154
154
|
def analyzer_defect?(diagnostic)
|
|
155
155
|
ANALYZER_DEFECT_RULES.include?(diagnostic.rule)
|
|
156
156
|
end
|
|
@@ -74,8 +74,8 @@ module Rigor
|
|
|
74
74
|
@sources = Set.new
|
|
75
75
|
@missing = Set.new
|
|
76
76
|
# ADR-46 slice 4 — symbol-granularity tracking.
|
|
77
|
-
# `symbol_sources`: source_path →
|
|
78
|
-
# `ancestry_sources`:
|
|
77
|
+
# `symbol_sources`: source_path → the "ClassName#method" names read from it, for method-call deps.
|
|
78
|
+
# `ancestry_sources`: the source paths read for class-ancestry (superclass / include) deps —
|
|
79
79
|
# file-granularity by nature (a superclass edge touches the whole class).
|
|
80
80
|
@symbol_sources = Hash.new { |h, k| h[k] = Set.new }
|
|
81
81
|
@ancestry_sources = Set.new
|
|
@@ -103,8 +103,8 @@ module Rigor
|
|
|
103
103
|
end
|
|
104
104
|
|
|
105
105
|
# Frozen record of one file's cross-file reads.
|
|
106
|
-
# `symbol_sources`: source_path → frozen
|
|
107
|
-
# `ancestry_sources`: frozen
|
|
106
|
+
# `symbol_sources`: source_path → frozen set of "ClassName#method" names (method-call edges).
|
|
107
|
+
# `ancestry_sources`: frozen set of source paths (class-ancestry edges, file-granularity).
|
|
108
108
|
Record = Data.define(:consumer, :sources, :missing, :symbol_sources, :ancestry_sources)
|
|
109
109
|
|
|
110
110
|
# ADR-84 WD2 — a replayable, consumer-independent read-set: `reads` is a frozen Set of frozen
|
|
@@ -25,9 +25,9 @@ module Rigor
|
|
|
25
25
|
@mutex = Mutex.new
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
# @return frozen snapshot of the recorded boundary-cross events. Each entry is a Data
|
|
29
|
-
#
|
|
30
|
-
#
|
|
28
|
+
# @return frozen snapshot of the recorded boundary-cross events. Each entry is a Data with `class_name`,
|
|
29
|
+
# `method_name`, `gem_name`, and `rbs_display` (the authoritative-side type's human-facing form, embedded
|
|
30
|
+
# into the diagnostic message).
|
|
31
31
|
def entries
|
|
32
32
|
@mutex.synchronize { @entries.dup.freeze }
|
|
33
33
|
end
|
|
@@ -50,6 +50,17 @@ module Rigor
|
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
+
# The pool drain ({Rigor::Analysis::WorkerSession#drain_reporters}): the frozen entries.
|
|
54
|
+
def snapshot
|
|
55
|
+
entries
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Replays one worker's {#snapshot}; the record-time dedup collapses every worker's copy. Nil records
|
|
59
|
+
# nothing.
|
|
60
|
+
def absorb(snapshot)
|
|
61
|
+
Array(snapshot).each { |entry| record(**entry.to_h) }
|
|
62
|
+
end
|
|
63
|
+
|
|
53
64
|
private
|
|
54
65
|
|
|
55
66
|
def same_key?(existing, entry)
|
|
@@ -15,10 +15,9 @@ module Rigor
|
|
|
15
15
|
:class_to_gem, :budget_overrun_strategy, :gem_modes, :refinements, :bound_constants,
|
|
16
16
|
:bound_segments
|
|
17
17
|
|
|
18
|
-
# @param method_catalog —
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
# attribution that slice 3's cache descriptor needs lives on `Resolved`, not here.
|
|
18
|
+
# @param method_catalog — the flat `(class_name, method_name) → CatalogEntry` table produced by
|
|
19
|
+
# {Walker.walk}, aggregated across every resolved gem in the run. The Index itself stays gem-agnostic —
|
|
20
|
+
# the per-gem attribution that slice 3's cache descriptor needs lives on `Resolved`, not here.
|
|
22
21
|
# @param budget_exceeded — gem names whose {Walker} run hit the per-gem catalog cap
|
|
23
22
|
# (slice 4). The Runner consumes this list to emit one `dynamic.dependency-source.budget-exceeded`
|
|
24
23
|
# warning per gem.
|
|
@@ -110,10 +109,9 @@ module Rigor
|
|
|
110
109
|
mode_for(class_name) == :full
|
|
111
110
|
end
|
|
112
111
|
|
|
113
|
-
# Looks up the
|
|
114
|
-
#
|
|
115
|
-
#
|
|
116
|
-
# build a `Type::Dynamic` directly from the lookup result.
|
|
112
|
+
# Looks up the `CatalogEntry` (kind, and the heuristic return type) for a `(class_name, method_name)` pair.
|
|
113
|
+
# Returns it when the walker observed a definition under one of the resolved gems' `roots:`, or `nil`
|
|
114
|
+
# otherwise.
|
|
117
115
|
def contribution_for(class_name:, method_name:)
|
|
118
116
|
@method_catalog[[class_name, method_name]]
|
|
119
117
|
end
|
|
@@ -99,10 +99,10 @@ module Rigor
|
|
|
99
99
|
# `Configuration::Dependencies::Entry`).
|
|
100
100
|
# @param budget — per-gem catalog cap (method-definition count). When unset, defaults
|
|
101
101
|
# to `UNBOUNDED` for backwards-compatible test paths.
|
|
102
|
-
# @return frozen wrapper carrying the catalog (`
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
#
|
|
102
|
+
# @return frozen wrapper carrying the catalog (keyed by `[class_name, method_name]`, valued by
|
|
103
|
+
# {CatalogEntry}) and a `truncated?` flag set when the walker stopped harvesting because the budget was
|
|
104
|
+
# reached. Methods of identical name on the same class with different kinds (rare; private API mostly)
|
|
105
|
+
# carry the kind that wins the per-class first walk.
|
|
106
106
|
def walk(gem_dir:, roots:, budget: UNBOUNDED)
|
|
107
107
|
harvest = Harvest.new({}, {}, nil, nil, Set.new, Set.new)
|
|
108
108
|
truncated = false
|
|
@@ -15,22 +15,108 @@ module Rigor
|
|
|
15
15
|
# into the closure. Emptying alone does not exercise that one: the broader key it fires re-checks the reader
|
|
16
16
|
# anyway. Not every finer edge is reached: dropping a body still moves a broader key for some of them. A
|
|
17
17
|
# `visibility:` edge is one (#2161's `module_function :two` to `private :two` swap passes both edits with the
|
|
18
|
-
# edge removed), and only an edit that changes a body while keeping it
|
|
18
|
+
# edge removed), and only an edit that changes a body while keeping it would test it.
|
|
19
|
+
# - `dropped` (#2277) — the file with ONE statement of a `class` / `module` body removed and everything else kept,
|
|
20
|
+
# so the declarations, the other definitions and every broader key stay as they were and a finer edge is the
|
|
21
|
+
# only thing that re-checks a reader. At most {MAX_DROPS} statements per file: the visibility-changing calls
|
|
22
|
+
# (`private`, `module_function`, ... {VISIBILITY_CALLS}) first, a body's own statements before those of the bodies
|
|
23
|
+
# nested in it, since those are what finer edges such as #2161's `visibility:` read; then the other statements,
|
|
24
|
+
# spread evenly over the file. A drop whose remainder does not parse (a heredoc body left behind) gives way to the
|
|
25
|
+
# next candidate. Statements of a `class << self` body are never candidates.
|
|
19
26
|
#
|
|
20
|
-
#
|
|
21
|
-
# (
|
|
27
|
+
# No edit is an addition. An edit identical to the file or to an earlier edit, or a file that does not parse
|
|
28
|
+
# (or, for the skeleton, declares nothing), is not replayed.
|
|
22
29
|
module EditReplay
|
|
23
30
|
Edit = Data.define(:label, :source)
|
|
24
31
|
|
|
32
|
+
# The most body-statement drops replayed per file; each is a recheck plus a full analysis.
|
|
33
|
+
MAX_DROPS = 2
|
|
34
|
+
|
|
35
|
+
VISIBILITY_CALLS = %i[
|
|
36
|
+
private public protected module_function private_class_method public_class_method
|
|
37
|
+
private_constant public_constant
|
|
38
|
+
].freeze
|
|
39
|
+
|
|
25
40
|
module_function
|
|
26
41
|
|
|
27
42
|
# The edits to replay for `source`, in order.
|
|
28
43
|
def edits(source)
|
|
29
44
|
emptied = Edit.new(label: "emptying", source: "")
|
|
30
45
|
reduced = skeleton(source)
|
|
31
|
-
|
|
46
|
+
edits = [emptied]
|
|
47
|
+
edits << Edit.new(label: "reducing to its class/module skeleton", source: reduced) unless reduced.empty?
|
|
48
|
+
edits.concat(drops(source))
|
|
49
|
+
edits.reject { |edit| edit.source == source }.uniq(&:source)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Up to {MAX_DROPS} edits, each `source` less one statement of a class / module body, in source order. A drop that
|
|
53
|
+
# leaves text that does not parse is never returned: the next candidate takes its place.
|
|
54
|
+
def drops(source)
|
|
55
|
+
result = Prism.parse(source)
|
|
56
|
+
return [] unless result.success?
|
|
57
|
+
|
|
58
|
+
picked = []
|
|
59
|
+
preferred(drop_candidates(result.value.statements.body)).each do |node|
|
|
60
|
+
edit = drop_edit(source, node)
|
|
61
|
+
picked << [node, edit] if edit
|
|
62
|
+
break if picked.size == MAX_DROPS
|
|
63
|
+
end
|
|
64
|
+
picked.sort_by { |node, _| node.location.start_offset }.map(&:last)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# `source` less `node`, or nil when what is left does not parse. The range runs to the furthest heredoc body
|
|
68
|
+
# inside the node, which sits after the node's own location.
|
|
69
|
+
def drop_edit(source, node)
|
|
70
|
+
location = node.location
|
|
71
|
+
from, to = whole_lines(source, location.start_offset, [location.end_offset, heredoc_end(node)].max)
|
|
72
|
+
dropped = source.byteslice(0, from) + source.byteslice(to..)
|
|
73
|
+
return nil unless Prism.parse(dropped).success?
|
|
74
|
+
|
|
75
|
+
Edit.new(label: "dropping the statement at line #{location.start_line} of", source: dropped)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# The end offset of the furthest heredoc terminator inside `node` (0 when it holds none).
|
|
79
|
+
def heredoc_end(node)
|
|
80
|
+
own = node.respond_to?(:closing_loc) && node.respond_to?(:opening_loc) &&
|
|
81
|
+
node.opening_loc&.slice&.start_with?("<<")
|
|
82
|
+
ends = node.compact_child_nodes.map { |child| heredoc_end(child) }
|
|
83
|
+
ends << node.closing_loc.end_offset if own && node.closing_loc
|
|
84
|
+
ends.max || 0
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# The byte range of `start...finish` widened to the lines it alone occupies, so no blank line is left behind.
|
|
88
|
+
def whole_lines(source, start, finish)
|
|
89
|
+
head = source.byteslice(0, start)[/(?<=\A|\n)[ \t]*\z/]
|
|
90
|
+
tail = finish.positive? && source.getbyte(finish - 1) == 10 ? "" : source.byteslice(finish..)[/\A[ \t]*\n/]
|
|
91
|
+
head.nil? || tail.nil? ? [start, finish] : [start - head.bytesize, finish + tail.bytesize]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Every statement directly in a `class` / `module` body of `nodes`, nested bodies included, in the order a body's
|
|
95
|
+
# own statements come before those of the bodies nested in it (a nested declaration is itself a candidate,
|
|
96
|
+
# dropped whole). A `class << self` body is never searched: its statements are not candidates.
|
|
97
|
+
def drop_candidates(nodes)
|
|
98
|
+
nodes.flat_map do |node|
|
|
99
|
+
next [] unless (node.is_a?(Prism::ClassNode) || node.is_a?(Prism::ModuleNode)) &&
|
|
100
|
+
node.body.is_a?(Prism::StatementsNode)
|
|
101
|
+
|
|
102
|
+
body = node.body.body
|
|
103
|
+
[*body, *drop_candidates(body)]
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# The candidates in the order to try them: the visibility calls, then the other statements from the evenly spread
|
|
108
|
+
# picks outward, then the rest of the other statements.
|
|
109
|
+
def preferred(candidates)
|
|
110
|
+
visibility, others = candidates.partition { |node| visibility_call?(node) }
|
|
111
|
+
spread = Array.new(others.empty? ? 0 : MAX_DROPS) do |index|
|
|
112
|
+
others[(((index * 2) + 1) * others.size) / (MAX_DROPS * 2)]
|
|
113
|
+
end
|
|
114
|
+
[*visibility, *spread, *others].uniq(&:object_id)
|
|
115
|
+
end
|
|
32
116
|
|
|
33
|
-
|
|
117
|
+
def visibility_call?(node)
|
|
118
|
+
node.is_a?(Prism::CallNode) && VISIBILITY_CALLS.include?(node.name) &&
|
|
119
|
+
(node.receiver.nil? || node.receiver.is_a?(Prism::SelfNode))
|
|
34
120
|
end
|
|
35
121
|
|
|
36
122
|
# `source`'s `class` / `module` declarations, nested, with their bodies dropped; "" when it does not parse or
|
|
@@ -61,7 +147,8 @@ module Rigor
|
|
|
61
147
|
superclass = node.superclass
|
|
62
148
|
superclass ? "class #{node.constant_path.slice} < #{superclass.slice}" : "class #{node.constant_path.slice}"
|
|
63
149
|
end
|
|
64
|
-
private_class_method :declarations, :header_of
|
|
150
|
+
private_class_method :declarations, :header_of, :drop_candidates, :whole_lines, :drop_edit, :heredoc_end,
|
|
151
|
+
:preferred, :visibility_call?
|
|
65
152
|
end
|
|
66
153
|
end
|
|
67
154
|
end
|