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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "rigor/plugin"
|
|
4
|
+
require_relative "rbs_inline/same_line_annotations"
|
|
4
5
|
|
|
5
6
|
# ADR-32 — bundled `rigor-rbs-inline` plugin.
|
|
6
7
|
#
|
|
@@ -61,9 +62,45 @@ module Rigor
|
|
|
61
62
|
|
|
62
63
|
COMMENT_LINE = /\A\s*#/
|
|
63
64
|
|
|
64
|
-
|
|
65
|
+
# The RBS annotation the synthesizer writes on every member whose type upstream DEFAULTED rather
|
|
66
|
+
# than read off an annotation (issue #823). Its meaning is normative in
|
|
67
|
+
# `docs/type-specification/rbs-extended.md`: the declaration states the member's presence and its
|
|
68
|
+
# parameters, and says nothing about what it returns, so the engine infers the return from the body
|
|
69
|
+
# instead of adopting the placeholder. `Rigor::RbsExtended.inferred_return?` is the reader.
|
|
70
|
+
INFERRED_RETURN_ANNOTATION = "rigor:v1:inferred-return"
|
|
65
71
|
|
|
66
|
-
#
|
|
72
|
+
# The RBS annotation the synthesizer writes on every member where upstream defaulted EVERY type
|
|
73
|
+
# slot — every parameter and the return alike — because the author's comment block asserted nothing
|
|
74
|
+
# about this member's own contract (issue #991). This is a strictly narrower condition than
|
|
75
|
+
# {INFERRED_RETURN_ANNOTATION}: that one fires whenever the return alone defaulted, which is also
|
|
76
|
+
# true of a member whose parameters WERE annotated (`# @rbs num: Float` with no `# @rbs return:`).
|
|
77
|
+
# This annotation distinguishes "nobody said anything about this member" from "the return half was
|
|
78
|
+
# left to inference" — the two collapse under return-only provenance, because no equivalent
|
|
79
|
+
# per-parameter survivor exists to separate them (see {DEFAULTED_TYPE_NAME}), so this constant
|
|
80
|
+
# exists precisely to carry that missing half. Its meaning is normative in
|
|
81
|
+
# `docs/type-specification/rbs-extended.md`. `Rigor::RbsExtended.inferred_signature?` is the reader.
|
|
82
|
+
INFERRED_SIGNATURE_ANNOTATION = "rigor:v1:inferred-signature"
|
|
83
|
+
|
|
84
|
+
# The stand-in type handed to upstream's `Writer#default_type`, and the whole reason this plugin can
|
|
85
|
+
# tell a defaulted type from an authored one. `RBS::Inline::Writer` substitutes `default_type`
|
|
86
|
+
# wherever the author wrote nothing — `return_type || default_type` in `RubyDef#method_overloads`,
|
|
87
|
+
# `attribute_type || default_type` in `RubyAttr#rbs` — and leaves an authored type alone. Rendering
|
|
88
|
+
# with a distinctive stand-in therefore makes "the author did not write this" a fact in the output
|
|
89
|
+
# rather than a guess about it: a member whose return renders as this name was defaulted, and one
|
|
90
|
+
# that renders `untyped` was WRITTEN `untyped` by its author (`#: (String) -> untyped`), which is a
|
|
91
|
+
# real contract and stays one.
|
|
92
|
+
#
|
|
93
|
+
# `default_type` is upstream's own public accessor for exactly this substitution, and `Writer.write`
|
|
94
|
+
# yields the writer to configure it, so nothing here reaches into upstream internals. The stand-in
|
|
95
|
+
# never survives into the contributed RBS — {#mark_inferred_returns} rewrites every occurrence back
|
|
96
|
+
# to `untyped` — which is not cosmetic: an undeclared type alias makes `RBS::DefinitionBuilder` raise
|
|
97
|
+
# `NoTypeFoundError` for the whole class, the same failure ADR-93 WD4's Finding 5 measured on
|
|
98
|
+
# `#:nodoc:`.
|
|
99
|
+
DEFAULTED_TYPE_NAME = :rigor__inline_defaulted
|
|
100
|
+
|
|
101
|
+
private_constant :ECHOED_ANNOTATION, :DECLARATION_LINE, :COMMENT_LINE, :DEFAULTED_TYPE_NAME
|
|
102
|
+
|
|
103
|
+
# @param require_magic_comment — when `false` (the default since ADR-93 WD1), the magic
|
|
67
104
|
# comment is not required and the file is processed only if it actually carries an annotation — see
|
|
68
105
|
# {#annotated?}. When `true` (the old ADR-32 WD2 gate), only files opening with
|
|
69
106
|
# `# rbs_inline: enabled` are processed, and upstream's opt-in semantics apply verbatim.
|
|
@@ -95,7 +132,11 @@ module Rigor
|
|
|
95
132
|
return nil if parsed.nil?
|
|
96
133
|
|
|
97
134
|
uses, decls, rbs_decls = parsed
|
|
98
|
-
|
|
135
|
+
# Issue #998 — before the writer reads the annotation lists, so a same-line `%a{…} () -> T` renders
|
|
136
|
+
# with its method type and its annotation both attached. See {SameLineAnnotations}.
|
|
137
|
+
SameLineAnnotations.each_parsing_result(decls) { |comments| SameLineAnnotations.split!(comments) }
|
|
138
|
+
rendered = mark_inferred_returns(render_with_defaulted_marker(uses, decls, rbs_decls))
|
|
139
|
+
rendered = reattach_declaration_annotations(rendered)
|
|
99
140
|
return nil if rendered.nil? || rendered.strip.empty?
|
|
100
141
|
|
|
101
142
|
notices = unhonoured_annotations(result)
|
|
@@ -109,6 +150,147 @@ module Rigor
|
|
|
109
150
|
|
|
110
151
|
private
|
|
111
152
|
|
|
153
|
+
# Upstream's writer, told to substitute {DEFAULTED_TYPE_NAME} instead of `untyped` wherever the
|
|
154
|
+
# author wrote no type. See that constant for why the distinction is worth a stand-in.
|
|
155
|
+
def render_with_defaulted_marker(uses, decls, rbs_decls)
|
|
156
|
+
marker = ::RBS::Types::Alias.new(
|
|
157
|
+
name: ::RBS::TypeName.new(namespace: ::RBS::Namespace.empty, name: DEFAULTED_TYPE_NAME),
|
|
158
|
+
args: [],
|
|
159
|
+
location: nil
|
|
160
|
+
)
|
|
161
|
+
::RBS::Inline::Writer.write(uses, decls, rbs_decls) { |writer| writer.default_type = marker }
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Issue #823 — the sibling rule. ADR-93 WD1 gates synthesis on a file that carries an annotation;
|
|
165
|
+
# inside such a file upstream emits a full `def f: (untyped x) -> untyped` skeleton for every
|
|
166
|
+
# unannotated `def`, and Rigor trusts an accepted signature over body inference. So one `# @rbs`
|
|
167
|
+
# anywhere in a file used to retype every OTHER method in it to `untyped` — the same mechanism
|
|
168
|
+
# ADR-93 WD1 measured project-wide before the file gate landed (mail 26 → 42 diagnostics), just
|
|
169
|
+
# scoped to the annotated file. An annotation on one method must not change the typing of its
|
|
170
|
+
# siblings in either direction.
|
|
171
|
+
#
|
|
172
|
+
# The skeleton is kept — that is what PR #779's member-level drop got wrong, because a partially
|
|
173
|
+
# declared class reads to RBS as a fully declared one (32 `call.undefined-method` and 12
|
|
174
|
+
# `call.wrong-arity` on `new`, and 44 classes to `Dynamic[top]` behind cross-file references that
|
|
175
|
+
# stopped resolving). What changes is what the skeleton CLAIMS: a defaulted type slot is rewritten
|
|
176
|
+
# back to `untyped` and its member is annotated {INFERRED_RETURN_ANNOTATION}, so the declaration
|
|
177
|
+
# keeps the class's full method surface, `new`'s arity and every cross-file name, and states nothing
|
|
178
|
+
# about the return. The engine reads the annotation in `RbsDispatch` and falls through to the same
|
|
179
|
+
# body-inference tier an undeclared method takes.
|
|
180
|
+
#
|
|
181
|
+
# A member additionally carries {INFERRED_SIGNATURE_ANNOTATION} when EVERY type slot on it —
|
|
182
|
+
# not only the return — defaulted: a fully bare `def`, or one whose only annotation lives on a
|
|
183
|
+
# sibling member the file also happens to carry. A member with a parameter annotated (`# @rbs num:
|
|
184
|
+
# Float`) but no `# @rbs return:` still gets {INFERRED_RETURN_ANNOTATION} (the return did default),
|
|
185
|
+
# but NOT this one — the author asserted something about the member, just not its return. Issue
|
|
186
|
+
# #991's `call.wrong-arity` reads this one rather than the return-only annotation, because the
|
|
187
|
+
# arity question is about the parameter list, and the parameter list is exactly what tells the two
|
|
188
|
+
# cases apart.
|
|
189
|
+
#
|
|
190
|
+
# Two properties make the rewrite safe rather than clever:
|
|
191
|
+
#
|
|
192
|
+
# - the stand-in is a token this plugin injected, so replacing it is exact — it can only appear
|
|
193
|
+
# where upstream substituted it (a comment echoing the same word would merely read `untyped`);
|
|
194
|
+
# - the lines to annotate come from `RBS::Parser` reading the writer's own output, not from a
|
|
195
|
+
# pattern over it, and the replacement is length-preserving per line, so the parser's line numbers
|
|
196
|
+
# still address the same members after it.
|
|
197
|
+
#
|
|
198
|
+
# Marking is best-effort and the rewrite is not: if the probe parse is skipped or fails, the file
|
|
199
|
+
# still ships with today's `untyped` skeleton rather than an unresolvable alias (see
|
|
200
|
+
# {DEFAULTED_TYPE_NAME}). That asymmetry is why the encoding guard is shaped the way it is. Invalid
|
|
201
|
+
# UTF-8 does not reach here today — the RDoc-directive scan above raises on it first, and {#call}'s
|
|
202
|
+
# rescue routes the file to WD6 — but the guarded parse is the one `RbsLoader.add_virtual_rbs`
|
|
203
|
+
# documents as HANG-prone on pre-4.1 rbs lexers, and a hang escapes every rescue there is. So the
|
|
204
|
+
# probe is skipped for a byte sequence it could hang on, while the substitution runs over bytes and
|
|
205
|
+
# therefore always completes.
|
|
206
|
+
def mark_inferred_returns(rendered)
|
|
207
|
+
return rendered if rendered.nil? || !rendered.b.include?(DEFAULTED_TYPE_NAME.to_s)
|
|
208
|
+
|
|
209
|
+
return_lines, signature_lines =
|
|
210
|
+
rendered.valid_encoding? ? defaulted_member_lines(rendered) : [Set.new, Set.new]
|
|
211
|
+
scrubbed = rendered.b.gsub(DEFAULTED_TYPE_NAME.to_s, "untyped").force_encoding(rendered.encoding)
|
|
212
|
+
return scrubbed if return_lines.empty?
|
|
213
|
+
|
|
214
|
+
scrubbed.lines.each_with_index.flat_map do |line, index|
|
|
215
|
+
line_number = index + 1
|
|
216
|
+
next line unless return_lines.include?(line_number)
|
|
217
|
+
|
|
218
|
+
indent = line[/\A[ \t]*/]
|
|
219
|
+
annotations = ["#{indent}%a{#{INFERRED_RETURN_ANNOTATION}}\n"]
|
|
220
|
+
annotations << "#{indent}%a{#{INFERRED_SIGNATURE_ANNOTATION}}\n" if signature_lines.include?(line_number)
|
|
221
|
+
[*annotations, line]
|
|
222
|
+
end.join
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# The 1-based lines of every member in `rendered` whose return type (or, for an attribute, whose
|
|
226
|
+
# type) is the defaulted stand-in — and, of those, the subset where EVERY type slot on the member
|
|
227
|
+
# defaulted, not only the return. A member's `location.start_line` is the line its `def` /
|
|
228
|
+
# `attr_*` keyword sits on, which is where the annotation has to go. Returns `[return_lines,
|
|
229
|
+
# signature_lines]`; `signature_lines` is always a subset of `return_lines`, since a member cannot
|
|
230
|
+
# have every slot defaulted without its return being one of them.
|
|
231
|
+
def defaulted_member_lines(rendered)
|
|
232
|
+
buffer = ::RBS::Buffer.new(name: "(rigor: inline defaulted-type probe)", content: rendered)
|
|
233
|
+
_, _directives, decls = ::RBS::Parser.parse_signature(buffer)
|
|
234
|
+
collect_defaulted_member_lines(decls, Set.new, Set.new)
|
|
235
|
+
rescue ::StandardError
|
|
236
|
+
# The writer's own output failing to parse is a condition the loader already handles (ADR-32 WD6
|
|
237
|
+
# fail-soft drops the entry). Declining to mark here keeps that the only failure.
|
|
238
|
+
[Set.new, Set.new]
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def collect_defaulted_member_lines(nodes, return_lines, signature_lines)
|
|
242
|
+
nodes.each do |node|
|
|
243
|
+
next unless node.respond_to?(:members)
|
|
244
|
+
|
|
245
|
+
node.members.each do |member|
|
|
246
|
+
classify_defaulted_member(member, return_lines, signature_lines)
|
|
247
|
+
end
|
|
248
|
+
collect_defaulted_member_lines(
|
|
249
|
+
node.members.grep(::RBS::AST::Declarations::Base), return_lines, signature_lines
|
|
250
|
+
)
|
|
251
|
+
end
|
|
252
|
+
[return_lines, signature_lines]
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def classify_defaulted_member(member, return_lines, signature_lines)
|
|
256
|
+
case member
|
|
257
|
+
when ::RBS::AST::Members::MethodDefinition
|
|
258
|
+
return unless member.overloads.any? { |o| defaulted_type?(o.method_type.type.return_type) }
|
|
259
|
+
|
|
260
|
+
line = member.location&.start_line
|
|
261
|
+
return if line.nil?
|
|
262
|
+
|
|
263
|
+
return_lines << line
|
|
264
|
+
signature_lines << line if member.overloads.all? { |o| fully_defaulted_overload?(o) }
|
|
265
|
+
when ::RBS::AST::Members::Attribute
|
|
266
|
+
return unless defaulted_type?(member.type)
|
|
267
|
+
|
|
268
|
+
line = member.location&.start_line
|
|
269
|
+
return if line.nil?
|
|
270
|
+
|
|
271
|
+
# An attribute has exactly one type slot (the value type, shared by its reader and writer
|
|
272
|
+
# overloads), so "the type defaulted" and "every type slot defaulted" are the same fact.
|
|
273
|
+
return_lines << line
|
|
274
|
+
signature_lines << line
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# True when every parameter type and the return type of `overload` — everything
|
|
279
|
+
# `RBS::Types::Function#each_type` yields — is the defaulted stand-in, i.e. the author's comment
|
|
280
|
+
# block asserted nothing about this overload's own contract. Block-type provenance is
|
|
281
|
+
# deliberately not part of this: a block parameter's shape does not bear on the positional arity
|
|
282
|
+
# #991 exists to check, and upstream defaults it independently of the parameter list either way.
|
|
283
|
+
def fully_defaulted_overload?(overload)
|
|
284
|
+
function = overload.method_type.type
|
|
285
|
+
return false unless function.respond_to?(:each_type)
|
|
286
|
+
|
|
287
|
+
function.each_type.all? { |type| defaulted_type?(type) }
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def defaulted_type?(type)
|
|
291
|
+
type.is_a?(::RBS::Types::Alias) && type.name.name == DEFAULTED_TYPE_NAME
|
|
292
|
+
end
|
|
293
|
+
|
|
112
294
|
# Re-attaches a class- or module-level `%a{…}` that upstream's writer dropped (#452).
|
|
113
295
|
#
|
|
114
296
|
# `RBS::Inline::Writer` emits a member's annotations as real annotation lines and a *declaration's*
|
|
@@ -175,29 +357,153 @@ module Rigor
|
|
|
175
357
|
# These are invisible without a report: synthesis succeeds, and the annotation comment is even echoed
|
|
176
358
|
# into the generated RBS, so the omission shows up neither in the output nor at runtime.
|
|
177
359
|
#
|
|
178
|
-
#
|
|
179
|
-
# own `docs/inline.md` documents `# @rbs module-self: Foo`; the rbs-inline gem's grammar is
|
|
180
|
-
# `# @rbs module-self Foo`, without the colon. Handed the colon form the gem still builds a
|
|
181
|
-
# `ModuleSelf` annotation but extracts no types from it, so an empty `self_types` on a parsed
|
|
182
|
-
# annotation is a precise signature for "the author asked for a constraint we did not apply".
|
|
183
|
-
# Measured both ways in `docs/notes/20260730-inline-rbs-parser-grammar-diff.md`.
|
|
360
|
+
# Five cases today:
|
|
184
361
|
#
|
|
185
|
-
#
|
|
186
|
-
#
|
|
187
|
-
#
|
|
362
|
+
# - `module-self`, where the two inline-RBS dialects disagree on spelling. rbs's own `docs/inline.md`
|
|
363
|
+
# documents `# @rbs module-self: Foo`; the rbs-inline gem's grammar is `# @rbs module-self Foo`,
|
|
364
|
+
# without the colon. Handed the colon form the gem still builds a `ModuleSelf` annotation but
|
|
365
|
+
# extracts no types from it, so an empty `self_types` on a parsed annotation is a precise signature
|
|
366
|
+
# for "the author asked for a constraint we did not apply". Measured both ways in
|
|
367
|
+
# `docs/notes/20260730-inline-rbs-parser-grammar-diff.md`.
|
|
368
|
+
# - a `#:` line whose type does not parse (issue #997). Upstream's own tolerant parse
|
|
369
|
+
# ({#parse_type_method_type} in its `annotation_parser.rb`) never raises on this: it consumes the
|
|
370
|
+
# rest of the line into a `SyntaxErrorAssertion` and moves on, so nothing upstream ever surfaces
|
|
371
|
+
# the failure. Measured at 568138c2: `#: (finite-float) -> String` above a `def` synthesized no
|
|
372
|
+
# signature for it AT ALL, and `rigor check` on the file reported nothing beyond an unrelated
|
|
373
|
+
# `rbs.coverage.missing-gem` info — the annotation was indistinguishable from one never written,
|
|
374
|
+
# which ADR-93's "an inline annotation is a live contract" forbids.
|
|
375
|
+
# - an `# @rbs %a{…}` line whose text after the annotations is not a method type (issue #998). The
|
|
376
|
+
# gem's `@rbs %a{}` rule lexes the annotations and ignores the rest of the line, so
|
|
377
|
+
# `# @rbs %a{pure} (finite-float) -> String` keeps `%a{pure}` and discards the signature beside it
|
|
378
|
+
# without a word. A remainder that IS a method type is not reported, because
|
|
379
|
+
# {SameLineAnnotations.split!} has already made it bind — the notice is for what that repair
|
|
380
|
+
# could not read.
|
|
381
|
+
# - an `# @rbs name: T` parameter (or local-variable) annotation whose `T` does not parse (issue
|
|
382
|
+
# #1019, row H). The gem's grammar makes the colon-and-type optional — {#parse_var_decl} always
|
|
383
|
+
# builds a `VarType`, even when `parse_type` fell through — so a malformed type leaves `name`
|
|
384
|
+
# present and `type` `nil` rather than raising: `# @rbs n: Integer[1..10]` names `n` and leaves its
|
|
385
|
+
# type unset. `Writer#default_type` then fills the empty slot the same way an UNANNOTATED parameter
|
|
386
|
+
# is filled, so `VarType#type.nil?` is indistinguishable downstream from "the author wrote nothing"
|
|
387
|
+
# without this check — the same silent-drop shape #997 closed for `#:` lines and `return:`, now
|
|
388
|
+
# closed for `name:`.
|
|
389
|
+
# - an `@rbs`-prefixed comment paragraph whose body is not one of the gem's known annotation shapes
|
|
390
|
+
# (issue #1019, row B). {RBS::Inline::AnnotationParser#annotation_comment?} matches a `\b` word
|
|
391
|
+
# boundary right after `@rbs`, so `# @rbs-ext …` is inside its net — the gem treats it as an `@rbs`
|
|
392
|
+
# annotation attempt — but {RBS::Inline::AnnotationParser#parse_annotation}'s `case` has no branch
|
|
393
|
+
# for what follows `-ext`, falls through with no `else`, and returns `nil`. `AnnotationParser#parse`
|
|
394
|
+
# then folds the whole paragraph back into a `CommentLines` — the SAME class an ordinary comment
|
|
395
|
+
# parses to — so nothing distinguishes "the gem tried to read this as `@rbs` and failed" from
|
|
396
|
+
# "this was never meant to be one" except the marker the gem itself keyed on. {SameLineAnnotations::
|
|
397
|
+
# RBS_MARKER} already carries that exact marker (documented there as "the `@rbs` marker as the
|
|
398
|
+
# gem's `annotation_comment?` detects it"), reused here rather than duplicated, so a `CommentLines`
|
|
399
|
+
# paragraph is only reported when the gem's own detector would have called it an `@rbs` attempt.
|
|
400
|
+
#
|
|
401
|
+
# The list is repaired by {SameLineAnnotations.split!} first, exactly as the synthesis path repairs
|
|
402
|
+
# its own, so a valid same-line `#: %a{…} () -> T` is not reported as a `#:` line that failed to
|
|
403
|
+
# parse: it did not fail, it binds.
|
|
404
|
+
#
|
|
405
|
+
# Deliberately narrow otherwise. A construct the gem's parser REJECTS already routes through WD6's
|
|
406
|
+
# error path, and one it never recognised at all is upstream's grammar to define (WD3) — guessing at
|
|
407
|
+
# those would make this a lint on comment prose, which is exactly the false-positive cost ADR-5 ranks
|
|
408
|
+
# first. `SyntaxErrorAssertion` is neither: the gem's OWN parser recognised the `#:` shape and
|
|
409
|
+
# positively flagged its payload as unparseable, so reporting it is naming a fact rbs-inline already
|
|
410
|
+
# computed, not guessing at one. The row-B check is the same kind of fact, read off the gem's own
|
|
411
|
+
# marker rather than a fact rbs-inline names with a dedicated AST class — nothing else in the grammar
|
|
412
|
+
# collapses a recognised-but-unparseable paragraph into ordinary prose.
|
|
188
413
|
def unhonoured_annotations(prism_result)
|
|
189
414
|
::RBS::Inline::AnnotationParser.parse(prism_result.comments).flat_map do |parsed|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
415
|
+
repaired = SameLineAnnotations.split!(parsed)
|
|
416
|
+
unrecognised_tag_notices(repaired) + repaired.each_annotation.filter_map do |annotation|
|
|
417
|
+
case annotation
|
|
418
|
+
when ::RBS::Inline::AST::Annotations::ModuleSelf
|
|
419
|
+
next if annotation.self_types.any?
|
|
193
420
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
421
|
+
"`@rbs module-self` contributed no self-type constraint. Rigor reads the " \
|
|
422
|
+
"`# @rbs module-self Foo` spelling; `# @rbs module-self: Foo` (the spelling in rbs's own " \
|
|
423
|
+
"inline documentation) is not honoured here."
|
|
424
|
+
when ::RBS::Inline::AST::Annotations::SyntaxErrorAssertion
|
|
425
|
+
syntax_error_assertion_notice(annotation)
|
|
426
|
+
when ::RBS::Inline::AST::Annotations::RBSAnnotation
|
|
427
|
+
dropped_remainder_notice(annotation)
|
|
428
|
+
when ::RBS::Inline::AST::Annotations::VarType
|
|
429
|
+
var_type_notice(annotation)
|
|
430
|
+
end
|
|
197
431
|
end
|
|
198
432
|
end.uniq
|
|
199
433
|
end
|
|
200
434
|
|
|
435
|
+
# Issue #997 — the line comes off the annotation's OWN `Prism::Comment` location, not the
|
|
436
|
+
# synthesized RBS buffer: unlike {Environment::RbsLoader#unresolved_type_name_detail}'s position,
|
|
437
|
+
# this one is read straight from the `.rb` file this synthesis call is fresh over (the per-file
|
|
438
|
+
# synthesizer cache is content-keyed, so a changed file simply misses rather than replaying a stale
|
|
439
|
+
# line), so it is the real line the author wrote, not an approximation.
|
|
440
|
+
def syntax_error_assertion_notice(annotation)
|
|
441
|
+
line = annotation.source.comments.first&.location&.start_line
|
|
442
|
+
position = line ? " on line #{line}" : ""
|
|
443
|
+
"a `#:` annotation#{position} did not parse as an RBS method type or type " \
|
|
444
|
+
"(`#{annotation.error_string}`) and was DROPPED — the method types as if the annotation had " \
|
|
445
|
+
"never been written, not merely as if its type were wrong. Fix the RBS syntax to restore it."
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
# Issue #998 — the line is read off the annotation's own comment, as in {#syntax_error_assertion_notice}.
|
|
449
|
+
def dropped_remainder_notice(annotation)
|
|
450
|
+
remainder = SameLineAnnotations.dropped_remainder(annotation)
|
|
451
|
+
return nil if remainder.nil?
|
|
452
|
+
|
|
453
|
+
line = annotation.source.comments.first&.location&.start_line
|
|
454
|
+
position = line ? " on line #{line}" : ""
|
|
455
|
+
"the text after the `%a{…}` annotation#{position} (`#{remainder}`) did not parse as an RBS method " \
|
|
456
|
+
"type and was DROPPED — the annotation is kept, but the method types as if no signature had been " \
|
|
457
|
+
"written beside it. Fix the RBS syntax to restore it."
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
# Issue #1019, row H — a `# @rbs name: T` whose `T` the gem's `parse_type` could not build, read off
|
|
461
|
+
# {RBS::Inline::AST::Annotations::VarType#type} being `nil` rather than off any pattern over the
|
|
462
|
+
# comment text. Silent otherwise: {Writer#default_type} fills the slot exactly as it fills a
|
|
463
|
+
# parameter nobody annotated, so `(untyped n)` does not distinguish "wrote nothing" from "wrote a
|
|
464
|
+
# type RBS could not parse". `raw` is `nil` (and the notice withheld) for the colon-less `# @rbs
|
|
465
|
+
# name` shorthand — the grammar accepts it too, but with no text to name as the dropped type it is a
|
|
466
|
+
# bare mention, not a failed assertion.
|
|
467
|
+
def var_type_notice(annotation)
|
|
468
|
+
return nil if annotation.type
|
|
469
|
+
|
|
470
|
+
raw = annotation.source.string[/#{Regexp.escape(annotation.name.to_s)}\s*:\s*(.+)\z/, 1]&.strip
|
|
471
|
+
return nil if raw.nil? || raw.empty?
|
|
472
|
+
|
|
473
|
+
line = annotation.source.comments.first&.location&.start_line
|
|
474
|
+
position = line ? " on line #{line}" : ""
|
|
475
|
+
"a `# @rbs #{annotation.name}:` annotation#{position} (`#{raw}`) did not parse as an RBS type " \
|
|
476
|
+
"and was DROPPED — the parameter types as `untyped`, not merely as if its type were wrong. If " \
|
|
477
|
+
"`#{raw}` is a Rigor refinement, write it as an `%a{rigor:v1:param: #{annotation.name} is " \
|
|
478
|
+
"#{raw}}` annotation beside the plain RBS type instead — see " \
|
|
479
|
+
"docs/manual/16-rbs-extended-annotations.md — to restore type coverage."
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
# Issue #1019, row B — an `@rbs`-prefixed comment paragraph the gem's OWN detector
|
|
483
|
+
# ({RBS::Inline::AnnotationParser#annotation_comment?}) called an `@rbs` annotation attempt, that
|
|
484
|
+
# `#parse_annotation`'s `case` had no branch for. `# @rbs-ext return: non-empty-string` is the case
|
|
485
|
+
# in point: `annotation_comment?`'s `/\A#(\s*)@rbs(\b|!)/` matches the `\b` word boundary between
|
|
486
|
+
# `s` and `-`, so the gem tries to read it as `@rbs`, fails, and folds it back into a `CommentLines`
|
|
487
|
+
# — the same class a `# rigor: …` or `# @extrbs …` paragraph parses to, which never claimed to be
|
|
488
|
+
# `@rbs` and must stay silent (row X / row C). {SameLineAnnotations::RBS_MARKER} already carries the
|
|
489
|
+
# gem's own marker, so matching it here is reading the SAME fact the gem's detector computed, not
|
|
490
|
+
# inventing a new one — see {#unhonoured_annotations}'s row-B paragraph for why that is not the
|
|
491
|
+
# "regexp lint on comment prose" WD12 otherwise declines to run.
|
|
492
|
+
def unrecognised_tag_notices(parsing_result)
|
|
493
|
+
parsing_result.annotations.grep(::RBS::Inline::AST::CommentLines).filter_map do |lines|
|
|
494
|
+
first_line = lines.string.each_line.first.to_s.strip
|
|
495
|
+
next unless SameLineAnnotations::RBS_MARKER.match?(first_line)
|
|
496
|
+
|
|
497
|
+
line = lines.comments.first&.location&.start_line
|
|
498
|
+
position = line ? " on line #{line}" : ""
|
|
499
|
+
"a comment#{position} (`##{first_line}`) opens with the `@rbs` marker but its body did not " \
|
|
500
|
+
"parse as a recognised `@rbs` annotation, so the whole line was DROPPED — this is not a " \
|
|
501
|
+
"recognised tag, and nothing on it was applied. Use one of the documented `# @rbs` forms " \
|
|
502
|
+
"(`name: T`, `return: T`, `module-self T`, …), or rename the tag so it does not start with " \
|
|
503
|
+
"`@rbs`, to avoid this notice."
|
|
504
|
+
end
|
|
505
|
+
end
|
|
506
|
+
|
|
201
507
|
# Rewrite every RDoc directive comment to its spaced spelling (`#:nodoc:` -> `# :nodoc:`) so
|
|
202
508
|
# upstream's annotation grammar never sees it, and re-parse. Two reasons this happens here rather
|
|
203
509
|
# than in {#annotated?}: the directive must not gate a file in, AND it must not reach the synthesis,
|
|
@@ -45,13 +45,11 @@ module Rigor
|
|
|
45
45
|
# ADR-52 slice 5a — every `let` / `subject` name declared anywhere in the file, across all describe
|
|
46
46
|
# scopes. Feeds the plugin's `dynamic_return file_methods:` gate: the engine only consults the rule
|
|
47
47
|
# for a call whose name appears here; the precise line-scoped resolution stays in `let_block_at`.
|
|
48
|
-
# @return [Array<Symbol>]
|
|
49
48
|
def let_names
|
|
50
49
|
@records.flat_map { |rec| rec.lets.keys }.uniq
|
|
51
50
|
end
|
|
52
51
|
|
|
53
52
|
# Resolves a `let` name at the given line by walking records innermost to outermost.
|
|
54
|
-
# @return [Prism::BlockNode, nil]
|
|
55
53
|
def let_block_at(line, name)
|
|
56
54
|
name_sym = name.to_sym
|
|
57
55
|
records_at(line).reverse.each do |rec|
|
|
@@ -62,7 +60,6 @@ module Rigor
|
|
|
62
60
|
|
|
63
61
|
# Resolves the `describe`-anchor constant name at the given line. The innermost describe with a
|
|
64
62
|
# constant anchor wins; describe-with-String anchors are skipped.
|
|
65
|
-
# @return [String, nil]
|
|
66
63
|
def describe_const_at(line)
|
|
67
64
|
records_at(line).reverse.each do |rec|
|
|
68
65
|
return rec.describe_const if rec.describe_const
|
|
@@ -30,12 +30,8 @@ module Rigor
|
|
|
30
30
|
|
|
31
31
|
module_function
|
|
32
32
|
|
|
33
|
-
# @param
|
|
34
|
-
# @param describe_const [String, nil]
|
|
35
|
-
# @param factory_index [Object, nil] something responding to `find(factory_name)` and returning an
|
|
33
|
+
# @param factory_index — something responding to `find(factory_name)` and returning an
|
|
36
34
|
# entry that responds to `model_class`.
|
|
37
|
-
# @param environment [Rigor::Environment, nil]
|
|
38
|
-
# @return [Rigor::Type, nil]
|
|
39
35
|
def resolve(block_node, describe_const:, factory_index:, environment:)
|
|
40
36
|
tail = body_tail(block_node)
|
|
41
37
|
return nil if tail.nil?
|
|
@@ -38,13 +38,12 @@ module Rigor
|
|
|
38
38
|
module MatcherAnalyzer
|
|
39
39
|
module_function
|
|
40
40
|
|
|
41
|
-
# @param call_node
|
|
41
|
+
# @param call_node — the call whose contribution we're computing. Returns nil when
|
|
42
42
|
# the call shape does not match `expect(local).to matcher`.
|
|
43
|
-
# @param environment
|
|
43
|
+
# @param environment — the surrounding environment used to resolve a
|
|
44
44
|
# matcher's class-name argument to a `Type::Nominal`. When nil, class-name resolution falls back
|
|
45
45
|
# to a bare `Nominal[<name>]` carrier (sound — the receiver constant may be a user class not in
|
|
46
46
|
# RBS).
|
|
47
|
-
# @return [Rigor::FlowContribution, nil]
|
|
48
47
|
def contribution_for(call_node, environment:)
|
|
49
48
|
verb = assertion_verb(call_node)
|
|
50
49
|
return nil if verb.nil?
|
|
@@ -48,6 +48,7 @@ module Rigor
|
|
|
48
48
|
class Rspec < Rigor::Plugin::Base
|
|
49
49
|
manifest(
|
|
50
50
|
id: "rspec",
|
|
51
|
+
target_gems: ["rspec-core"],
|
|
51
52
|
version: "0.3.0",
|
|
52
53
|
description: "Validates RSpec `let` / `subject` declarations within each scope; " \
|
|
53
54
|
"narrows expect(x).to <matcher> assertions downstream in `it` bodies; " \
|
|
@@ -40,9 +40,6 @@ module Rigor
|
|
|
40
40
|
# The matcher violation for a single CallNode, or nil when the node is not a
|
|
41
41
|
# `have_http_status(<int|symbol>)` matcher or its argument is statically valid. ADR-37: the engine
|
|
42
42
|
# owns the walk, so this is per-node logic — no traversal here.
|
|
43
|
-
#
|
|
44
|
-
# @param call_node [Prism::Node]
|
|
45
|
-
# @return [Violation, nil]
|
|
46
43
|
def violation_for(call_node)
|
|
47
44
|
return nil unless call_to_matcher?(call_node)
|
|
48
45
|
|
|
@@ -72,23 +72,15 @@ module Rigor
|
|
|
72
72
|
|
|
73
73
|
module_function
|
|
74
74
|
|
|
75
|
-
# @param
|
|
76
|
-
# @param root [Prism::Node]
|
|
77
|
-
# @param model_index [Hash{String => Hash}, nil] the `:model_index` fact value — the flat
|
|
75
|
+
# @param model_index — the `:model_index` fact value — the flat
|
|
78
76
|
# `class_name => { table:, columns:, associations:, ... }` Hash `rigor-activerecord` publishes
|
|
79
77
|
# (ADR-9's "the value is data, not objects" contract; see `Activerecord#index_to_published_hash`).
|
|
80
78
|
# When nil the analyzer falls silent.
|
|
81
|
-
# @return [Array<Diagnostic>]
|
|
82
79
|
# The matcher violations for a single call node (0..1), or `[]` when it is not a shoulda matcher,
|
|
83
80
|
# sits outside any `describe <ModelConst>` block, or the model is unknown. ADR-37: the engine owns
|
|
84
81
|
# the walk; the model anchor comes from the node-rule `NodeContext` ancestors (the innermost
|
|
85
82
|
# enclosing `describe`-with-constant — nested const describes override, `describe ".method"`
|
|
86
83
|
# string blocks inherit), exactly as the former recursive anchor propagation did.
|
|
87
|
-
#
|
|
88
|
-
# @param matcher_call [Prism::Node]
|
|
89
|
-
# @param ancestors [Array<Prism::Node>]
|
|
90
|
-
# @param model_index [Hash{String => Hash}, nil]
|
|
91
|
-
# @return [Array<Violation>]
|
|
92
84
|
def violations_for(matcher_call:, ancestors:, model_index:)
|
|
93
85
|
return [] if model_index.nil?
|
|
94
86
|
return [] unless matcher_invocation?(matcher_call)
|
|
@@ -47,6 +47,7 @@ module Rigor
|
|
|
47
47
|
class ShouldaMatchers < Rigor::Plugin::Base
|
|
48
48
|
manifest(
|
|
49
49
|
id: "shoulda-matchers",
|
|
50
|
+
target_gems: ["shoulda-matchers"],
|
|
50
51
|
version: "0.1.0",
|
|
51
52
|
description: "Validates shoulda-matchers matchers (validate_presence_of / belong_to / " \
|
|
52
53
|
"have_many / have_db_column / ...) against :model_index from " \
|
|
@@ -34,10 +34,6 @@ module Rigor
|
|
|
34
34
|
|
|
35
35
|
# The worker-call violations for a single call node (0..2), or `[]` when the node is not a
|
|
36
36
|
# `<Worker>.perform_*` entry call on a known worker. ADR-37: the engine owns the walk.
|
|
37
|
-
#
|
|
38
|
-
# @param call_node [Prism::Node]
|
|
39
|
-
# @param worker_index [WorkerIndex]
|
|
40
|
-
# @return [Array<Violation>]
|
|
41
37
|
def violations_for(call_node:, worker_index:)
|
|
42
38
|
return [] unless call_node.is_a?(Prism::CallNode) && entry_call?(call_node)
|
|
43
39
|
|
|
@@ -52,7 +52,7 @@ module Rigor
|
|
|
52
52
|
|
|
53
53
|
module_function
|
|
54
54
|
|
|
55
|
-
# @param marker_modules
|
|
55
|
+
# @param marker_modules — the modules a worker includes — the plugin's own
|
|
56
56
|
# `worker_marker_modules:` config, so one project setting drives the arity check and these rows.
|
|
57
57
|
def attributions(marker_modules)
|
|
58
58
|
marker_modules.flat_map do |marker|
|
|
@@ -41,6 +41,7 @@ module Rigor
|
|
|
41
41
|
# `schedule.yml` IS the schedule map — and the nested paths are `sidekiq-scheduler`'s, whose entries
|
|
42
42
|
# live under `:scheduler: :schedule:` (with the bare `:schedule:` form kept for pre-3.0 layouts).
|
|
43
43
|
SCHEDULE_BLOCK_KEY_PATHS = [[], %w[scheduler schedule], %w[schedule]].freeze
|
|
44
|
+
Ractor.make_shareable(SCHEDULE_BLOCK_KEY_PATHS)
|
|
44
45
|
|
|
45
46
|
# Errno classes that mean "this path is not readable as a schedule" — swallowed so one bad path does
|
|
46
47
|
# not cost the roots the other paths supply.
|
|
@@ -51,7 +52,7 @@ module Rigor
|
|
|
51
52
|
@schedule_paths = schedule_paths
|
|
52
53
|
end
|
|
53
54
|
|
|
54
|
-
# @return
|
|
55
|
+
# @return the class names named by a `class:` key in a schedule entry, sorted and
|
|
55
56
|
# unique. NOT yet intersected with the discovered workers — the caller does that.
|
|
56
57
|
def worker_names
|
|
57
58
|
names = Set.new
|
|
@@ -39,8 +39,6 @@ module Rigor
|
|
|
39
39
|
# never `"::WelcomeWorker"`; see {WorkerDiscoverer}), while a QUERY may legitimately arrive rooted:
|
|
40
40
|
# `::WelcomeWorker.perform_async(1)` renders its receiver as `"::WelcomeWorker"`. The root marker is
|
|
41
41
|
# dropped here, once, so no caller needs a `find(name) || find("::#{name}")` retry (#621).
|
|
42
|
-
#
|
|
43
|
-
# @return [Entry, nil]
|
|
44
42
|
def find(class_name)
|
|
45
43
|
@by_name[strip_leading_namespace(class_name.to_s)]
|
|
46
44
|
end
|
|
@@ -54,6 +54,7 @@ module Rigor
|
|
|
54
54
|
class Sidekiq < Rigor::Plugin::Base
|
|
55
55
|
manifest(
|
|
56
56
|
id: "sidekiq",
|
|
57
|
+
target_gems: ["sidekiq"],
|
|
57
58
|
# Bumped 2026-08-16 — publishes `:reachability_roots` for `rigor unused` (ADR-102 WD3): the workers
|
|
58
59
|
# a schedule file enqueues by name, which no `perform_async` call site writes down.
|
|
59
60
|
# Bumped 2026-09-01 (#534 item 4) — the enqueue methods return the jid `String`.
|
|
@@ -136,12 +137,12 @@ module Rigor
|
|
|
136
137
|
services.fact_store.publish(plugin_id: manifest.id, name: :reachability_roots, value: roots)
|
|
137
138
|
end
|
|
138
139
|
|
|
139
|
-
# File-level only: the load-error
|
|
140
|
+
# File-level only: the load-error disclosure. The per-call arity validation runs over the engine-owned
|
|
140
141
|
# walk via the node_rule below (ADR-37). The worker index is lazily loaded + memoised by
|
|
141
142
|
# `producer_value`, shared by both surfaces.
|
|
142
143
|
def diagnostics_for_file(path:, scope:, root:) # rubocop:disable Lint/UnusedMethodArgument
|
|
143
144
|
index = producer_value(:worker_index)
|
|
144
|
-
|
|
145
|
+
disclose_load_error if index.nil? && producer_error(:worker_index)
|
|
145
146
|
|
|
146
147
|
[]
|
|
147
148
|
end
|
|
@@ -205,10 +206,14 @@ module Rigor
|
|
|
205
206
|
|
|
206
207
|
private
|
|
207
208
|
|
|
208
|
-
|
|
209
|
+
# Issue #1056 — "the worker index did not load" is a fact about the run's INPUTS, not about the file being
|
|
210
|
+
# analysed, so it is registered rather than returned: see {Plugin::Base#disclose_once} for the
|
|
211
|
+
# channel, the `(plugin id, key)` de-duplication and why the engine positions it at `.rigor.yml:1:1`.
|
|
212
|
+
# Returned from the per-file hook it carried no once-guard at all and repeated on every file.
|
|
213
|
+
def disclose_load_error
|
|
209
214
|
error = producer_error(:worker_index)
|
|
210
|
-
|
|
211
|
-
|
|
215
|
+
disclose_once(
|
|
216
|
+
:worker_index_load_failed,
|
|
212
217
|
message: "rigor-sidekiq: failed to discover workers: #{error.class}: #{error.message}",
|
|
213
218
|
severity: :warning,
|
|
214
219
|
rule: "load-error"
|