rigortype 0.3.8 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/data/capability_roles/capability_roles.rbs +36 -0
- data/data/core_overlay/enumerable.rbs +51 -0
- data/data/core_overlay/enumerator.rbs +84 -0
- data/data/core_overlay/hash_rbs3.rbs +41 -0
- data/data/core_overlay/process.rbs +40 -0
- data/data/core_overlay/string_io.rbs +33 -0
- data/data/effects/core.yml +3 -3
- data/data/gem_overlay/activesupport/core_ext.rbs +236 -17
- data/docs/handbook/02-everyday-types.md +17 -14
- data/docs/handbook/03-narrowing.md +132 -14
- data/docs/handbook/04-tuples-and-shapes.md +8 -6
- 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 +19 -1
- data/docs/handbook/10-sorbet.md +9 -10
- data/docs/handbook/11-sig-gen.md +525 -13
- 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 +95 -6
- data/docs/manual/03-configuration.md +18 -0
- data/docs/manual/04-diagnostics.md +15 -0
- data/docs/manual/07-plugins.md +15 -5
- data/docs/manual/08-skills.md +23 -2
- data/docs/manual/10-mcp-server.md +3 -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 +46 -8
- data/docs/manual/18-removing-dead-code.md +10 -8
- data/docs/manual/19-effect-labels.md +10 -0
- data/docs/manual/plugins/README.md +7 -0
- data/docs/manual/plugins/rigor-actioncable.md +8 -1
- data/docs/manual/plugins/rigor-actionmailer.md +7 -0
- data/docs/manual/plugins/rigor-actionpack.md +261 -0
- data/docs/manual/plugins/rigor-active-model-serializers.md +153 -0
- data/docs/manual/plugins/rigor-activejob.md +7 -0
- data/docs/manual/plugins/rigor-activerecord.md +138 -4
- data/docs/manual/plugins/rigor-activestorage.md +8 -1
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +43 -7
- data/docs/manual/plugins/rigor-grape.md +106 -0
- data/docs/manual/plugins/rigor-graphql.md +23 -2
- data/docs/manual/plugins/rigor-pundit.md +8 -1
- data/docs/manual/plugins/rigor-rails-i18n.md +10 -5
- data/docs/manual/plugins/rigor-rails-routes.md +7 -0
- data/docs/manual/plugins/rigor-rbs-inline.md +229 -1
- data/docs/manual/plugins/rigor-sidekiq.md +8 -1
- data/docs/manual/plugins/rigor-sorbet.md +21 -5
- data/exe/rigor +19 -4
- data/lib/rigor/analysis/baseline.rb +3 -3
- 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/declaration_sourced_guard.rb +167 -2
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +189 -0
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +6 -4
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
- data/lib/rigor/analysis/check_rules/rule_ids.rb +21 -6
- data/lib/rigor/analysis/check_rules/rule_walk.rb +41 -5
- 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/source_arity.rb +323 -0
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +146 -0
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +38 -4
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
- data/lib/rigor/analysis/check_rules.rb +718 -79
- data/lib/rigor/analysis/crash_signature.rb +8 -24
- data/lib/rigor/analysis/dependency_recorder.rb +23 -0
- 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 +15 -1
- data/lib/rigor/analysis/incremental.rb +42 -0
- data/lib/rigor/analysis/incremental_session.rb +117 -26
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
- data/lib/rigor/analysis/project_scan.rb +11 -1
- data/lib/rigor/analysis/reachability/graph.rb +3 -5
- data/lib/rigor/analysis/reachability/plugin_roots.rb +5 -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 +3 -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 +131 -1
- data/lib/rigor/analysis/run_cache_key.rb +21 -12
- 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 +340 -33
- data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +18 -14
- data/lib/rigor/analysis/runner/pool_coordinator.rb +357 -97
- data/lib/rigor/analysis/runner/project_pre_passes.rb +38 -13
- data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
- data/lib/rigor/analysis/runner.rb +407 -51
- data/lib/rigor/analysis/template_unit_collector.rb +303 -0
- data/lib/rigor/analysis/template_unit_paths.rb +91 -0
- data/lib/rigor/analysis/template_unit_positions.rb +293 -0
- data/lib/rigor/analysis/template_units.rb +399 -0
- data/lib/rigor/analysis/worker_session.rb +62 -20
- data/lib/rigor/bleeding_edge.rb +10 -24
- data/lib/rigor/builtins/hkt_builtins.rb +3 -2
- data/lib/rigor/builtins/imported_refinements.rb +159 -21
- data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
- data/lib/rigor/builtins/regex_refinement.rb +21 -13
- data/lib/rigor/builtins/static_return_refinements.rb +7 -6
- data/lib/rigor/cache/annotation_location.rb +2 -4
- data/lib/rigor/cache/descriptor.rb +63 -20
- data/lib/rigor/cache/engine_source.rb +33 -5
- data/lib/rigor/cache/file_digest.rb +9 -1
- data/lib/rigor/cache/incremental_snapshot.rb +72 -6
- data/lib/rigor/cache/rbs_cache_producer.rb +4 -0
- 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 +112 -14
- 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 +76 -32
- data/lib/rigor/ci_detector.rb +1 -0
- 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 +4 -4
- data/lib/rigor/cli/docs_command.rb +6 -6
- 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 +4 -4
- data/lib/rigor/cli/plugins_command.rb +2 -1
- data/lib/rigor/cli/prism_colorizer.rb +1 -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 +212 -31
- data/lib/rigor/cli/skill_command.rb +2 -2
- data/lib/rigor/cli/skill_deep_probe.rb +4 -4
- data/lib/rigor/cli/skill_describe.rb +35 -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 +29 -11
- data/lib/rigor/cli/type_of_renderer.rb +20 -9
- data/lib/rigor/cli/type_of_template_probe.rb +189 -0
- 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 +89 -9
- data/lib/rigor/config_audit.rb +30 -5
- data/lib/rigor/configuration/severity_profile.rb +25 -9
- data/lib/rigor/configuration.rb +109 -7
- data/lib/rigor/effects/ancestry_recorder.rb +191 -0
- data/lib/rigor/effects/attribution.rb +12 -5
- data/lib/rigor/effects/callee_rule.rb +368 -0
- data/lib/rigor/effects/catalog.rb +7 -4
- data/lib/rigor/effects/collector.rb +11 -5
- data/lib/rigor/effects/config_envelopes.rb +15 -10
- data/lib/rigor/effects/definition_context.rb +179 -0
- data/lib/rigor/effects/definition_lines.rb +25 -6
- data/lib/rigor/effects/effect_table.rb +11 -4
- data/lib/rigor/effects/entry_points.rb +0 -2
- data/lib/rigor/effects/envelope_check.rb +9 -9
- data/lib/rigor/effects/envelope_index.rb +20 -8
- data/lib/rigor/effects/file_collection.rb +59 -5
- data/lib/rigor/effects/framework_units.rb +5 -7
- data/lib/rigor/effects/identity.rb +20 -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/local_ownership.rb +38 -12
- data/lib/rigor/effects/method_key.rb +22 -1
- data/lib/rigor/effects/mutation_classifier.rb +23 -12
- data/lib/rigor/effects/plugin_facts.rb +47 -37
- data/lib/rigor/effects/propagator.rb +297 -19
- data/lib/rigor/effects/registry.rb +1 -1
- data/lib/rigor/effects/scanner.rb +122 -73
- data/lib/rigor/effects/signature_sources.rb +4 -6
- data/lib/rigor/effects/snapshot.rb +12 -11
- data/lib/rigor/effects/snapshot_diff.rb +1 -1
- data/lib/rigor/effects/summary.rb +27 -4
- data/lib/rigor/effects/unit_scan.rb +393 -38
- data/lib/rigor/effects/unknown_label_check.rb +3 -8
- data/lib/rigor/effects/unknown_label_report.rb +3 -4
- data/lib/rigor/effects/visibility.rb +101 -0
- 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 +68 -4
- data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
- data/lib/rigor/environment/member_consistency.rb +298 -0
- 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 +674 -79
- data/lib/rigor/environment.rb +174 -24
- data/lib/rigor/flow_contribution/merger.rb +0 -2
- data/lib/rigor/flow_contribution.rb +11 -13
- data/lib/rigor/hashing/xxh3.rb +264 -0
- data/lib/rigor/inference/acceptance.rb +292 -17
- data/lib/rigor/inference/block_auto_splat.rb +216 -0
- data/lib/rigor/inference/block_call_timing.rb +338 -0
- data/lib/rigor/inference/block_parameter_binder.rb +75 -30
- data/lib/rigor/inference/block_repetition.rb +71 -0
- data/lib/rigor/inference/body_fixpoint.rb +7 -6
- data/lib/rigor/inference/budget_trace.rb +3 -6
- data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
- data/lib/rigor/inference/builtins/string_catalog.rb +1 -1
- data/lib/rigor/inference/captured_locals.rb +389 -18
- data/lib/rigor/inference/closure_escape_analyzer.rb +159 -17
- data/lib/rigor/inference/content_join.rb +200 -27
- data/lib/rigor/inference/coverage_scanner.rb +4 -6
- data/lib/rigor/inference/def_return_typer.rb +11 -7
- data/lib/rigor/inference/define_method_block_self.rb +64 -0
- data/lib/rigor/inference/dynamic_origin.rb +1 -1
- data/lib/rigor/inference/element_read_widening.rb +180 -0
- data/lib/rigor/inference/error_info.rb +196 -0
- data/lib/rigor/inference/expression_typer.rb +2124 -468
- data/lib/rigor/inference/external_ancestor_resolution.rb +267 -0
- data/lib/rigor/inference/fork_map.rb +5 -7
- data/lib/rigor/inference/fresh_frame_blocks.rb +127 -0
- data/lib/rigor/inference/global_write_census.rb +239 -0
- data/lib/rigor/inference/guard_rebinding.rb +447 -0
- data/lib/rigor/inference/hash_lookup_mutation.rb +88 -0
- 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 +16 -7
- data/lib/rigor/inference/indexed_narrowing.rb +61 -9
- data/lib/rigor/inference/jump_targets.rb +82 -0
- data/lib/rigor/inference/last_line/implicit_self.rb +210 -0
- data/lib/rigor/inference/last_line/self_evidence.rb +329 -0
- data/lib/rigor/inference/last_line.rb +340 -0
- data/lib/rigor/inference/last_status.rb +144 -0
- data/lib/rigor/inference/macro_block_self_type.rb +168 -21
- data/lib/rigor/inference/match_rebinding/calls.rb +281 -0
- data/lib/rigor/inference/match_rebinding/frame.rb +148 -0
- data/lib/rigor/inference/match_rebinding/operands.rb +236 -0
- data/lib/rigor/inference/match_rebinding/self_calls.rb +83 -0
- data/lib/rigor/inference/match_rebinding.rb +392 -0
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +87 -33
- 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/facet_distribution.rb +147 -0
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/hash_transform_keys_folding.rb +191 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +6 -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/match_data_folding.rb +159 -0
- 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 +165 -111
- data/lib/rigor/inference/method_dispatcher/process_folding.rb +56 -0
- data/lib/rigor/inference/method_dispatcher/proven_overload.rb +72 -0
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +1046 -69
- 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/self_substitute.rb +142 -0
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +129 -54
- 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 +5 -6
- 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 +225 -25
- data/lib/rigor/inference/method_parameter_binder.rb +12 -9
- data/lib/rigor/inference/multi_target_binder.rb +341 -55
- data/lib/rigor/inference/mutation_rejoin.rb +171 -0
- data/lib/rigor/inference/mutation_widening.rb +156 -85
- data/lib/rigor/inference/narrowing.rb +835 -162
- data/lib/rigor/inference/operand_effects.rb +167 -0
- data/lib/rigor/inference/operand_walk.rb +88 -0
- data/lib/rigor/inference/optimistic_origin.rb +153 -16
- data/lib/rigor/inference/origin_lookup.rb +2 -3
- data/lib/rigor/inference/parameter_inference_collector.rb +7 -7
- data/lib/rigor/inference/pre_eval_constants.rb +6 -5
- data/lib/rigor/inference/precision_scanner.rb +3 -4
- data/lib/rigor/inference/project_method_ownership.rb +136 -0
- data/lib/rigor/inference/project_patched_methods.rb +9 -4
- data/lib/rigor/inference/project_patched_scanner.rb +10 -6
- 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 +93 -4
- data/lib/rigor/inference/receiver_blind_block.rb +219 -0
- data/lib/rigor/inference/refinement_mutation.rb +76 -0
- data/lib/rigor/inference/repeated_or_writes.rb +463 -0
- data/lib/rigor/inference/return_barrier.rb +54 -0
- data/lib/rigor/inference/rewrite_mutation.rb +120 -0
- data/lib/rigor/inference/scope_indexer.rb +5228 -627
- data/lib/rigor/inference/statement_evaluator.rb +3452 -595
- data/lib/rigor/inference/stored_block_call.rb +54 -0
- data/lib/rigor/inference/string_mutation.rb +97 -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/unknown_store_widening.rb +200 -0
- data/lib/rigor/inference/unthreaded_rebinds.rb +282 -0
- 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 +0 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +7 -6
- 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 +4 -4
- data/lib/rigor/language_server/incremental_sync.rb +6 -6
- data/lib/rigor/language_server/project_context.rb +7 -8
- 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/mcp/server.rb +2 -1
- data/lib/rigor/plugin/base.rb +258 -26
- data/lib/rigor/plugin/box_probe.rb +91 -0
- data/lib/rigor/plugin/bundled_catalog.rb +167 -0
- data/lib/rigor/plugin/effect_attribution.rb +65 -11
- 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 +33 -13
- data/lib/rigor/plugin/macro/block_as_method.rb +45 -6
- 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 +94 -11
- data/lib/rigor/plugin/registry.rb +76 -12
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +21 -4
- data/lib/rigor/plugin/template_unit.rb +196 -0
- data/lib/rigor/plugin/trust_policy.rb +2 -4
- data/lib/rigor/plugin/type_node_resolver.rb +3 -3
- data/lib/rigor/plugin.rb +1 -0
- 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 +14 -12
- data/lib/rigor/protection/kill_signature.rb +1 -4
- data/lib/rigor/protection/measurement_integrity.rb +1 -3
- data/lib/rigor/protection/mutation_cache.rb +7 -11
- 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 +88 -21
- data/lib/rigor/reflection/constant_ancestors.rb +97 -0
- data/lib/rigor/reflection/constant_path.rb +139 -0
- data/lib/rigor/reflection.rb +93 -110
- data/lib/rigor/runtime/jit.rb +6 -8
- data/lib/rigor/scope/discovery_index.rb +165 -2
- data/lib/rigor/scope.rb +1041 -32
- data/lib/rigor/sig_gen/alias_index.rb +289 -0
- data/lib/rigor/sig_gen/classification.rb +22 -5
- data/lib/rigor/sig_gen/declaration_equivalence.rb +127 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +202 -0
- data/lib/rigor/sig_gen/generator.rb +673 -53
- data/lib/rigor/sig_gen/inline_declarations.rb +184 -0
- 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/method_candidate.rb +47 -3
- data/lib/rigor/sig_gen/observation_collector.rb +18 -16
- 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 +128 -14
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +154 -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/write_result.rb +19 -3
- data/lib/rigor/sig_gen/writer.rb +189 -41
- data/lib/rigor/sig_gen.rb +3 -0
- 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 +0 -6
- data/lib/rigor/source/parameter_envelope.rb +72 -0
- data/lib/rigor/source.rb +1 -0
- 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 +144 -5
- data/lib/rigor/type/data_class.rb +2 -2
- data/lib/rigor/type/data_instance.rb +4 -4
- data/lib/rigor/type/difference.rb +1 -0
- data/lib/rigor/type/float_range.rb +128 -0
- data/lib/rigor/type/hash_shape.rb +6 -6
- data/lib/rigor/type/integer_range.rb +13 -8
- data/lib/rigor/type/nominal.rb +8 -2
- data/lib/rigor/type/refined.rb +4 -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/lib/rigor.rb +1 -0
- 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/effects.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +10 -5
- 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 +14 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +32 -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/controller_scan.rb +96 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/effects.rb +65 -8
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +270 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +402 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +370 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_units.rb +132 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +264 -3
- data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
- data/plugins/rigor-actionpack/sig/action_view.rbs +43 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +220 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_index.rb +56 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +263 -0
- data/plugins/rigor-active-model-serializers/lib/rigor-active-model-serializers.rb +7 -0
- data/plugins/rigor-active-model-serializers/sig/active_model_serializers.rbs +45 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +2 -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 +10 -5
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +35 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +72 -12
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +383 -7
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +14 -6
- 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 +322 -41
- data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +206 -31
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +26 -14
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +9 -2
- 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 +290 -19
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +3 -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 +2 -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/types.rb +1 -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-grape/lib/rigor/plugin/grape.rb +141 -0
- data/plugins/rigor-grape/lib/rigor-grape.rb +6 -0
- data/plugins/rigor-grape/sig/grape.rbs +263 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +78 -5
- data/plugins/rigor-graphql/sig/graphql.rbs +485 -0
- 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 +2 -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 +12 -7
- 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 +36 -37
- 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 +0 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +5 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +3 -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 +10 -17
- 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/same_line_annotations.rb +149 -0
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +324 -18
- 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 +2 -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 +10 -5
- 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 +3 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +51 -6
- data/plugins/rigor-sorbet/sig/sorbet.rbs +277 -0
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
- data/sig/rigor/analysis/baseline.rbs +69 -7
- data/sig/rigor/analysis/fact_store.rbs +1 -1
- data/sig/rigor/analysis/project_scan.rbs +74 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
- data/sig/rigor/cache.rbs +2 -3
- data/sig/rigor/effects/config_envelopes.rbs +100 -0
- data/sig/rigor/effects/effect_table.rbs +60 -0
- data/sig/rigor/effects/envelope.rbs +97 -0
- data/sig/rigor/effects/envelope_index.rbs +33 -0
- data/sig/rigor/effects/file_collection.rbs +81 -0
- data/sig/rigor/effects/label.rbs +26 -0
- data/sig/rigor/effects/label_set.rbs +46 -0
- data/sig/rigor/effects/method_key.rbs +24 -0
- data/sig/rigor/effects/origin.rbs +50 -0
- data/sig/rigor/effects/plugin_facts.rbs +141 -0
- data/sig/rigor/effects/registry.rbs +68 -0
- data/sig/rigor/effects/summary.rbs +50 -0
- data/sig/rigor/effects/taint_cause.rbs +12 -0
- data/sig/rigor/environment.rbs +17 -10
- data/sig/rigor/inference/optimistic_origin.rbs +10 -0
- data/sig/rigor/inference.rbs +9 -14
- data/sig/rigor/plugin/additional_initializer.rbs +36 -0
- data/sig/rigor/plugin/base.rbs +30 -7
- data/sig/rigor/plugin/effect_ancestry.rbs +27 -0
- data/sig/rigor/plugin/effect_attribution.rbs +66 -0
- data/sig/rigor/plugin/effect_edge.rbs +33 -0
- data/sig/rigor/plugin/effect_entry_points.rbs +25 -0
- data/sig/rigor/plugin/io_boundary.rbs +4 -0
- data/sig/rigor/plugin/loader.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +51 -11
- data/sig/rigor/plugin/protocol_contract.rbs +68 -0
- data/sig/rigor/plugin/registry.rbs +62 -1
- data/sig/rigor/plugin.rbs +1 -1
- data/sig/rigor/rbs_extended.rbs +1 -1
- data/sig/rigor/reflection.rbs +9 -6
- data/sig/rigor/scope.rbs +148 -12
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +21 -0
- data/sig/rigor/source.rbs +4 -4
- data/sig/rigor/testing.rbs +10 -4
- data/sig/rigor/type.rbs +33 -1
- data/sig/rigor.rbs +39 -20
- data/skills/rigor-ask/SKILL.md +16 -11
- data/skills/rigor-baseline-reduce/SKILL.md +4 -6
- data/skills/rigor-ci-setup/SKILL.md +17 -21
- data/skills/rigor-doctor/SKILL.md +24 -22
- data/skills/rigor-doctor/references/01-checks.md +97 -33
- data/skills/rigor-editor-setup/SKILL.md +6 -4
- data/skills/rigor-mcp-setup/SKILL.md +5 -4
- data/skills/rigor-monkeypatch-resolve/SKILL.md +5 -3
- data/skills/rigor-next-steps/SKILL.md +4 -2
- data/skills/rigor-plugin-author/SKILL.md +19 -23
- data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +9 -8
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +25 -27
- data/skills/rigor-plugin-author/references/03-test-and-ship.md +6 -8
- data/skills/rigor-plugin-review/SKILL.md +6 -4
- data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +2 -1
- data/skills/rigor-plugin-tune/SKILL.md +4 -2
- data/skills/rigor-project-init/SKILL.md +15 -10
- data/skills/rigor-project-init/references/01-detect.md +13 -9
- data/skills/rigor-project-init/references/02-configure.md +43 -14
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +14 -14
- data/skills/rigor-project-init/references/04-sig-uplift.md +27 -14
- data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
- data/skills/rigor-protection-uplift/SKILL.md +4 -6
- data/skills/rigor-rbs-setup/SKILL.md +4 -2
- data/skills/rigor-type-oracle/SKILL.md +226 -0
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +265 -0
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +125 -0
- data/skills/rigor-unused-adjudicate/SKILL.md +12 -7
- data/skills/rigor-upgrade/SKILL.md +13 -8
- metadata +133 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
|
@@ -0,0 +1,154 @@
|
|
|
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, ADR-112 WD5's consistency rule decides which binds.",
|
|
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.inline-declared" => Entry.new(
|
|
88
|
+
id: "sig.skipped.inline-declared",
|
|
89
|
+
summary: "The member is declared inline, and `sig_gen.inline_declared: skip` keeps it out of `sig/`.",
|
|
90
|
+
explanation: "Not a gap. The project asked sig-gen to leave every method its inline `# @rbs` / `#:` " \
|
|
91
|
+
"reader declares out of `sig/`, the setting for a project whose Steep reads the same " \
|
|
92
|
+
"annotations (`inline: true` beside `signature \"sig\"`), where a `sig/` copy would " \
|
|
93
|
+
"declare the method twice (`DuplicatedMethodDefinition`). Rigor still reads the inline " \
|
|
94
|
+
"declaration when it analyses the source; a consumer reading only the shipped `sig/` " \
|
|
95
|
+
"does not see this method.",
|
|
96
|
+
next_step: "Nothing, if the setting is intended. To ship the member in `sig/`, remove " \
|
|
97
|
+
"`sig_gen.inline_declared: skip` from `.rigor.yml` and stop Steep from reading the " \
|
|
98
|
+
"annotations of the files you generate for."
|
|
99
|
+
),
|
|
100
|
+
"sig.skipped.inline-generic-class" => Entry.new(
|
|
101
|
+
id: "sig.skipped.inline-generic-class",
|
|
102
|
+
summary: "The class is generic by an inline declaration, and sig-gen does not write type parameters.",
|
|
103
|
+
explanation: "The class (or one it is nested in) takes type parameters from `# @rbs generic`, and " \
|
|
104
|
+
"`sig/` either does not declare it yet or declares it with differently named parameters. " \
|
|
105
|
+
"sig-gen would have to open it with a header that has no type parameters, which rbs " \
|
|
106
|
+
"rejects (`GenericParameterMismatchError`) — the class, and every class whose signature " \
|
|
107
|
+
"mentions it, would fail its definition build and read `Dynamic[top]` — or write members " \
|
|
108
|
+
"whose `T` the `sig/` declaration does not bind. The inline declaration still binds when " \
|
|
109
|
+
"the source is analysed.",
|
|
110
|
+
next_step: "Declare the class in `sig/` with the same type parameters as the inline declaration " \
|
|
111
|
+
"(`class Box[T]` ... `end`) and re-run; sig-gen then writes the members into it."
|
|
112
|
+
),
|
|
113
|
+
"sig.skipped.inline-differs" => Entry.new(
|
|
114
|
+
id: "sig.skipped.inline-differs",
|
|
115
|
+
summary: "The method's inline declaration and its `sig/` declaration disagree; neither was changed.",
|
|
116
|
+
explanation: "The method is declared inline by `# @rbs` / `#:` and in `sig/` (a `def` or an `attr_*`), " \
|
|
117
|
+
"and the two do not state the same types, or `sig/` lacks an annotation written inline. " \
|
|
118
|
+
"Parameter names, union spelling and a `::` that does not change what a name resolves to " \
|
|
119
|
+
"are ignored; overload order is not, because RBS answers a call with the first overload " \
|
|
120
|
+
"that matches. For a parameter-only annotation (`# @rbs name: T`, no `return:`) only the " \
|
|
121
|
+
"parameters are compared, and the return follows the ordinary proposal rules. sig-gen " \
|
|
122
|
+
"does not presume either side right, so it refuses, and `--write` and `--check` exit 1.",
|
|
123
|
+
next_step: "Decide which declaration is right. To keep the inline one, re-run with `--overwrite`, which " \
|
|
124
|
+
"replaces the whole `sig/` member with it (an overload written only in `sig/` is dropped); " \
|
|
125
|
+
"to keep the `sig/` one, edit the annotation to match, or delete it. A parameter-only " \
|
|
126
|
+
"annotation whose parameters differ stays refused under `--overwrite`, because its return " \
|
|
127
|
+
"would be inferred under the `sig/` parameters: delete the `sig/` member and re-run, or add " \
|
|
128
|
+
"`# @rbs return:` so nothing is inferred."
|
|
129
|
+
),
|
|
130
|
+
"sig.skipped.overridden-by-unsigned-subclass" => Entry.new(
|
|
131
|
+
id: "sig.skipped.overridden-by-unsigned-subclass",
|
|
132
|
+
summary: "A subclass overrides this method and its override was not itself emitted.",
|
|
133
|
+
explanation: "A declaration on the parent is inherited by every subclass that does not redeclare " \
|
|
134
|
+
"the member. Emitting it here would hand the subclass a type describing the parent's " \
|
|
135
|
+
"body, not the override the subclass actually runs.",
|
|
136
|
+
next_step: "Generate signatures for the overriding subclass too — widen the `sig-gen` path set to " \
|
|
137
|
+
"cover it — and the parent's declaration becomes safe to emit."
|
|
138
|
+
)
|
|
139
|
+
}.freeze
|
|
140
|
+
|
|
141
|
+
module_function
|
|
142
|
+
|
|
143
|
+
# @param token — a `sig.skipped.*` identifier.
|
|
144
|
+
# @return the matching entry, or nil when the token is not a known skip reason.
|
|
145
|
+
def resolve(token)
|
|
146
|
+
ENTRIES[token.to_s]
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def all
|
|
150
|
+
ENTRIES.values
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
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 = {}
|
|
@@ -13,23 +13,32 @@ module Rigor
|
|
|
13
13
|
# - `action` — one of `:created` / `:updated` / `:noop` / `:skipped_outside_sig_root` /
|
|
14
14
|
# `:skipped_invalid_rbs` / `:skipped_invalid_encoding`.
|
|
15
15
|
# - `applied` — the {MethodCandidate}s that actually landed on disk.
|
|
16
|
+
# - `replaced` — the subset of `applied` that replaced an existing declaration rather than adding one
|
|
17
|
+
# (`--overwrite`'s tighter returns, and inline updates).
|
|
16
18
|
# - `skipped` — the {MethodCandidate}s the writer declined (e.g. tighter-return without `--overwrite`). Each
|
|
17
19
|
# entry pairs the candidate with a skip reason keyword (`:user_authored`).
|
|
20
|
+
# - `left_unreadable` — the {MethodCandidate}s whose effect annotation was NOT written because the
|
|
21
|
+
# target declaration already carries annotations, so its bytes were left alone
|
|
22
|
+
# (`sig.effect.left-unreadable`; see {Writer#splice_annotations}). The signature line itself was
|
|
23
|
+
# still updated.
|
|
18
24
|
# - `error` — the refusal cause, when `action` is a refusal: for `:skipped_invalid_rbs` the file the writer
|
|
19
25
|
# assembled does not parse, so it was NOT written (writing it would poison the project's sig tree — the
|
|
20
26
|
# consumer quarantines an unparseable `.rbs`, taking every other type in that file down with it); for
|
|
21
27
|
# `:skipped_invalid_encoding` the EXISTING target file is not valid UTF-8, so the writer refuses to merge
|
|
22
28
|
# into content it cannot read faithfully.
|
|
23
29
|
class WriteResult
|
|
24
|
-
attr_reader :source_path, :target_path, :action, :applied, :skipped, :error
|
|
30
|
+
attr_reader :source_path, :target_path, :action, :applied, :skipped, :error, :left_unreadable, :replaced
|
|
25
31
|
|
|
26
|
-
def initialize(source_path:, target_path:, action:, applied: [], skipped: [], error: nil
|
|
32
|
+
def initialize(source_path:, target_path:, action:, applied: [], skipped: [], error: nil, # rubocop:disable Metrics/ParameterLists
|
|
33
|
+
left_unreadable: [], replaced: [])
|
|
27
34
|
@source_path = source_path
|
|
28
35
|
@target_path = target_path
|
|
29
36
|
@action = action
|
|
30
37
|
@applied = applied.freeze
|
|
31
38
|
@skipped = skipped.freeze
|
|
32
39
|
@error = error
|
|
40
|
+
@left_unreadable = left_unreadable.freeze
|
|
41
|
+
@replaced = replaced.freeze
|
|
33
42
|
freeze
|
|
34
43
|
end
|
|
35
44
|
|
|
@@ -40,7 +49,14 @@ module Rigor
|
|
|
40
49
|
action: action.to_s,
|
|
41
50
|
applied: applied.map(&:to_h),
|
|
42
51
|
skipped: skipped.map { |c, reason| c.to_h.merge(write_skip_reason: reason.to_s) }
|
|
43
|
-
}.tap
|
|
52
|
+
}.tap do |h|
|
|
53
|
+
h[:error] = error if error
|
|
54
|
+
# Absent when nothing was replaced, so a run that only adds lines keeps its pre-#1076 payload.
|
|
55
|
+
h[:replaced] = replaced.map(&:to_h) unless replaced.empty?
|
|
56
|
+
# Absent rather than empty when nothing was left: an effects-off payload stays byte-identical
|
|
57
|
+
# to a pre-#391 one.
|
|
58
|
+
h[:effect_left_unreadable] = left_unreadable.map(&:to_h) unless left_unreadable.empty?
|
|
59
|
+
end
|
|
44
60
|
end
|
|
45
61
|
end
|
|
46
62
|
end
|
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
|
|
@@ -30,12 +31,16 @@ module Rigor
|
|
|
30
31
|
|
|
31
32
|
# Per-`update_existing` accumulator. The merge_class helper mutates `source` / `decls` / `applied` /
|
|
32
33
|
# `skipped` in place as each class is processed so the next class sees the latest byte positions.
|
|
33
|
-
MergeState = Struct.new(:source, :decls, :applied, :skipped, keyword_init: true)
|
|
34
|
+
MergeState = Struct.new(:source, :decls, :applied, :skipped, :left_unreadable, :replaced, keyword_init: true)
|
|
34
35
|
private_constant :MergeState
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
# @param dry_run — assemble and validate every target exactly as a write would, and report it, without
|
|
38
|
+
# creating or changing a file. `sig-gen --check` (ADR-112 WD4) is this, so it can only ever disagree
|
|
39
|
+
# with `--write` about the disk, never about the content.
|
|
40
|
+
def initialize(path_mapper:, overwrite: false, dry_run: false)
|
|
37
41
|
@path_mapper = path_mapper
|
|
38
42
|
@overwrite = overwrite
|
|
43
|
+
@dry_run = dry_run
|
|
39
44
|
# Run-level (cross-file) namespace-kind view, populated per `#write_all` from every candidate's per-file
|
|
40
45
|
# map. Empty until then so the single-target `#write` path falls back to per-candidate kinds only.
|
|
41
46
|
@global_namespace_kinds = {}
|
|
@@ -50,8 +55,7 @@ module Rigor
|
|
|
50
55
|
# ADR-14 follow-up: this is the consolidated-layout entry point. The legacy `write(source_path,
|
|
51
56
|
# candidates)` below assumes all candidates share a target and remains for spec convenience.
|
|
52
57
|
#
|
|
53
|
-
# @
|
|
54
|
-
# @return [Array<WriteResult>] one per target sig file.
|
|
58
|
+
# @return one per target sig file.
|
|
55
59
|
def write_all(candidates)
|
|
56
60
|
emittable = candidates.select { |c| EMITTABLE.include?(c.classification) }
|
|
57
61
|
return [] if emittable.empty?
|
|
@@ -62,12 +66,10 @@ module Rigor
|
|
|
62
66
|
.map { |target, group| write_target(target, group) }
|
|
63
67
|
end
|
|
64
68
|
|
|
65
|
-
# @param
|
|
66
|
-
# @param candidates [Array<MethodCandidate>] only
|
|
69
|
+
# @param candidates — only
|
|
67
70
|
# emittable classifications (new-method /
|
|
68
71
|
# tighter-return) are honoured; the caller is
|
|
69
72
|
# responsible for filtering.
|
|
70
|
-
# @return [WriteResult]
|
|
71
73
|
def write(source_path, candidates)
|
|
72
74
|
emittable = candidates.select { |c| EMITTABLE.include?(c.classification) }
|
|
73
75
|
return WriteResult.new(source_path: source_path, target_path: nil, action: :noop) if emittable.empty?
|
|
@@ -76,6 +78,15 @@ module Rigor
|
|
|
76
78
|
write_target(target, emittable, source_path: source_path)
|
|
77
79
|
end
|
|
78
80
|
|
|
81
|
+
# Whether `proposed` has STRICTLY FEWER bare `untyped` tokens than `existing`. A count, not a slot-by-slot
|
|
82
|
+
# comparison: it reads as "at least one more position is concrete", and does not prove that no concrete
|
|
83
|
+
# position became `untyped` elsewhere. Word-boundary matching counts `untyped` only as a type token, not
|
|
84
|
+
# as a substring inside identifiers. Public because the generator asks the same question before it lists a
|
|
85
|
+
# declared `initialize` at all (#1436).
|
|
86
|
+
def self.fewer_untyped?(proposed, existing)
|
|
87
|
+
proposed.scan(/\buntyped\b/).size < existing.scan(/\buntyped\b/).size
|
|
88
|
+
end
|
|
89
|
+
|
|
79
90
|
private
|
|
80
91
|
|
|
81
92
|
# Shared per-target write path used by both `#write` and `#write_all`. Picks a representative
|
|
@@ -93,7 +104,7 @@ module Rigor
|
|
|
93
104
|
# Deliberately NARROWER than {Classification::EMITTABLE}: the write path has never produced a
|
|
94
105
|
# `:new_file` row (the generator does not classify one), and admitting it here would let a
|
|
95
106
|
# classification with no writer behind it decide that a target file must be created.
|
|
96
|
-
EMITTABLE = [Classification::NEW_METHOD, Classification::TIGHTER_RETURN].freeze
|
|
107
|
+
EMITTABLE = [Classification::NEW_METHOD, Classification::TIGHTER_RETURN, Classification::INLINE_OVERWRITE].freeze
|
|
97
108
|
private_constant :EMITTABLE
|
|
98
109
|
|
|
99
110
|
def inside_sig_root?(target)
|
|
@@ -118,8 +129,10 @@ module Rigor
|
|
|
118
129
|
action: :skipped_invalid_rbs, error: error)
|
|
119
130
|
end
|
|
120
131
|
|
|
121
|
-
|
|
122
|
-
|
|
132
|
+
unless @dry_run
|
|
133
|
+
FileUtils.mkdir_p(target.dirname)
|
|
134
|
+
target.write(content)
|
|
135
|
+
end
|
|
123
136
|
WriteResult.new(source_path: source_path, target_path: target,
|
|
124
137
|
action: :created, applied: candidates)
|
|
125
138
|
end
|
|
@@ -247,7 +260,7 @@ module Rigor
|
|
|
247
260
|
|
|
248
261
|
def render_tree_node_body(node, kinds, supers, depth, prefix)
|
|
249
262
|
inner_indent = INDENT * (depth + 1)
|
|
250
|
-
method_lines = node[:methods].
|
|
263
|
+
method_lines = node[:methods].flat_map { |c| c.rbs_lines.map { |line| "#{inner_indent}#{line}\n" } }.join
|
|
251
264
|
child_blocks = node[:children].values.map do |child|
|
|
252
265
|
render_tree_node(child, kinds, supers, depth + 1, prefix + [node[:name]])
|
|
253
266
|
end.join
|
|
@@ -261,7 +274,7 @@ module Rigor
|
|
|
261
274
|
return "" unless keyword == :class
|
|
262
275
|
|
|
263
276
|
superclass = supers[qualified]
|
|
264
|
-
superclass ? " < #{superclass}" : ""
|
|
277
|
+
superclass ? " < #{SuperclassSpelling.absolute(superclass)}" : ""
|
|
265
278
|
end
|
|
266
279
|
|
|
267
280
|
# Per ADR-14 gap-#3 (a) the keyword for a segment comes from `namespace_kinds` when known. The default for
|
|
@@ -293,13 +306,15 @@ module Rigor
|
|
|
293
306
|
decls = parse_signature(source)
|
|
294
307
|
return WriteResult.new(source_path: source_path, target_path: target, action: :noop) if decls.nil?
|
|
295
308
|
|
|
296
|
-
state = MergeState.new(source: source, decls: decls, applied: [], skipped: []
|
|
309
|
+
state = MergeState.new(source: source, decls: decls, applied: [], skipped: [], left_unreadable: [],
|
|
310
|
+
replaced: [])
|
|
297
311
|
merge_candidates(state, candidates)
|
|
298
312
|
|
|
299
313
|
action = state.applied.empty? ? :noop : :updated
|
|
300
314
|
unless action == :updated
|
|
301
315
|
return WriteResult.new(source_path: source_path, target_path: target, action: action,
|
|
302
|
-
applied: state.applied, skipped: state.skipped
|
|
316
|
+
applied: state.applied, skipped: state.skipped,
|
|
317
|
+
left_unreadable: state.left_unreadable)
|
|
303
318
|
end
|
|
304
319
|
|
|
305
320
|
# The merge splices text into an existing file by byte offset, so a bug here can produce a file neither
|
|
@@ -312,9 +327,10 @@ module Rigor
|
|
|
312
327
|
action: :skipped_invalid_rbs, error: error)
|
|
313
328
|
end
|
|
314
329
|
|
|
315
|
-
target.write(state.source)
|
|
330
|
+
target.write(state.source) unless @dry_run
|
|
316
331
|
WriteResult.new(source_path: source_path, target_path: target,
|
|
317
|
-
action: action, applied: state.applied, skipped: state.skipped
|
|
332
|
+
action: action, applied: state.applied, skipped: state.skipped,
|
|
333
|
+
left_unreadable: state.left_unreadable, replaced: state.replaced)
|
|
318
334
|
end
|
|
319
335
|
|
|
320
336
|
# Applies every class group, then every requested shell, then normalises the layout the three steps
|
|
@@ -546,7 +562,7 @@ module Rigor
|
|
|
546
562
|
state.applied.concat(methods)
|
|
547
563
|
insert_namespace_chain(state, class_name, kinds, supers, methods)
|
|
548
564
|
else
|
|
549
|
-
state.source = merge_into_existing_class(state.source, decl, methods, state
|
|
565
|
+
state.source = merge_into_existing_class(state.source, decl, methods, state)
|
|
550
566
|
state.decls = parse_signature(state.source) || state.decls
|
|
551
567
|
end
|
|
552
568
|
end
|
|
@@ -577,24 +593,29 @@ module Rigor
|
|
|
577
593
|
source.end_with?("\n")
|
|
578
594
|
end
|
|
579
595
|
|
|
580
|
-
def merge_into_existing_class(source, decl, methods,
|
|
596
|
+
def merge_into_existing_class(source, decl, methods, state)
|
|
581
597
|
existing_pairs = collect_member_pairs(decl)
|
|
582
598
|
new_methods, conflicting = partition_against_existing(methods, existing_pairs)
|
|
583
599
|
|
|
584
600
|
source = insert_into_class(source, decl, new_methods)
|
|
585
|
-
applied.concat(new_methods)
|
|
601
|
+
state.applied.concat(new_methods)
|
|
586
602
|
|
|
587
603
|
if @overwrite
|
|
588
|
-
source, replaced = replace_eligible_conflicts(source, decl, conflicting)
|
|
589
|
-
applied.concat(replaced)
|
|
590
|
-
|
|
604
|
+
source, replaced = replace_eligible_conflicts(source, decl, conflicting, state)
|
|
605
|
+
state.applied.concat(replaced)
|
|
606
|
+
state.replaced.concat(replaced)
|
|
607
|
+
state.skipped.concat(conflicting.reject { |c| replaced.include?(c) }.map { |c| [c, :user_authored] })
|
|
591
608
|
else
|
|
592
|
-
skipped.concat(conflicting.map { |c| [c, :user_authored] })
|
|
609
|
+
state.skipped.concat(conflicting.map { |c| [c, :user_authored] })
|
|
593
610
|
end
|
|
594
611
|
|
|
595
612
|
source
|
|
596
613
|
end
|
|
597
614
|
|
|
615
|
+
def inline_overwrite?(candidate)
|
|
616
|
+
candidate.classification == Classification::INLINE_OVERWRITE
|
|
617
|
+
end
|
|
618
|
+
|
|
598
619
|
# Returns a list of `[method_name (Symbol), kind (Symbol)]` pairs for every method-like member in the
|
|
599
620
|
# declaration. ADR-14 slice 4 recognises `MethodDefinition`'s `:instance` / `:singleton` kind plus the
|
|
600
621
|
# three `attr_*` declaration kinds so a source-side `attr_reader :name` and an RBS-side
|
|
@@ -630,7 +651,8 @@ module Rigor
|
|
|
630
651
|
return source if new_methods.empty?
|
|
631
652
|
|
|
632
653
|
indent = INDENT * member_indent_depth(decl)
|
|
633
|
-
|
|
654
|
+
lines = new_methods.flat_map { |c| c.rbs_lines.map { |line| "#{indent}#{line}\n" } }.join
|
|
655
|
+
insert_before_end(source, decl, lines)
|
|
634
656
|
end
|
|
635
657
|
|
|
636
658
|
# Walks the class's existing method declarations; for each replaceable candidate that matches a member
|
|
@@ -645,21 +667,34 @@ module Rigor
|
|
|
645
667
|
# canonical case is `initialize_stub_candidate`, which bypasses the existing-RBS comparison and always
|
|
646
668
|
# classifies as `NEW_METHOD` — when sig-gen's `--params=observed` upgrades a `(path: untyped) -> void`
|
|
647
669
|
# declaration to `(path: String) -> void` we want `--overwrite` to apply it.
|
|
648
|
-
def replace_eligible_conflicts(source, decl, candidates)
|
|
670
|
+
def replace_eligible_conflicts(source, decl, candidates, state)
|
|
649
671
|
eligible = candidates.select { |c| eligible_for_replacement?(c, decl, source) }
|
|
650
672
|
return [source, []] if eligible.empty?
|
|
651
673
|
|
|
652
674
|
replaced = []
|
|
675
|
+
done = {}
|
|
653
676
|
# Apply replacements from highest byte position downward so earlier byte offsets remain valid as the
|
|
654
677
|
# source grows or shrinks.
|
|
655
|
-
sorted = eligible.sort_by { |c| -member_position(decl, c
|
|
678
|
+
sorted = eligible.sort_by { |c| -member_position(decl, c) }
|
|
656
679
|
sorted.each do |candidate|
|
|
657
|
-
|
|
680
|
+
member = replaceable_member(decl, candidate)
|
|
681
|
+
# An `attr_accessor` answers for two methods; its one replacement covers both candidates.
|
|
682
|
+
next replaced << candidate if member && done[member.location.start_pos]
|
|
683
|
+
|
|
684
|
+
result = apply_replacement(source, decl, candidate, state)
|
|
685
|
+
next if result.nil?
|
|
686
|
+
|
|
687
|
+
done[member.location.start_pos] = true
|
|
688
|
+
source = result
|
|
689
|
+
replaced << candidate
|
|
658
690
|
end
|
|
659
691
|
[source, replaced]
|
|
660
692
|
end
|
|
661
693
|
|
|
662
694
|
def eligible_for_replacement?(candidate, decl, source)
|
|
695
|
+
return true if inline_overwrite?(candidate)
|
|
696
|
+
return false if replaces_declared_void?(candidate, decl)
|
|
697
|
+
|
|
663
698
|
case candidate.classification
|
|
664
699
|
when Classification::TIGHTER_RETURN then true
|
|
665
700
|
when Classification::NEW_METHOD then tightens_untyped?(candidate, decl, source)
|
|
@@ -667,27 +702,56 @@ module Rigor
|
|
|
667
702
|
end
|
|
668
703
|
end
|
|
669
704
|
|
|
670
|
-
#
|
|
671
|
-
#
|
|
672
|
-
#
|
|
673
|
-
#
|
|
674
|
-
|
|
705
|
+
# Issue #836 — `--overwrite` never turns a declared `-> void` into a value return. The generator no
|
|
706
|
+
# longer proposes one (`Generator#declares_void?` classifies the method `equivalent`), but the writer
|
|
707
|
+
# reaches the same member through case 2 above whenever the RBS environment did not resolve the `def` to
|
|
708
|
+
# this declaration: an observed-parameter upgrade of `(untyped m) -> void` to `(Module m) -> Registry`
|
|
709
|
+
# counts one `untyped` fewer and would be applied, erasing the author's statement that the return is not
|
|
710
|
+
# part of the contract. A candidate that keeps `void` is still eligible, which is what case 2 is for —
|
|
711
|
+
# the `initialize` stub is always spelled `-> void`.
|
|
712
|
+
def replaces_declared_void?(candidate, decl)
|
|
675
713
|
member = find_method_member(decl, candidate.method_name, candidate.kind)
|
|
676
714
|
return false if member.nil?
|
|
677
715
|
|
|
678
|
-
|
|
679
|
-
|
|
716
|
+
declared_void = member.overloads.any? do |overload|
|
|
717
|
+
overload.method_type.type.return_type.is_a?(RBS::Types::Bases::Void)
|
|
718
|
+
end
|
|
719
|
+
declared_void && !candidate.rbs.to_s.end_with?(" -> void")
|
|
680
720
|
end
|
|
681
721
|
|
|
682
|
-
|
|
683
|
-
|
|
722
|
+
# Compares the existing member's source-side RBS text against the candidate's proposed RBS text
|
|
723
|
+
# ({.fewer_untyped?}).
|
|
724
|
+
def tightens_untyped?(candidate, decl, source)
|
|
725
|
+
member = find_method_member(decl, candidate.method_name, candidate.kind)
|
|
726
|
+
return false if member.nil?
|
|
727
|
+
|
|
728
|
+
self.class.fewer_untyped?(candidate.rbs, source[member.location.start_pos...member.location.end_pos])
|
|
684
729
|
end
|
|
685
730
|
|
|
686
|
-
def member_position(decl,
|
|
687
|
-
member =
|
|
731
|
+
def member_position(decl, candidate)
|
|
732
|
+
member = replaceable_member(decl, candidate)
|
|
688
733
|
member ? member.location.start_pos : -1
|
|
689
734
|
end
|
|
690
735
|
|
|
736
|
+
# The member a replacement overwrites. An inline overwrite may also land on an `attr_*` declaration of
|
|
737
|
+
# the same method (ADR-112 WD4), which it replaces in the inline attribute's own spelling; an
|
|
738
|
+
# `overloading?` member (`| ...`) is never its target.
|
|
739
|
+
def replaceable_member(decl, candidate)
|
|
740
|
+
member = find_method_member(decl, candidate.method_name, candidate.kind)
|
|
741
|
+
return member unless candidate.classification == Classification::INLINE_OVERWRITE
|
|
742
|
+
|
|
743
|
+
member = nil if member.respond_to?(:overloading?) && member.overloading?
|
|
744
|
+
member || find_attr_member(decl, candidate.method_name, candidate.kind)
|
|
745
|
+
end
|
|
746
|
+
|
|
747
|
+
def find_attr_member(decl, method_name, kind)
|
|
748
|
+
decl.members.find do |m|
|
|
749
|
+
pairs = []
|
|
750
|
+
collect_pairs_for_member(m, pairs) unless m.is_a?(RBS::AST::Members::MethodDefinition)
|
|
751
|
+
m.respond_to?(:kind) && m.kind == kind && pairs.any? { |name, _| name == method_name }
|
|
752
|
+
end
|
|
753
|
+
end
|
|
754
|
+
|
|
691
755
|
def find_method_member(decl, method_name, kind)
|
|
692
756
|
decl.members.find do |m|
|
|
693
757
|
m.is_a?(RBS::AST::Members::MethodDefinition) && m.name == method_name && m.kind == kind
|
|
@@ -697,12 +761,96 @@ module Rigor
|
|
|
697
761
|
# Splices the new RBS one-liner over the existing declaration's byte range. `RBS::Parser`'s location
|
|
698
762
|
# starts at the `def` keyword, NOT at the column zero of the line, so the leading whitespace stays inside
|
|
699
763
|
# `source[0...start_pos]` and we do not re-emit it.
|
|
700
|
-
def apply_replacement(source, decl, candidate)
|
|
701
|
-
member =
|
|
764
|
+
def apply_replacement(source, decl, candidate, state)
|
|
765
|
+
member = replaceable_member(decl, candidate)
|
|
702
766
|
return nil if member.nil?
|
|
703
767
|
|
|
704
768
|
loc = member.location
|
|
705
|
-
source[0...loc.start_pos] + candidate.rbs + source[loc.end_pos..]
|
|
769
|
+
replaced = source[0...loc.start_pos] + kept_comments(source, loc) + candidate.rbs + source[loc.end_pos..]
|
|
770
|
+
# Both splices insert above the member, the effect annotation at its own line and the declared ones at
|
|
771
|
+
# its first annotation's, so running the lower insertion first keeps the positions the parse gave valid.
|
|
772
|
+
replaced = splice_annotations(replaced, member, candidate, state)
|
|
773
|
+
splice_declared_annotations(replaced, member, candidate)
|
|
774
|
+
end
|
|
775
|
+
|
|
776
|
+
# A member written across lines (`def f: () -> A` / ` # why` / ` | (B) -> C`) can hold comments inside its
|
|
777
|
+
# own location, which the splice above would delete with the text it replaces. Each such comment line is
|
|
778
|
+
# kept, moved above the new line at the member's indentation. Only whole comment lines are recognised: RBS
|
|
779
|
+
# has no string type that spans a line, so a line whose first non-blank character is `#` is a comment.
|
|
780
|
+
def kept_comments(source, loc)
|
|
781
|
+
text = source[loc.start_pos...loc.end_pos].to_s
|
|
782
|
+
return "" unless text.include?("\n")
|
|
783
|
+
|
|
784
|
+
indent = source[line_start_index(source, loc.start_pos)...loc.start_pos].to_s
|
|
785
|
+
indent = "" unless indent.match?(/\A[ \t]*\z/)
|
|
786
|
+
text.lines.drop(1).filter_map { |line| line.strip if line.match?(/\A\s*#/) }
|
|
787
|
+
.map { |comment| "#{comment}\n#{indent}" }.join
|
|
788
|
+
end
|
|
789
|
+
|
|
790
|
+
# ADR-112 WD4 — the annotations an inline declaration carries that its `sig/` copy lacks, added above the
|
|
791
|
+
# member. Additive only: an annotation already there stays, whoever wrote it, so a copy never loses
|
|
792
|
+
# anything to regeneration (an annotation later deleted inline is therefore not deleted here).
|
|
793
|
+
def splice_declared_annotations(source, member, candidate)
|
|
794
|
+
missing = missing_declared_annotations(member, candidate)
|
|
795
|
+
return source if missing.empty?
|
|
796
|
+
|
|
797
|
+
line_start = line_start_index(source, member.location.start_pos)
|
|
798
|
+
indent = source[line_start...member.location.start_pos].to_s
|
|
799
|
+
return source unless indent.match?(/\A[ \t]*\z/)
|
|
800
|
+
|
|
801
|
+
anchor = annotation_block_start(source, member, line_start)
|
|
802
|
+
source[0...anchor] + missing.map { |line| "#{indent}#{line}\n" }.join + source[anchor..]
|
|
803
|
+
end
|
|
804
|
+
|
|
805
|
+
# A declared annotation line is `%a` plus a one-character delimiter pair around its content.
|
|
806
|
+
def missing_declared_annotations(member, candidate)
|
|
807
|
+
present = member.annotations.map { |annotation| annotation.string.to_s.strip }
|
|
808
|
+
candidate.declared_annotations.reject { |line| present.include?(line[3...-1].to_s.strip) }
|
|
809
|
+
end
|
|
810
|
+
|
|
811
|
+
# The start of the line the member's first annotation sits on, or of the member's own line.
|
|
812
|
+
def annotation_block_start(source, member, line_start)
|
|
813
|
+
starts = member.annotations.filter_map { |annotation| annotation.location&.start_pos }
|
|
814
|
+
([line_start] + starts.map { |pos| line_start_index(source, pos) }).min
|
|
815
|
+
end
|
|
816
|
+
|
|
817
|
+
# ADR-103 WD9 — the annotation half of a replacement, and the one place this slice is allowed to
|
|
818
|
+
# write bytes ABOVE an existing declaration.
|
|
819
|
+
#
|
|
820
|
+
# `RBS::Parser` puts a member's annotations OUTSIDE its `location`, so {#apply_replacement} above
|
|
821
|
+
# already leaves every annotation on the declaration byte-untouched — the `%a{deprecated}` and the
|
|
822
|
+
# `%a{implicitly-returns-nil}` a user wrote survive an `--overwrite` unchanged. What is left is the
|
|
823
|
+
# question of adding ours, and the answer is: only onto a declaration that carries none.
|
|
824
|
+
#
|
|
825
|
+
# A declaration that already carries an annotation is left exactly as it is, and the candidate is
|
|
826
|
+
# reported as `sig.effect.left-unreadable`. The writer cannot tell an annotation it authored from
|
|
827
|
+
# one the user wrote, and it has no grammar for merging two — so rewriting the region could silently
|
|
828
|
+
# replace an author's own `%a{pure}` with a labelled envelope, or strip a directive Rigor does not
|
|
829
|
+
# read at all. Leaving it and saying so is the only move that cannot destroy an authored fact.
|
|
830
|
+
#
|
|
831
|
+
# An annotation on one of the member's overloads (`def m: %a{pure} (Integer) -> String`) counts too:
|
|
832
|
+
# it sits inside the location, and a tighter return keeps it (#1436), so ours would be a second one.
|
|
833
|
+
def splice_annotations(source, member, candidate, state)
|
|
834
|
+
return source if candidate.annotations.empty?
|
|
835
|
+
|
|
836
|
+
if annotated?(member)
|
|
837
|
+
# Rebuilt so the reported row carries the reason it was actually given. The generator stamped
|
|
838
|
+
# `:emitted` when it decided the annotation; only the writer knows the declaration refused it.
|
|
839
|
+
state.left_unreadable << candidate.with_effect_annotation(candidate.annotations, :left_unreadable)
|
|
840
|
+
return source
|
|
841
|
+
end
|
|
842
|
+
|
|
843
|
+
line_start = line_start_index(source, member.location.start_pos)
|
|
844
|
+
indent = source[line_start...member.location.start_pos].to_s
|
|
845
|
+
return source unless indent.match?(/\A[ \t]*\z/)
|
|
846
|
+
|
|
847
|
+
source[0...line_start] + candidate.annotations.map { |line| "#{indent}#{line}\n" }.join + source[line_start..]
|
|
848
|
+
end
|
|
849
|
+
|
|
850
|
+
def annotated?(member)
|
|
851
|
+
return true unless member.annotations.empty?
|
|
852
|
+
|
|
853
|
+
member.respond_to?(:overloads) && member.overloads.any? { |overload| overload.annotations.any? }
|
|
706
854
|
end
|
|
707
855
|
end
|
|
708
856
|
end
|
data/lib/rigor/sig_gen.rb
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "sig_gen/alias_index"
|
|
3
4
|
require_relative "sig_gen/classification"
|
|
5
|
+
require_relative "sig_gen/effect_annotation"
|
|
6
|
+
require_relative "sig_gen/inline_declarations"
|
|
4
7
|
require_relative "sig_gen/method_candidate"
|
|
5
8
|
require_relative "sig_gen/observed_call"
|
|
6
9
|
require_relative "sig_gen/meta_class_shape"
|