rigortype 0.3.8 → 0.3.9
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 +1 -1
- data/data/capability_roles/capability_roles.rbs +36 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +109 -13
- data/docs/handbook/02-everyday-types.md +17 -14
- data/docs/handbook/03-narrowing.md +37 -4
- data/docs/handbook/05-methods-and-blocks.md +1 -1
- data/docs/handbook/06-classes.md +2 -2
- data/docs/handbook/07-rbs-and-extended.md +14 -1
- data/docs/handbook/11-sig-gen.md +72 -1
- data/docs/handbook/README.md +1 -1
- data/docs/handbook/appendix-elixir.md +2 -2
- data/docs/handbook/appendix-go.md +2 -2
- data/docs/handbook/appendix-java-csharp.md +2 -2
- data/docs/handbook/appendix-mypy.md +2 -2
- data/docs/handbook/appendix-phpstan.md +1 -1
- data/docs/handbook/appendix-rust.md +1 -1
- data/docs/handbook/appendix-type-theory.md +4 -4
- data/docs/handbook/appendix-typescript.md +1 -1
- data/docs/llms.txt +2 -0
- data/docs/manual/02-cli-reference.md +32 -4
- data/docs/manual/03-configuration.md +11 -0
- data/docs/manual/04-diagnostics.md +11 -0
- data/docs/manual/07-plugins.md +15 -5
- data/docs/manual/08-skills.md +23 -2
- data/docs/manual/11-ci.md +9 -0
- data/docs/manual/15-type-protection-coverage.md +8 -0
- data/docs/manual/16-rbs-extended-annotations.md +6 -1
- data/docs/manual/18-removing-dead-code.md +10 -8
- data/docs/manual/plugins/rigor-actionpack.md +18 -0
- data/docs/manual/plugins/rigor-activerecord.md +28 -0
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +32 -2
- data/docs/manual/plugins/rigor-rbs-inline.md +42 -1
- data/lib/rigor/analysis/baseline.rb +2 -2
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
- data/lib/rigor/analysis/check_rules/rule_walk.rb +39 -4
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
- data/lib/rigor/analysis/check_rules.rb +200 -32
- data/lib/rigor/analysis/crash_signature.rb +8 -24
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
- data/lib/rigor/analysis/erb_template_detector.rb +1 -2
- data/lib/rigor/analysis/fact_store.rb +6 -1
- data/lib/rigor/analysis/incremental.rb +16 -0
- data/lib/rigor/analysis/incremental_session.rb +81 -22
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
- data/lib/rigor/analysis/reachability/graph.rb +3 -5
- data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -5
- data/lib/rigor/analysis/reachability/project_files.rb +2 -2
- data/lib/rigor/analysis/reachability/scan.rb +5 -5
- data/lib/rigor/analysis/reachability/scan_cache.rb +2 -2
- data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
- data/lib/rigor/analysis/result.rb +1 -3
- data/lib/rigor/analysis/rule_catalog.rb +4 -1
- data/lib/rigor/analysis/run_cache_key.rb +1 -1
- data/lib/rigor/analysis/run_cache_probe.rb +3 -5
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +177 -26
- data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -10
- data/lib/rigor/analysis/runner/pool_coordinator.rb +256 -88
- data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
- data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
- data/lib/rigor/analysis/runner.rb +133 -35
- data/lib/rigor/analysis/worker_session.rb +9 -10
- data/lib/rigor/bleeding_edge.rb +12 -24
- data/lib/rigor/builtins/hkt_builtins.rb +2 -2
- data/lib/rigor/builtins/imported_refinements.rb +155 -21
- data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
- data/lib/rigor/builtins/regex_refinement.rb +4 -4
- data/lib/rigor/builtins/static_return_refinements.rb +5 -6
- data/lib/rigor/cache/annotation_location.rb +2 -4
- data/lib/rigor/cache/descriptor.rb +58 -20
- data/lib/rigor/cache/engine_source.rb +4 -4
- data/lib/rigor/cache/file_digest.rb +9 -1
- data/lib/rigor/cache/incremental_snapshot.rb +40 -6
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
- data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
- data/lib/rigor/cache/rbs_constant_table.rb +0 -3
- data/lib/rigor/cache/rbs_descriptor.rb +79 -11
- data/lib/rigor/cache/rbs_environment.rb +8 -4
- data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
- data/lib/rigor/cache/store.rb +77 -32
- data/lib/rigor/cli/annotate_command.rb +5 -6
- data/lib/rigor/cli/check_command.rb +57 -10
- data/lib/rigor/cli/check_invocation.rb +6 -11
- data/lib/rigor/cli/check_runner_factory.rb +1 -5
- data/lib/rigor/cli/coverage_command.rb +1 -1
- data/lib/rigor/cli/coverage_mutation.rb +12 -3
- data/lib/rigor/cli/coverage_scan.rb +1 -5
- data/lib/rigor/cli/diff_command.rb +1 -1
- data/lib/rigor/cli/doc_links.rb +3 -3
- data/lib/rigor/cli/docs_command.rb +2 -2
- data/lib/rigor/cli/doctor_command.rb +85 -38
- data/lib/rigor/cli/effects_command.rb +2 -2
- data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
- data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
- data/lib/rigor/cli/explain_command.rb +34 -1
- data/lib/rigor/cli/fused_protection_report.rb +7 -1
- data/lib/rigor/cli/lsp_command.rb +1 -1
- data/lib/rigor/cli/mcp_command.rb +1 -1
- data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
- data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
- data/lib/rigor/cli/mutation_protection_report.rb +7 -1
- data/lib/rigor/cli/plugin_command.rb +1 -1
- data/lib/rigor/cli/plugins_command.rb +2 -1
- data/lib/rigor/cli/prism_colorizer.rb +2 -2
- data/lib/rigor/cli/protection_fork_scan.rb +6 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
- data/lib/rigor/cli/sig_gen_command.rb +2 -2
- data/lib/rigor/cli/skill_command.rb +1 -1
- data/lib/rigor/cli/skill_deep_probe.rb +4 -4
- data/lib/rigor/cli/skill_describe.rb +36 -35
- data/lib/rigor/cli/trace_command.rb +4 -4
- data/lib/rigor/cli/trace_renderer.rb +5 -6
- data/lib/rigor/cli/triage_command.rb +1 -1
- data/lib/rigor/cli/type_of_command.rb +10 -6
- data/lib/rigor/cli/type_scan_command.rb +4 -4
- data/lib/rigor/cli/unused_command.rb +11 -3
- data/lib/rigor/cli/upgrade_command.rb +1 -1
- data/lib/rigor/cli.rb +68 -6
- data/lib/rigor/config_audit.rb +30 -5
- data/lib/rigor/configuration/severity_profile.rb +6 -6
- data/lib/rigor/configuration.rb +43 -4
- data/lib/rigor/effects/attribution.rb +1 -3
- data/lib/rigor/effects/config_envelopes.rb +6 -8
- data/lib/rigor/effects/definition_lines.rb +25 -6
- data/lib/rigor/effects/effect_table.rb +0 -1
- data/lib/rigor/effects/entry_points.rb +0 -2
- data/lib/rigor/effects/envelope_check.rb +8 -8
- data/lib/rigor/effects/envelope_index.rb +5 -8
- data/lib/rigor/effects/framework_units.rb +4 -6
- data/lib/rigor/effects/identity.rb +4 -6
- data/lib/rigor/effects/inline_anchor.rb +7 -7
- data/lib/rigor/effects/label_intent.rb +3 -4
- data/lib/rigor/effects/liskov_check.rb +8 -8
- data/lib/rigor/effects/method_key.rb +1 -1
- data/lib/rigor/effects/plugin_facts.rb +4 -6
- data/lib/rigor/effects/propagator.rb +2 -3
- data/lib/rigor/effects/scanner.rb +1 -1
- data/lib/rigor/effects/signature_sources.rb +3 -5
- data/lib/rigor/effects/snapshot.rb +10 -10
- data/lib/rigor/effects/snapshot_diff.rb +1 -1
- data/lib/rigor/effects/unit_scan.rb +10 -10
- data/lib/rigor/effects/unknown_label_check.rb +3 -8
- data/lib/rigor/effects/unknown_label_report.rb +3 -4
- data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
- data/lib/rigor/environment/class_registry.rb +3 -1
- data/lib/rigor/environment/failure_slot.rb +2 -2
- data/lib/rigor/environment/installed_gem_set.rb +85 -0
- data/lib/rigor/environment/lockfile_resolver.rb +51 -4
- data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
- data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
- data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
- data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
- data/lib/rigor/environment/rbs_loader.rb +388 -59
- data/lib/rigor/environment.rb +105 -20
- data/lib/rigor/flow_contribution/merger.rb +0 -2
- data/lib/rigor/flow_contribution.rb +11 -13
- data/lib/rigor/inference/acceptance.rb +153 -8
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/body_fixpoint.rb +5 -5
- data/lib/rigor/inference/budget_trace.rb +1 -5
- data/lib/rigor/inference/captured_locals.rb +2 -3
- data/lib/rigor/inference/closure_escape_analyzer.rb +2 -4
- data/lib/rigor/inference/coverage_scanner.rb +4 -6
- data/lib/rigor/inference/dynamic_origin.rb +1 -1
- data/lib/rigor/inference/element_read_widening.rb +168 -0
- data/lib/rigor/inference/expression_typer.rb +763 -143
- data/lib/rigor/inference/fork_map.rb +5 -7
- data/lib/rigor/inference/hkt_reducer.rb +2 -3
- data/lib/rigor/inference/hkt_registry.rb +4 -7
- data/lib/rigor/inference/index_write_widening.rb +1 -5
- data/lib/rigor/inference/macro_block_self_type.rb +1 -4
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +2 -9
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +61 -47
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +165 -31
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +4 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +12 -13
- data/lib/rigor/inference/method_parameter_binder.rb +4 -6
- data/lib/rigor/inference/multi_target_binder.rb +1 -3
- data/lib/rigor/inference/mutation_rejoin.rb +168 -0
- data/lib/rigor/inference/mutation_widening.rb +100 -51
- data/lib/rigor/inference/narrowing.rb +299 -46
- data/lib/rigor/inference/optimistic_origin.rb +1 -7
- data/lib/rigor/inference/origin_lookup.rb +2 -3
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +6 -5
- data/lib/rigor/inference/precision_scanner.rb +3 -4
- data/lib/rigor/inference/project_patched_methods.rb +2 -2
- data/lib/rigor/inference/project_patched_scanner.rb +3 -3
- data/lib/rigor/inference/protection_scanner.rb +1 -2
- data/lib/rigor/inference/range_constant.rb +57 -0
- data/lib/rigor/inference/rbs_type_translator.rb +49 -11
- data/lib/rigor/inference/receiver_alias.rb +3 -3
- data/lib/rigor/inference/refinement_mutation.rb +72 -0
- data/lib/rigor/inference/scope_indexer.rb +688 -151
- data/lib/rigor/inference/statement_evaluator.rb +221 -50
- data/lib/rigor/inference/string_mutation.rb +60 -0
- data/lib/rigor/inference/struct_fold_safety.rb +6 -7
- data/lib/rigor/inference/synthetic_method_index.rb +1 -2
- data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
- data/lib/rigor/inference/version_guard.rb +28 -21
- data/lib/rigor/inference/void_origin.rb +3 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -4
- data/lib/rigor/language_server/buffer_table.rb +4 -4
- data/lib/rigor/language_server/completion_provider.rb +1 -1
- data/lib/rigor/language_server/debouncer.rb +1 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +4 -4
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -1
- data/lib/rigor/language_server/hover_provider.rb +1 -1
- data/lib/rigor/language_server/hover_renderer.rb +2 -2
- data/lib/rigor/language_server/incremental_sync.rb +6 -6
- data/lib/rigor/language_server/project_context.rb +2 -5
- data/lib/rigor/language_server/publish_batcher.rb +2 -2
- data/lib/rigor/language_server/selection_range_provider.rb +2 -2
- data/lib/rigor/language_server/server.rb +9 -9
- data/lib/rigor/language_server/signature_help_provider.rb +1 -1
- data/lib/rigor/language_server/uri.rb +1 -1
- data/lib/rigor/plugin/base.rb +90 -21
- data/lib/rigor/plugin/bundled_catalog.rb +167 -0
- data/lib/rigor/plugin/effect_attribution.rb +7 -7
- data/lib/rigor/plugin/effect_entry_points.rb +3 -3
- data/lib/rigor/plugin/fact_store.rb +6 -6
- data/lib/rigor/plugin/io_boundary.rb +93 -8
- data/lib/rigor/plugin/isolation.rb +29 -6
- data/lib/rigor/plugin/loader.rb +31 -12
- data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
- data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
- data/lib/rigor/plugin/manifest.rb +23 -1
- data/lib/rigor/plugin/registry.rb +42 -12
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +12 -1
- data/lib/rigor/plugin/trust_policy.rb +2 -4
- data/lib/rigor/plugin/type_node_resolver.rb +3 -3
- data/lib/rigor/plugin_gap_advisory.rb +96 -0
- data/lib/rigor/project_environment.rb +138 -0
- data/lib/rigor/protection/analysis_guard.rb +93 -14
- data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
- data/lib/rigor/protection/dependency_closure.rb +5 -8
- data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
- data/lib/rigor/protection/discovery_seed.rb +11 -11
- data/lib/rigor/protection/kill_signature.rb +2 -4
- data/lib/rigor/protection/measurement_integrity.rb +1 -3
- data/lib/rigor/protection/mutation_cache.rb +6 -9
- data/lib/rigor/protection/mutation_scanner.rb +12 -15
- data/lib/rigor/protection/mutator.rb +2 -1
- data/lib/rigor/protection/test_suite_oracle.rb +5 -5
- data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/reporter.rb +36 -10
- data/lib/rigor/rbs_extended.rb +61 -21
- data/lib/rigor/reflection/constant_path.rb +126 -0
- data/lib/rigor/reflection.rb +26 -10
- data/lib/rigor/runtime/jit.rb +6 -8
- data/lib/rigor/scope/discovery_index.rb +30 -0
- data/lib/rigor/scope.rb +203 -4
- data/lib/rigor/sig_gen/generator.rb +115 -22
- data/lib/rigor/sig_gen/layout_index.rb +3 -4
- data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
- data/lib/rigor/sig_gen/observation_collector.rb +17 -15
- data/lib/rigor/sig_gen/path_mapper.rb +4 -6
- data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
- data/lib/rigor/sig_gen/renderer.rb +4 -5
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +111 -0
- data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
- data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
- data/lib/rigor/sig_gen/writer.rb +23 -6
- data/lib/rigor/signature_path_audit.rb +153 -6
- data/lib/rigor/source/literals.rb +0 -23
- data/lib/rigor/source/node_children.rb +0 -2
- data/lib/rigor/source/node_locator.rb +5 -11
- data/lib/rigor/source/node_walker.rb +2 -5
- data/lib/rigor/triage/catalogue.rb +1 -3
- data/lib/rigor/triage.rb +3 -5
- data/lib/rigor/type/accepts_result.rb +24 -6
- data/lib/rigor/type/combinator.rb +68 -5
- data/lib/rigor/type/data_class.rb +2 -2
- data/lib/rigor/type/data_instance.rb +4 -4
- data/lib/rigor/type/float_range.rb +128 -0
- data/lib/rigor/type/hash_shape.rb +5 -5
- data/lib/rigor/type/integer_range.rb +13 -8
- data/lib/rigor/type/nominal.rb +8 -2
- data/lib/rigor/type/refined.rb +3 -3
- data/lib/rigor/type/struct_class.rb +3 -3
- data/lib/rigor/type/struct_instance.rb +4 -4
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/generic.rb +1 -1
- data/lib/rigor/type_node/range_literal.rb +25 -0
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node.rb +1 -0
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -0
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +1 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +33 -30
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +28 -2
- data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +1 -0
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +3 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +42 -7
- data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +1 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +119 -14
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +1 -0
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
- data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +6 -3
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +0 -3
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +1 -0
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +1 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +3 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -0
- data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
- data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +128 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
- data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +1 -0
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +5 -5
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
- data/sig/rigor/cache.rbs +2 -3
- data/sig/rigor/environment.rbs +5 -0
- data/sig/rigor/inference.rbs +3 -10
- data/sig/rigor/plugin/io_boundary.rbs +4 -0
- data/sig/rigor/plugin/manifest.rbs +2 -1
- data/sig/rigor/reflection.rbs +3 -1
- data/sig/rigor/scope.rbs +24 -3
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -0
- data/sig/rigor/type.rbs +27 -1
- data/sig/rigor.rbs +1 -0
- data/skills/rigor-ask/SKILL.md +8 -6
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +13 -2
- data/skills/rigor-project-init/SKILL.md +6 -3
- data/skills/rigor-project-init/references/02-configure.md +10 -6
- data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
- data/skills/rigor-type-oracle/SKILL.md +228 -0
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +261 -0
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +128 -0
- data/skills/rigor-unused-adjudicate/SKILL.md +8 -5
- metadata +26 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
require "prism"
|
|
4
4
|
|
|
5
5
|
require_relative "../environment"
|
|
6
|
+
require_relative "../project_environment"
|
|
6
7
|
require_relative "../scope"
|
|
7
8
|
require_relative "../type"
|
|
9
|
+
require_relative "../source/constant_path"
|
|
8
10
|
require_relative "../source/literals"
|
|
9
11
|
require_relative "../source/node_children"
|
|
10
12
|
require_relative "../inference/scope_indexer"
|
|
@@ -18,7 +20,7 @@ module Rigor
|
|
|
18
20
|
# for every `Prism::CallNode` whose receiver types as a `Type::Nominal`. The {Generator} consumes the
|
|
19
21
|
# resulting map to render `--params=observed` RBS:
|
|
20
22
|
#
|
|
21
|
-
# @return
|
|
23
|
+
# @return => Array<Array<Rigor::Type>>}]
|
|
22
24
|
#
|
|
23
25
|
# ADR-5 clause 2 compliance: the observed union is the MOST PERMISSIVE parameter contract the existing
|
|
24
26
|
# callers prove sufficient — by construction it accepts every type any caller has already passed. The
|
|
@@ -32,10 +34,9 @@ module Rigor
|
|
|
32
34
|
# the collector cannot attribute them to a specific class.
|
|
33
35
|
# - Zero-argument calls give no observation; methods are matched by `(class_name, method_name)` only.
|
|
34
36
|
class ObservationCollector # rubocop:disable Metrics/ClassLength
|
|
35
|
-
# @param
|
|
36
|
-
# @param paths [Array<String>] observe paths (files /
|
|
37
|
+
# @param paths — observe paths (files /
|
|
37
38
|
# directories).
|
|
38
|
-
# @param source_paths
|
|
39
|
+
# @param source_paths — source-tree paths
|
|
39
40
|
# (defaults to `configuration.paths`) pre-walked to
|
|
40
41
|
# register every project-defined class so that calls
|
|
41
42
|
# like `Foo.new.bar(x)` in the observe tree resolve
|
|
@@ -51,10 +52,11 @@ module Rigor
|
|
|
51
52
|
def collect
|
|
52
53
|
return {} if @paths.empty?
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
resolved = resolve_paths(@paths)
|
|
56
|
+
environment = build_environment(resolved)
|
|
55
57
|
discovered_classes = preindex_source_classes
|
|
56
58
|
observations = Hash.new { |h, k| h[k] = [] }
|
|
57
|
-
|
|
59
|
+
resolved.each do |path|
|
|
58
60
|
collect_from_file(path, environment, discovered_classes, observations)
|
|
59
61
|
end
|
|
60
62
|
observations.transform_values(&:freeze).freeze
|
|
@@ -62,11 +64,11 @@ module Rigor
|
|
|
62
64
|
|
|
63
65
|
private
|
|
64
66
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
)
|
|
67
|
+
# Issue #821 — same universe as the generator and as `rigor check`; see
|
|
68
|
+
# {Generator#build_environment}. An observation collected against a narrower environment types its
|
|
69
|
+
# argument differently from the run that will consume it.
|
|
70
|
+
def build_environment(source_files = [])
|
|
71
|
+
ProjectEnvironment.build(configuration: @configuration, source_files: source_files)
|
|
70
72
|
end
|
|
71
73
|
|
|
72
74
|
def resolve_paths(args)
|
|
@@ -118,11 +120,11 @@ module Rigor
|
|
|
118
120
|
return unless node.is_a?(Prism::Node)
|
|
119
121
|
|
|
120
122
|
if node.is_a?(Prism::ClassNode) || node.is_a?(Prism::ModuleNode)
|
|
121
|
-
|
|
122
|
-
if
|
|
123
|
-
full =
|
|
123
|
+
child_prefix = Source::ConstantPath.declaration_prefix(prefix, node.constant_path)
|
|
124
|
+
if child_prefix
|
|
125
|
+
full = child_prefix.join("::")
|
|
124
126
|
accumulator[full] = Type::Combinator.singleton_of(full)
|
|
125
|
-
walk_class_decls(node.body,
|
|
127
|
+
walk_class_decls(node.body, child_prefix, accumulator) if node.body
|
|
126
128
|
return
|
|
127
129
|
end
|
|
128
130
|
end
|
|
@@ -21,9 +21,8 @@ module Rigor
|
|
|
21
21
|
# When the source path is not under any configured source root (e.g. files supplied directly on the CLI
|
|
22
22
|
# from outside `lib/`), the full relative path is preserved under the sig root.
|
|
23
23
|
class PathMapper
|
|
24
|
-
# @param
|
|
25
|
-
# @param
|
|
26
|
-
# @param layout_index [LayoutIndex, nil] optional class
|
|
24
|
+
# @param project_root — (defaults to `Dir.pwd`)
|
|
25
|
+
# @param layout_index — optional class
|
|
27
26
|
# → existing sig file index; routes the target to the
|
|
28
27
|
# consolidated file when the class is already declared.
|
|
29
28
|
def initialize(configuration:, project_root: Dir.pwd, layout_index: nil)
|
|
@@ -32,12 +31,11 @@ module Rigor
|
|
|
32
31
|
@layout_index = layout_index
|
|
33
32
|
end
|
|
34
33
|
|
|
35
|
-
# @param
|
|
36
|
-
# @param class_name [String, nil] fully-qualified Ruby
|
|
34
|
+
# @param class_name — fully-qualified Ruby
|
|
37
35
|
# class name. When supplied and matched by the
|
|
38
36
|
# `LayoutIndex`, the consolidated sig file's path is
|
|
39
37
|
# returned instead of the 1:1 mirror.
|
|
40
|
-
# @return
|
|
38
|
+
# @return absolute path of the target `.rbs`
|
|
41
39
|
# file for the candidate.
|
|
42
40
|
def target_for(source_path, class_name: nil)
|
|
43
41
|
existing = existing_target_for(class_name)
|
|
@@ -23,16 +23,16 @@ module Rigor
|
|
|
23
23
|
|
|
24
24
|
module_function
|
|
25
25
|
|
|
26
|
-
# @param line
|
|
27
|
-
# @return
|
|
26
|
+
# @param line — a rendered RBS method line, e.g. `def self.parse: (String) -> Integer`.
|
|
27
|
+
# @return the parse error's first line, or nil when the line is valid.
|
|
28
28
|
def method_line_error(line)
|
|
29
29
|
return nil if line.nil?
|
|
30
30
|
|
|
31
31
|
source_error("class #{PROBE_CLASS}\n #{line}\nend\n")
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
# @param source
|
|
35
|
-
# @return
|
|
34
|
+
# @param source — a complete `.rbs` file's text.
|
|
35
|
+
# @return the parse error's first line, or nil when the file is valid.
|
|
36
36
|
def source_error(source)
|
|
37
37
|
::RBS::Parser.parse_signature(::RBS::Buffer.new(name: "(rigor sig-gen)", content: source))
|
|
38
38
|
nil
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "json"
|
|
4
4
|
|
|
5
5
|
require_relative "classification"
|
|
6
|
+
require_relative "superclass_spelling"
|
|
6
7
|
|
|
7
8
|
module Rigor
|
|
8
9
|
module SigGen
|
|
@@ -21,10 +22,8 @@ module Rigor
|
|
|
21
22
|
@out = out
|
|
22
23
|
end
|
|
23
24
|
|
|
24
|
-
# @param
|
|
25
|
-
# @param
|
|
26
|
-
# @param format [String] "text" or "json"
|
|
27
|
-
# @param selection [Array<Symbol>] subset of
|
|
25
|
+
# @param format — "text" or "json"
|
|
26
|
+
# @param selection — subset of
|
|
28
27
|
# {Classification} constants to include; an empty
|
|
29
28
|
# array means "all emittable classifications".
|
|
30
29
|
def render(candidates:, mode:, format:, selection:)
|
|
@@ -91,7 +90,7 @@ module Rigor
|
|
|
91
90
|
return "module #{class_name}" if candidate.namespace_kinds[class_name] == :module
|
|
92
91
|
|
|
93
92
|
superclass = candidate.class_superclasses[class_name]
|
|
94
|
-
superclass ? "class #{class_name} < #{superclass}" : "class #{class_name}"
|
|
93
|
+
superclass ? "class #{class_name} < #{SuperclassSpelling.absolute(superclass)}" : "class #{class_name}"
|
|
95
94
|
end
|
|
96
95
|
|
|
97
96
|
def render_diff(candidates)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "classification"
|
|
4
|
+
|
|
5
|
+
module Rigor
|
|
6
|
+
module SigGen
|
|
7
|
+
# Answers for the `sig.skipped.*` identifiers `rigor sig-gen` prints when it declines to emit a
|
|
8
|
+
# signature for a method.
|
|
9
|
+
#
|
|
10
|
+
# ADR-108 WD4 — a skip id is an ANSWER ("Rigor cannot prove a type here"), and the command that exists
|
|
11
|
+
# to answer identifiers must be able to answer it. `rigor explain` resolves against
|
|
12
|
+
# {Analysis::RuleCatalog}, which carries diagnostic rules only, so `rigor explain
|
|
13
|
+
# sig.skipped.untyped-return` used to reply `Unknown rule` for an id Rigor had just printed. This is the
|
|
14
|
+
# second catalogue `explain` consults; it is deliberately separate from the rule catalog because a skip
|
|
15
|
+
# reason has no severity, no profile mapping and no suppression, and forcing it into {RuleCatalog::Entry}
|
|
16
|
+
# would mean inventing four fields that do not apply.
|
|
17
|
+
#
|
|
18
|
+
# Every id in {Classification::SKIP_DIAGNOSTIC_IDS} MUST have an entry here; the gate is
|
|
19
|
+
# `spec/rigor/sig_gen/skip_reason_catalog_spec.rb`.
|
|
20
|
+
module SkipReasonCatalog
|
|
21
|
+
class Entry < Data.define(:id, :summary, :explanation, :next_step)
|
|
22
|
+
# Hash-shaped form for `rigor explain --format=json`. String keys, and `kind` distinguishes a skip
|
|
23
|
+
# reason from a rule entry in a stream a consumer may receive either from.
|
|
24
|
+
def to_h
|
|
25
|
+
{
|
|
26
|
+
"id" => id,
|
|
27
|
+
"kind" => "sig_skip_reason",
|
|
28
|
+
"summary" => summary,
|
|
29
|
+
"explanation" => explanation,
|
|
30
|
+
"next_step" => next_step
|
|
31
|
+
}
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
ENTRIES = {
|
|
36
|
+
"sig.skipped.untyped-return" => Entry.new(
|
|
37
|
+
id: "sig.skipped.untyped-return",
|
|
38
|
+
summary: "The method's return types as Dynamic[top], so no signature was written.",
|
|
39
|
+
explanation: "Rigor inferred the body but the value it returns has no proven type — the last " \
|
|
40
|
+
"expression widened to Dynamic[top] somewhere up its chain. Emitting `untyped` here " \
|
|
41
|
+
"would record noise as a contract: the file would gain a signature that says nothing " \
|
|
42
|
+
"and stops a later, better inference from being noticed. This is the commonest skip " \
|
|
43
|
+
"reason on a project whose dependencies have no RBS.",
|
|
44
|
+
next_step: "Do not write the return type by hand — the skip is the finding. Trace the returned " \
|
|
45
|
+
"expression back to where it became dynamic (`rigor annotate PATH` reads the `#=>` " \
|
|
46
|
+
"column, `rigor trace --format=json --line=N` for one line) and close THAT: install or " \
|
|
47
|
+
"write RBS for the dependency, enable the plugin for the framework, or report an " \
|
|
48
|
+
"engine gap."
|
|
49
|
+
),
|
|
50
|
+
"sig.skipped.user-authored" => Entry.new(
|
|
51
|
+
id: "sig.skipped.user-authored",
|
|
52
|
+
summary: "An RBS declaration for this method already exists and was left alone.",
|
|
53
|
+
explanation: "Not a gap. The project already states a type for this member, and sig-gen does not " \
|
|
54
|
+
"overwrite a hand-authored contract without being told to. Where `sig/` and an inline " \
|
|
55
|
+
"annotation both declare the member, `sig/` wins.",
|
|
56
|
+
next_step: "Read the existing declaration. If it disagrees with what Rigor infers, raise the " \
|
|
57
|
+
"disagreement rather than silently retyping it; pass `--overwrite` only once you have " \
|
|
58
|
+
"decided the generated type is the better contract."
|
|
59
|
+
),
|
|
60
|
+
"sig.skipped.unrenderable-rbs" => Entry.new(
|
|
61
|
+
id: "sig.skipped.unrenderable-rbs",
|
|
62
|
+
summary: "Rigor rendered a line that `rbs` itself rejects, so the method was dropped.",
|
|
63
|
+
explanation: "A defect in Rigor's renderer, not in your code. The method is skipped rather than " \
|
|
64
|
+
"written so one pathological signature cannot make the whole generated file " \
|
|
65
|
+
"unparseable, and the count is reported so the defect is not silent.",
|
|
66
|
+
next_step: "Report it, with the method and the file. `rigor sig-gen --print PATH` shows what was " \
|
|
67
|
+
"generated for the rest of the file and is the useful attachment."
|
|
68
|
+
),
|
|
69
|
+
"sig.skipped.complex-shape" => Entry.new(
|
|
70
|
+
id: "sig.skipped.complex-shape",
|
|
71
|
+
summary: "Reserved for a shape too complex to render; no code path produces it today.",
|
|
72
|
+
explanation: "The identifier is part of the reserved `sig.skipped.*` family and is carried so the " \
|
|
73
|
+
"family stays stable, but no generator path emits it.",
|
|
74
|
+
next_step: "If you have actually seen this id in output, that is itself worth reporting — it means " \
|
|
75
|
+
"a path exists that this catalogue does not describe."
|
|
76
|
+
),
|
|
77
|
+
"sig.skipped.unresolvable-superclass" => Entry.new(
|
|
78
|
+
id: "sig.skipped.unresolvable-superclass",
|
|
79
|
+
summary: "The class's superclass chain does not end in a class the RBS environment knows.",
|
|
80
|
+
explanation: "Writing the declaration would make things worse rather than better: RBS would read " \
|
|
81
|
+
"the class as rooted at an unknown parent and collapse it on the next run, losing the " \
|
|
82
|
+
"members it can resolve today.",
|
|
83
|
+
next_step: "Give the environment the missing ancestor — install or write RBS for the gem that " \
|
|
84
|
+
"defines it, or add the project file that declares it to the analysed paths — then " \
|
|
85
|
+
"re-run `rigor sig-gen`."
|
|
86
|
+
),
|
|
87
|
+
"sig.skipped.overridden-by-unsigned-subclass" => Entry.new(
|
|
88
|
+
id: "sig.skipped.overridden-by-unsigned-subclass",
|
|
89
|
+
summary: "A subclass overrides this method and its override was not itself emitted.",
|
|
90
|
+
explanation: "A declaration on the parent is inherited by every subclass that does not redeclare " \
|
|
91
|
+
"the member. Emitting it here would hand the subclass a type describing the parent's " \
|
|
92
|
+
"body, not the override the subclass actually runs.",
|
|
93
|
+
next_step: "Generate signatures for the overriding subclass too — widen the `sig-gen` path set to " \
|
|
94
|
+
"cover it — and the parent's declaration becomes safe to emit."
|
|
95
|
+
)
|
|
96
|
+
}.freeze
|
|
97
|
+
|
|
98
|
+
module_function
|
|
99
|
+
|
|
100
|
+
# @param token — a `sig.skipped.*` identifier.
|
|
101
|
+
# @return the matching entry, or nil when the token is not a known skip reason.
|
|
102
|
+
def resolve(token)
|
|
103
|
+
ENTRIES[token.to_s]
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def all
|
|
107
|
+
ENTRIES.values
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rigor
|
|
4
|
+
module SigGen
|
|
5
|
+
# Renders a recorded superclass token into the spelling that goes on an emitted `class` header.
|
|
6
|
+
#
|
|
7
|
+
# Issue [#609](https://github.com/rigortype/rigor/issues/609): every token that survives
|
|
8
|
+
# {Generator#demote_unresolvable_superclasses} names a class the RBS environment or this run declares at an
|
|
9
|
+
# ABSOLUTE position — both guards ask `Reflection.rbs_class_known?` and the emitted-name set, and both are
|
|
10
|
+
# keyed by fully qualified name. The writer then emitted that token bare inside a nested `module` tree, where
|
|
11
|
+
# RBS resolves its first segment relatively against the enclosing namespaces, so `class Show < Help::Show`
|
|
12
|
+
# written under `module Views; module Help` re-points at `::Views::Help::Show` — the class being declared.
|
|
13
|
+
# The self-referential ancestry is what made `AncestorBuilder#singleton_ancestors` recurse until the stack
|
|
14
|
+
# was exhausted on the reporter's project, and what `RBS::RecursiveAncestorError` collapses the class over on
|
|
15
|
+
# a smaller one. Anchoring the token with `::` says the name the guards already checked.
|
|
16
|
+
module SuperclassSpelling
|
|
17
|
+
module_function
|
|
18
|
+
|
|
19
|
+
# @param token — a recorded superclass token, possibly carrying type arguments (`Foo::Bar[untyped]`).
|
|
20
|
+
def absolute(token)
|
|
21
|
+
return token if token.nil? || token.start_with?("::")
|
|
22
|
+
|
|
23
|
+
"::#{token}"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -20,9 +20,7 @@ module Rigor
|
|
|
20
20
|
# The module is sig-gen-local; the broader question of whether the inference engine itself should always
|
|
21
21
|
# construct generics with explicit type_args is queued as an ADR-14 follow-up.
|
|
22
22
|
module TypeElaborator
|
|
23
|
-
# @
|
|
24
|
-
# @param environment [Rigor::Environment]
|
|
25
|
-
# @return [Rigor::Type] same shape with bare generic
|
|
23
|
+
# @return same shape with bare generic
|
|
26
24
|
# nominals filled in.
|
|
27
25
|
def self.elaborate(type, environment:)
|
|
28
26
|
arity_cache = {}
|
data/lib/rigor/sig_gen/writer.rb
CHANGED
|
@@ -4,6 +4,7 @@ require "fileutils"
|
|
|
4
4
|
require "rbs"
|
|
5
5
|
|
|
6
6
|
require_relative "classification"
|
|
7
|
+
require_relative "superclass_spelling"
|
|
7
8
|
require_relative "write_result"
|
|
8
9
|
|
|
9
10
|
module Rigor
|
|
@@ -50,8 +51,7 @@ module Rigor
|
|
|
50
51
|
# ADR-14 follow-up: this is the consolidated-layout entry point. The legacy `write(source_path,
|
|
51
52
|
# candidates)` below assumes all candidates share a target and remains for spec convenience.
|
|
52
53
|
#
|
|
53
|
-
# @
|
|
54
|
-
# @return [Array<WriteResult>] one per target sig file.
|
|
54
|
+
# @return one per target sig file.
|
|
55
55
|
def write_all(candidates)
|
|
56
56
|
emittable = candidates.select { |c| EMITTABLE.include?(c.classification) }
|
|
57
57
|
return [] if emittable.empty?
|
|
@@ -62,12 +62,10 @@ module Rigor
|
|
|
62
62
|
.map { |target, group| write_target(target, group) }
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
-
# @param
|
|
66
|
-
# @param candidates [Array<MethodCandidate>] only
|
|
65
|
+
# @param candidates — only
|
|
67
66
|
# emittable classifications (new-method /
|
|
68
67
|
# tighter-return) are honoured; the caller is
|
|
69
68
|
# responsible for filtering.
|
|
70
|
-
# @return [WriteResult]
|
|
71
69
|
def write(source_path, candidates)
|
|
72
70
|
emittable = candidates.select { |c| EMITTABLE.include?(c.classification) }
|
|
73
71
|
return WriteResult.new(source_path: source_path, target_path: nil, action: :noop) if emittable.empty?
|
|
@@ -261,7 +259,7 @@ module Rigor
|
|
|
261
259
|
return "" unless keyword == :class
|
|
262
260
|
|
|
263
261
|
superclass = supers[qualified]
|
|
264
|
-
superclass ? " < #{superclass}" : ""
|
|
262
|
+
superclass ? " < #{SuperclassSpelling.absolute(superclass)}" : ""
|
|
265
263
|
end
|
|
266
264
|
|
|
267
265
|
# Per ADR-14 gap-#3 (a) the keyword for a segment comes from `namespace_kinds` when known. The default for
|
|
@@ -660,6 +658,8 @@ module Rigor
|
|
|
660
658
|
end
|
|
661
659
|
|
|
662
660
|
def eligible_for_replacement?(candidate, decl, source)
|
|
661
|
+
return false if replaces_declared_void?(candidate, decl)
|
|
662
|
+
|
|
663
663
|
case candidate.classification
|
|
664
664
|
when Classification::TIGHTER_RETURN then true
|
|
665
665
|
when Classification::NEW_METHOD then tightens_untyped?(candidate, decl, source)
|
|
@@ -667,6 +667,23 @@ module Rigor
|
|
|
667
667
|
end
|
|
668
668
|
end
|
|
669
669
|
|
|
670
|
+
# Issue #836 — `--overwrite` never turns a declared `-> void` into a value return. The generator no
|
|
671
|
+
# longer proposes one (`Generator#declares_void?` classifies the method `equivalent`), but the writer
|
|
672
|
+
# reaches the same member through case 2 above whenever the RBS environment did not resolve the `def` to
|
|
673
|
+
# this declaration: an observed-parameter upgrade of `(untyped m) -> void` to `(Module m) -> Registry`
|
|
674
|
+
# counts one `untyped` fewer and would be applied, erasing the author's statement that the return is not
|
|
675
|
+
# part of the contract. A candidate that keeps `void` is still eligible, which is what case 2 is for —
|
|
676
|
+
# the `initialize` stub is always spelled `-> void`.
|
|
677
|
+
def replaces_declared_void?(candidate, decl)
|
|
678
|
+
member = find_method_member(decl, candidate.method_name, candidate.kind)
|
|
679
|
+
return false if member.nil?
|
|
680
|
+
|
|
681
|
+
declared_void = member.overloads.any? do |overload|
|
|
682
|
+
overload.method_type.type.return_type.is_a?(RBS::Types::Bases::Void)
|
|
683
|
+
end
|
|
684
|
+
declared_void && !candidate.rbs.to_s.end_with?(" -> void")
|
|
685
|
+
end
|
|
686
|
+
|
|
670
687
|
# Compares the existing member's source-side RBS text against the candidate's proposed RBS text. Returns
|
|
671
688
|
# true when the new spelling has STRICTLY FEWER bare `untyped` tokens than the existing one — i.e. at
|
|
672
689
|
# least one `untyped` slot becomes a concrete type AND no concrete slot becomes `untyped`. Word-boundary
|
|
@@ -16,7 +16,24 @@ module Rigor
|
|
|
16
16
|
# The audit deliberately mirrors the loader's own acceptance test (`path.directory?` + a
|
|
17
17
|
# recursive `**/*.rbs` glob) so a `:ok` verdict means the loader did load from it and a
|
|
18
18
|
# warning means it did not.
|
|
19
|
+
#
|
|
20
|
+
# {.bundled_plugin_routes} answers the mirror-image question — an entry that loads too
|
|
21
|
+
# *little of the plugin*, rather than nothing at all. See its own comment.
|
|
19
22
|
module SignaturePathAudit
|
|
23
|
+
# The `plugins/` tree of the engine running this code, anchored from THIS file
|
|
24
|
+
# (`<root>/lib/rigor/signature_path_audit.rb`) so a git checkout and an installed
|
|
25
|
+
# `rigortype` gem answer identically — the anchor {Plugin::Loader::ENGINE_ROOT} uses, for
|
|
26
|
+
# the reason it documents.
|
|
27
|
+
#
|
|
28
|
+
# Deliberately re-derived rather than read off `Plugin::Loader`: ADR-87 WD4 keeps this
|
|
29
|
+
# file, which every `rigor check` loads before the cache probe, off the plugin
|
|
30
|
+
# subsystem's require graph — and that graph is order-dependent (`plugin/registry.rb`
|
|
31
|
+
# resolves `NodeRuleWalk` at load, which only `rigor/plugin.rb` has required by then), so
|
|
32
|
+
# requiring the loader from here fails outright. `signature_path_audit_spec` pins the two
|
|
33
|
+
# anchors equal, and pins this discovery against `Loader.bundled_plugin_sig_path`, so the
|
|
34
|
+
# duplication cannot drift unnoticed.
|
|
35
|
+
BUNDLED_PLUGINS_ROOT = File.expand_path("../../plugins", __dir__)
|
|
36
|
+
|
|
20
37
|
# One configured `signature_paths:` entry's resolution status.
|
|
21
38
|
#
|
|
22
39
|
# `status` is one of:
|
|
@@ -60,17 +77,11 @@ module Rigor
|
|
|
60
77
|
# `nil` — the unset default, where Rigor auto-detects `<root>/sig` — to get an empty
|
|
61
78
|
# result: an absent auto-detected `sig/` is a normal setup, not a misconfiguration, so it
|
|
62
79
|
# is never audited.
|
|
63
|
-
#
|
|
64
|
-
# @param signature_paths [Array<String, Pathname>, nil]
|
|
65
|
-
# @return [Array<Entry>]
|
|
66
80
|
def self.audit(signature_paths)
|
|
67
81
|
Array(signature_paths).map { |path| classify(path.to_s) }
|
|
68
82
|
end
|
|
69
83
|
|
|
70
84
|
# The subset of {audit} that resolved to nothing — the entries worth warning about.
|
|
71
|
-
#
|
|
72
|
-
# @param signature_paths [Array<String, Pathname>, nil]
|
|
73
|
-
# @return [Array<Entry>]
|
|
74
85
|
def self.warnings(signature_paths)
|
|
75
86
|
audit(signature_paths).select(&:warning?)
|
|
76
87
|
end
|
|
@@ -82,5 +93,141 @@ module Rigor
|
|
|
82
93
|
count = Dir.glob(File.join(path, "**", "*.rbs")).size
|
|
83
94
|
Entry.new(path: path, status: count.zero? ? :empty : :ok, rbs_file_count: count)
|
|
84
95
|
end
|
|
96
|
+
|
|
97
|
+
# One configured entry that reaches the `sig/` of a plugin THIS engine bundles, while
|
|
98
|
+
# `plugins:` never names that plugin (issue #697).
|
|
99
|
+
BundledPluginRoute = Data.define(:path, :gem) do
|
|
100
|
+
# Names the plugin and the one edit that fixes it. The consequence is spelled out
|
|
101
|
+
# because the symptom the user actually sees is `call.undefined-method` on code that
|
|
102
|
+
# runs — nothing in that row points back at the `signature_paths:` line.
|
|
103
|
+
def message
|
|
104
|
+
"signature_paths: #{path.inspect} loads the signatures of the bundled plugin #{gem.inspect}, " \
|
|
105
|
+
"which `plugins:` does not name — so the plugin's manifest never loads, and the classes it " \
|
|
106
|
+
"declares only partially on purpose (ADR-26 `open_receivers:`) are read as complete, which " \
|
|
107
|
+
"reports methods your code really defines as undefined. " \
|
|
108
|
+
"Add #{gem.inspect} to `plugins:` instead of naming its `sig/` in `signature_paths:`."
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def to_h
|
|
112
|
+
{ "path" => path, "gem" => gem, "message" => message }
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# The mirror image of {.warnings}: an entry the loader reads perfectly well, which
|
|
117
|
+
# nevertheless leaves the user worse off than not configuring it.
|
|
118
|
+
#
|
|
119
|
+
# A bundled plugin ships two halves that only work together — the RBS under its `sig/`,
|
|
120
|
+
# and the manifest that says which of those declarations are deliberately partial
|
|
121
|
+
# (ADR-26 `open_receivers:`). `plugins:` loads both; `signature_paths:` loads only the
|
|
122
|
+
# first, and the partial declarations then read as complete, so a scope the application
|
|
123
|
+
# really defines draws `call.undefined-method`. This is a configuration mistake with no
|
|
124
|
+
# visible cause, so it is reported here rather than fixed by teaching the check rules yet
|
|
125
|
+
# another route to the same protection — which is [#660](https://github.com/rigortype/rigor/issues/660)'s
|
|
126
|
+
# question to settle, not this warning's.
|
|
127
|
+
#
|
|
128
|
+
# **What it asks is what the entry LOADS, not what it looks like**, exactly as
|
|
129
|
+
# `Environment::RbsLoader.bundled_overlay_twin_signatures_loaded?` does for the overlay
|
|
130
|
+
# stand-down: both sides are `File.realpath`'d and matched against the plugin's actual
|
|
131
|
+
# `.rbs` files, so a symlink or a case-variant spelling answers the same as the directory
|
|
132
|
+
# itself, and an entry naming a file (or a directory that does not exist) — which the
|
|
133
|
+
# loader reads nothing from — matches nothing. The two are not single-homed because the
|
|
134
|
+
# overlay-side predicate lives on the RBS loader, and ADR-87 WD4 keeps this file, which
|
|
135
|
+
# every `rigor check` loads before the cache probe, off the engine's require graph.
|
|
136
|
+
#
|
|
137
|
+
# @param signature_paths — {Configuration#signature_paths}; `nil` (the unset default,
|
|
138
|
+
# where Rigor auto-detects `<root>/sig`) is never audited.
|
|
139
|
+
# @param plugins — the raw {Configuration#plugins} list.
|
|
140
|
+
def self.bundled_plugin_routes(signature_paths, plugins = [])
|
|
141
|
+
entries = resolved_entries(signature_paths)
|
|
142
|
+
return [] if entries.empty?
|
|
143
|
+
|
|
144
|
+
root = canonical_path(BUNDLED_PLUGINS_ROOT)
|
|
145
|
+
return [] if root.nil? || entries.none? { |(_, dir)| overlapping?(dir, root) }
|
|
146
|
+
|
|
147
|
+
listed = listed_plugin_names(plugins)
|
|
148
|
+
bundled_plugin_sig_dirs.reject { |gem, _| listed?(gem, listed) }.flat_map do |gem, sig_dir|
|
|
149
|
+
files = canonical_rbs_files(sig_dir)
|
|
150
|
+
next [] if files.empty?
|
|
151
|
+
|
|
152
|
+
entries.filter_map do |(entry, dir)|
|
|
153
|
+
BundledPluginRoute.new(path: entry, gem: gem) if files.any? { |file| under?(file, dir) }
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# `[entry as configured, canonical directory]` for every entry the loader would read from.
|
|
159
|
+
def self.resolved_entries(signature_paths)
|
|
160
|
+
Array(signature_paths).filter_map do |entry|
|
|
161
|
+
dir = canonical_path(entry.to_s)
|
|
162
|
+
[entry.to_s, dir] if dir && File.directory?(dir)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
private_class_method :resolved_entries
|
|
166
|
+
|
|
167
|
+
# Gem name of every plugin the engine bundles that ships signatures, to its `sig/`.
|
|
168
|
+
# Derived from the `plugins/` tree rather than listed, for {Plugin::FirstParty}'s reason:
|
|
169
|
+
# a list would be a second source of truth, and its first drift would silently stop
|
|
170
|
+
# warning about the plugin that drifted.
|
|
171
|
+
def self.bundled_plugin_sig_dirs
|
|
172
|
+
return {} unless File.directory?(BUNDLED_PLUGINS_ROOT)
|
|
173
|
+
|
|
174
|
+
Dir.children(BUNDLED_PLUGINS_ROOT).sort.filter_map do |gem|
|
|
175
|
+
sig = File.join(BUNDLED_PLUGINS_ROOT, gem, "sig")
|
|
176
|
+
[gem, sig] if File.directory?(sig)
|
|
177
|
+
end.to_h
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Every name a `plugins:` entry puts on the record, in both the bare-string and the
|
|
181
|
+
# `gem:` / `id:` hash forms. An `enabled: false` entry counts as named even though it
|
|
182
|
+
# loads nothing: the user has demonstrably found the plugin, and telling them to add what
|
|
183
|
+
# is already there would be worse than staying quiet. Under-warning is this module's
|
|
184
|
+
# standing FP-safe direction.
|
|
185
|
+
def self.listed_plugin_names(plugins)
|
|
186
|
+
Array(plugins).flat_map do |raw|
|
|
187
|
+
case raw
|
|
188
|
+
when String then [raw]
|
|
189
|
+
when Hash
|
|
190
|
+
keyed = raw.to_h { |key, value| [key.to_s, value] }
|
|
191
|
+
[keyed["gem"], keyed["id"]].compact.map(&:to_s)
|
|
192
|
+
else []
|
|
193
|
+
end
|
|
194
|
+
end.to_set
|
|
195
|
+
end
|
|
196
|
+
private_class_method :listed_plugin_names
|
|
197
|
+
|
|
198
|
+
# A bundled plugin's manifest id is its gem name minus the `rigor-` prefix, and `plugins:`
|
|
199
|
+
# accepts either spelling as `id:`, so both count as naming it.
|
|
200
|
+
def self.listed?(gem, listed)
|
|
201
|
+
require_relative "plugin/first_party"
|
|
202
|
+
listed.include?(gem) || listed.include?(gem.delete_prefix(Plugin::FirstParty::GEM_PREFIX))
|
|
203
|
+
end
|
|
204
|
+
private_class_method :listed?
|
|
205
|
+
|
|
206
|
+
def self.canonical_rbs_files(dir)
|
|
207
|
+
Dir.glob(File.join(dir, "**", "*.rbs")).filter_map { |file| canonical_path(file) }
|
|
208
|
+
end
|
|
209
|
+
private_class_method :canonical_rbs_files
|
|
210
|
+
|
|
211
|
+
# `File.realpath`, not `File.expand_path`: it resolves symlinks and, on a case-folding
|
|
212
|
+
# filesystem, the on-disk spelling. It raises for a path that does not exist, which is
|
|
213
|
+
# the answer wanted here — a path the loader cannot read is not a route to anything.
|
|
214
|
+
def self.canonical_path(path)
|
|
215
|
+
File.realpath(path.to_s)
|
|
216
|
+
rescue SystemCallError
|
|
217
|
+
nil
|
|
218
|
+
end
|
|
219
|
+
private_class_method :canonical_path
|
|
220
|
+
|
|
221
|
+
def self.under?(path, dir)
|
|
222
|
+
path.start_with?("#{dir}#{File::SEPARATOR}")
|
|
223
|
+
end
|
|
224
|
+
private_class_method :under?
|
|
225
|
+
|
|
226
|
+
# Whether the two directories could possibly share a file. The cheap prune that keeps the
|
|
227
|
+
# per-plugin globs off every run whose `signature_paths:` point at the project's own tree.
|
|
228
|
+
def self.overlapping?(one, other)
|
|
229
|
+
one == other || under?(one, other) || under?(other, one)
|
|
230
|
+
end
|
|
231
|
+
private_class_method :overlapping?
|
|
85
232
|
end
|
|
86
233
|
end
|
|
@@ -31,9 +31,6 @@ module Rigor
|
|
|
31
31
|
|
|
32
32
|
# The Symbol a literal `Prism::SymbolNode` / `Prism::StringNode` names, or `nil` for any other node
|
|
33
33
|
# (including `nil`).
|
|
34
|
-
#
|
|
35
|
-
# @param node [Prism::Node, nil]
|
|
36
|
-
# @return [Symbol, nil]
|
|
37
34
|
def symbol_or_string(node)
|
|
38
35
|
return nil unless node.is_a?(Prism::SymbolNode) || node.is_a?(Prism::StringNode)
|
|
39
36
|
|
|
@@ -44,9 +41,6 @@ module Rigor
|
|
|
44
41
|
# (including `nil`). The String-returning sibling of {.symbol_or_string} — for callers that key on the
|
|
45
42
|
# raw name rather than the interned Symbol (route helpers, factory names, filter targets).
|
|
46
43
|
# `#unescaped` round-trips an interpolation-free `:foo` / `"foo"` to `"foo"` for both kinds.
|
|
47
|
-
#
|
|
48
|
-
# @param node [Prism::Node, nil]
|
|
49
|
-
# @return [String, nil]
|
|
50
44
|
def symbol_or_string_name(node)
|
|
51
45
|
return nil unless node.is_a?(Prism::SymbolNode) || node.is_a?(Prism::StringNode)
|
|
52
46
|
|
|
@@ -56,9 +50,6 @@ module Rigor
|
|
|
56
50
|
# The Symbol a literal `Prism::SymbolNode` names, or `nil` for any other node (including a
|
|
57
51
|
# `Prism::StringNode` and `nil`). Stricter than {.symbol_or_string}: a DSL that accepts only `:draft`
|
|
58
52
|
# and not `"draft"` keeps that distinction by reaching for this rather than the Symbol-or-String form.
|
|
59
|
-
#
|
|
60
|
-
# @param node [Prism::Node, nil]
|
|
61
|
-
# @return [Symbol, nil]
|
|
62
53
|
def symbol(node)
|
|
63
54
|
return nil unless node.is_a?(Prism::SymbolNode)
|
|
64
55
|
|
|
@@ -68,9 +59,6 @@ module Rigor
|
|
|
68
59
|
# The String a literal `Prism::SymbolNode` names, or `nil` for any other node (including a
|
|
69
60
|
# `Prism::StringNode` and `nil`). The String-returning sibling of {.symbol} — SymbolNode-only, but the
|
|
70
61
|
# caller wants the raw name rather than the interned Symbol.
|
|
71
|
-
#
|
|
72
|
-
# @param node [Prism::Node, nil]
|
|
73
|
-
# @return [String, nil]
|
|
74
62
|
def symbol_name(node)
|
|
75
63
|
return nil unless node.is_a?(Prism::SymbolNode)
|
|
76
64
|
|
|
@@ -79,9 +67,6 @@ module Rigor
|
|
|
79
67
|
|
|
80
68
|
# Every literal Symbol/String positional argument of a call, in source order. Non-literal arguments
|
|
81
69
|
# are dropped. Returns `[]` when the call has no argument list.
|
|
82
|
-
#
|
|
83
|
-
# @param call_node [Prism::CallNode, nil]
|
|
84
|
-
# @return [Array<Symbol>]
|
|
85
70
|
def symbol_arguments(call_node)
|
|
86
71
|
args = call_node&.arguments&.arguments
|
|
87
72
|
return [] if args.nil?
|
|
@@ -93,20 +78,12 @@ module Rigor
|
|
|
93
78
|
# {.symbol_name} — for callers that need a predicate rather than an extraction (hash-key matching in
|
|
94
79
|
# keyword or assoc argument positions, e.g. `el.is_a?(AssocNode) && symbol_named?(el.key, "required")`).
|
|
95
80
|
# Uses `#unescaped` (not `#value`) for round-trip consistency.
|
|
96
|
-
#
|
|
97
|
-
# @param node [Prism::Node, nil]
|
|
98
|
-
# @param name [String]
|
|
99
|
-
# @return [Boolean]
|
|
100
81
|
def symbol_named?(node, name)
|
|
101
82
|
node.is_a?(Prism::SymbolNode) && node.unescaped == name
|
|
102
83
|
end
|
|
103
84
|
|
|
104
85
|
# The literal Symbol/String at positional `index`, or `nil` when the call has no argument list, the
|
|
105
86
|
# index is out of range, or the argument there is not a literal Symbol/String.
|
|
106
|
-
#
|
|
107
|
-
# @param call_node [Prism::CallNode, nil]
|
|
108
|
-
# @param index [Integer]
|
|
109
|
-
# @return [Symbol, nil]
|
|
110
87
|
def symbol_arg(call_node, index)
|
|
111
88
|
args = call_node&.arguments&.arguments
|
|
112
89
|
return nil if args.nil?
|
|
@@ -106,8 +106,6 @@ module Rigor
|
|
|
106
106
|
# e.g. `node.body`). Pure and re-entrant; `break` / `next` / `return` in the block behave exactly as they
|
|
107
107
|
# would with `compact_child_nodes.each`. Walkers holding a known `Prism::Node` call `#rigor_each_child`
|
|
108
108
|
# directly; this wrapper is the nil-tolerant entry.
|
|
109
|
-
#
|
|
110
|
-
# @yieldparam child [Prism::Node]
|
|
111
109
|
def each_child(node, &)
|
|
112
110
|
node.rigor_each_child(&) if node.is_a?(Prism::Node)
|
|
113
111
|
end
|