rigortype 0.3.7 → 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 +96 -15
- 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 +39 -4
- data/docs/manual/03-configuration.md +11 -0
- data/docs/manual/04-diagnostics.md +12 -1
- 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 +69 -13
- 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 +100 -29
- 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 +316 -40
- 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 +430 -77
- data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
- data/lib/rigor/analysis/runner/run_snapshots.rb +13 -3
- data/lib/rigor/analysis/runner.rb +200 -43
- data/lib/rigor/analysis/worker_session.rb +36 -12
- data/lib/rigor/bleeding_edge.rb +12 -24
- data/lib/rigor/builtins/hkt_builtins.rb +2 -2
- data/lib/rigor/builtins/imported_refinements.rb +166 -30
- 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 +70 -0
- 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_environment_marshal_patch.rb +39 -0
- data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
- data/lib/rigor/cache/store.rb +85 -33
- 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 +31 -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 +28 -0
- 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 +613 -113
- data/lib/rigor/environment.rb +189 -27
- 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 +19 -10
- data/lib/rigor/inference/hkt_sugar_translator.rb +15 -21
- 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 +67 -14
- 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 +12 -13
- data/lib/rigor/rbs_extended/hkt_directives.rb +16 -1
- data/lib/rigor/rbs_extended/reporter.rb +122 -16
- data/lib/rigor/rbs_extended.rb +67 -22
- 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 +181 -102
- 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 +17 -11
- 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 +11 -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 +28 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
|
@@ -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
|
|
@@ -14,24 +15,22 @@ module Rigor
|
|
|
14
15
|
# - `:diff` — a unified-style diff comparing the existing RBS spelling (if any) against the inferred
|
|
15
16
|
# spelling. The MVP renders a minimal "- declared / + inferred" block; full per-file diffing arrives with
|
|
16
17
|
# slice 2's `--write` merge.
|
|
17
|
-
# - `:json` — machine-readable payload with the same classification table as `:print
|
|
18
|
+
# - `:json` — machine-readable payload with the same classification table as `:print`, plus every `skipped`
|
|
19
|
+
# row with its `skip_reason` (#778).
|
|
18
20
|
class Renderer
|
|
19
21
|
def initialize(out:)
|
|
20
22
|
@out = out
|
|
21
23
|
end
|
|
22
24
|
|
|
23
|
-
# @param
|
|
24
|
-
# @param
|
|
25
|
-
# @param format [String] "text" or "json"
|
|
26
|
-
# @param selection [Array<Symbol>] subset of
|
|
25
|
+
# @param format — "text" or "json"
|
|
26
|
+
# @param selection — subset of
|
|
27
27
|
# {Classification} constants to include; an empty
|
|
28
28
|
# array means "all emittable classifications".
|
|
29
29
|
def render(candidates:, mode:, format:, selection:)
|
|
30
|
-
filtered = filter(candidates, selection)
|
|
31
|
-
|
|
32
30
|
case format
|
|
33
|
-
when "json" then render_json(
|
|
31
|
+
when "json" then render_json(filter(candidates, selection, with_skipped: true))
|
|
34
32
|
when "text"
|
|
33
|
+
filtered = filter(candidates, selection)
|
|
35
34
|
mode == :diff ? render_diff(filtered) : render_print(filtered)
|
|
36
35
|
else
|
|
37
36
|
raise ArgumentError, "unsupported format: #{format}"
|
|
@@ -40,9 +39,16 @@ module Rigor
|
|
|
40
39
|
|
|
41
40
|
private
|
|
42
41
|
|
|
43
|
-
|
|
42
|
+
# The emittable rows the selection asks for. JSON also carries every `skipped` row whatever the selection:
|
|
43
|
+
# ADR-14 makes the JSON payload the surface where `sig.skipped.*` is reported, and a consumer asking why a
|
|
44
|
+
# method is missing from its `sig/` needs the reason next to the rows that did emit (#778 — the rows were
|
|
45
|
+
# built with their `skip_reason` and then dropped here). `equivalent` rows stay out: nothing to do,
|
|
46
|
+
# nothing to explain.
|
|
47
|
+
def filter(candidates, selection, with_skipped: false)
|
|
44
48
|
active = selection.empty? ? Classification::EMITTABLE : selection
|
|
45
|
-
candidates.select
|
|
49
|
+
candidates.select do |c|
|
|
50
|
+
active.include?(c.classification) || (with_skipped && c.classification == Classification::SKIPPED)
|
|
51
|
+
end
|
|
46
52
|
end
|
|
47
53
|
|
|
48
54
|
def render_print(candidates)
|
|
@@ -84,7 +90,7 @@ module Rigor
|
|
|
84
90
|
return "module #{class_name}" if candidate.namespace_kinds[class_name] == :module
|
|
85
91
|
|
|
86
92
|
superclass = candidate.class_superclasses[class_name]
|
|
87
|
-
superclass ? "class #{class_name} < #{superclass}" : "class #{class_name}"
|
|
93
|
+
superclass ? "class #{class_name} < #{SuperclassSpelling.absolute(superclass)}" : "class #{class_name}"
|
|
88
94
|
end
|
|
89
95
|
|
|
90
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
|
|
@@ -22,25 +22,19 @@ module Rigor
|
|
|
22
22
|
class OutOfRangeError < StandardError; end
|
|
23
23
|
|
|
24
24
|
class << self
|
|
25
|
-
# @param
|
|
26
|
-
# @param
|
|
27
|
-
# @param line [Integer] 1-indexed line number
|
|
28
|
-
# @param column [Integer] 1-indexed column number (byte index within the line)
|
|
29
|
-
# @return [Prism::Node, nil]
|
|
25
|
+
# @param line — 1-indexed line number
|
|
26
|
+
# @param column — 1-indexed column number (byte index within the line)
|
|
30
27
|
def at_position(source:, root:, line:, column:)
|
|
31
28
|
new(source: source, root: root).at_position(line: line, column: column)
|
|
32
29
|
end
|
|
33
30
|
|
|
34
|
-
# @param
|
|
35
|
-
# @param offset [Integer] 0-indexed byte offset
|
|
36
|
-
# @return [Prism::Node, nil]
|
|
31
|
+
# @param offset — 0-indexed byte offset
|
|
37
32
|
def at_offset(root:, offset:)
|
|
38
33
|
new(source: nil, root: root).at_offset(offset)
|
|
39
34
|
end
|
|
40
35
|
end
|
|
41
36
|
|
|
42
|
-
# @param source
|
|
43
|
-
# @param root [Prism::Node]
|
|
37
|
+
# @param source — used by `#at_position`; may be omitted when only `#at_offset` is needed.
|
|
44
38
|
def initialize(source:, root:)
|
|
45
39
|
@source = source
|
|
46
40
|
@root = root
|
|
@@ -48,7 +42,7 @@ module Rigor
|
|
|
48
42
|
|
|
49
43
|
# Resolve a `(line, column)` pair (1-indexed) to the deepest enclosing node.
|
|
50
44
|
#
|
|
51
|
-
# @raise
|
|
45
|
+
# @raise OutOfRangeError — if the line or column falls outside the source buffer.
|
|
52
46
|
def at_position(line:, column:)
|
|
53
47
|
offset = position_to_offset(line, column)
|
|
54
48
|
at_offset(offset)
|
|
@@ -25,8 +25,7 @@ module Rigor
|
|
|
25
25
|
module NodeWalker
|
|
26
26
|
module_function
|
|
27
27
|
|
|
28
|
-
# @
|
|
29
|
-
# @return [Enumerator] when no block is given.
|
|
28
|
+
# @return when no block is given.
|
|
30
29
|
def each(root, &)
|
|
31
30
|
return to_enum(__method__, root) unless block_given?
|
|
32
31
|
|
|
@@ -48,9 +47,7 @@ module Rigor
|
|
|
48
47
|
# block invocation MUST copy it (`Plugin::NodeContext` does). Used by the plugin engine to give
|
|
49
48
|
# `node_rule` blocks their enclosing class / method / block context (ADR-37 slice 1d).
|
|
50
49
|
#
|
|
51
|
-
# @
|
|
52
|
-
# @yieldparam ancestors [Array<Prism::Node>]
|
|
53
|
-
# @return [Enumerator] when no block is given.
|
|
50
|
+
# @return when no block is given.
|
|
54
51
|
def each_with_ancestors(root, &)
|
|
55
52
|
return to_enum(__method__, root) unless block_given?
|
|
56
53
|
|
|
@@ -91,9 +91,7 @@ module Rigor
|
|
|
91
91
|
INFO_GUARDED = %i[h5_systemic_cluster h6_genuine_bugs].freeze
|
|
92
92
|
private_constant :INFO_GUARDED
|
|
93
93
|
|
|
94
|
-
# @param diagnostics
|
|
95
|
-
# @param include_info [Boolean] let H5/H6 see `:info` diagnostics
|
|
96
|
-
# @return [Array<Hint>]
|
|
94
|
+
# @param include_info — let H5/H6 see `:info` diagnostics
|
|
97
95
|
def recognise(diagnostics, include_info: false)
|
|
98
96
|
claimed = {}.compare_by_identity
|
|
99
97
|
recognisers.filter_map do |recogniser|
|
data/lib/rigor/triage.rb
CHANGED
|
@@ -34,11 +34,9 @@ module Rigor
|
|
|
34
34
|
# `rbs.coverage.missing-gem`) survives; the count-based H5/H6 recognisers guard against info themselves so
|
|
35
35
|
# recognition trace never reads as a bug.
|
|
36
36
|
#
|
|
37
|
-
# @param
|
|
38
|
-
# @param
|
|
39
|
-
# @param
|
|
40
|
-
# @param include_info [Boolean] route info into the volume views
|
|
41
|
-
# @return [Report]
|
|
37
|
+
# @param top — hotspot-file cap
|
|
38
|
+
# @param hints — run the heuristic catalogue
|
|
39
|
+
# @param include_info — route info into the volume views
|
|
42
40
|
def analyze(diagnostics, top: 10, hints: true, include_info: false)
|
|
43
41
|
routed = include_info ? diagnostics : diagnostics.reject { |d| d.severity == :info }
|
|
44
42
|
Report.new(
|