rigortype 0.4.6 → 0.4.8
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 +4 -2
- data/data/core_overlay/bigdecimal.rbs +33 -0
- data/data/core_overlay/resolv.rbs +30 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +45 -13
- data/data/gem_overlay/addressable/addressable.rbs +40 -0
- data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
- data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
- data/docs/handbook/01-getting-started.md +6 -3
- data/docs/handbook/11-sig-gen.md +85 -3
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/install.md +20 -0
- data/docs/manual/01-installation.md +67 -26
- data/docs/manual/02-cli-reference.md +455 -113
- data/docs/manual/03-configuration.md +76 -5
- data/docs/manual/04-diagnostics.md +5 -4
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/08-skills.md +11 -0
- data/docs/manual/09-editor-integration.md +21 -2
- data/docs/manual/10-mcp-server.md +38 -2
- data/docs/manual/12-caching.md +4 -3
- data/docs/manual/14-rails-quickstart.md +1 -1
- data/docs/manual/19-effect-labels.md +3 -4
- data/docs/manual/plugins/rigor-actionpack.md +32 -15
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +23 -1
- data/docs/manual/plugins/rigor-sorbet.md +17 -3
- data/lib/rigor/analysis/baseline.rb +24 -27
- data/lib/rigor/analysis/check_rules/always_raises_shield.rb +147 -0
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
- data/lib/rigor/analysis/check_rules/call_facts.rb +58 -0
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +12 -4
- data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +85 -13
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +16 -4
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +6 -1
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
- data/lib/rigor/analysis/check_rules/plugin_api_surface.rb +195 -0
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +41 -12
- data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +27 -1
- data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +3 -3
- data/lib/rigor/analysis/check_rules/replaced_singleton_defs.rb +96 -0
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- data/lib/rigor/analysis/check_rules/rule_ids.rb +9 -2
- data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules.rb +720 -141
- data/lib/rigor/analysis/crash_signature.rb +45 -3
- data/lib/rigor/analysis/dependency_recorder.rb +237 -80
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/edit_replay.rb +93 -6
- data/lib/rigor/analysis/fact_store.rb +3 -3
- data/lib/rigor/analysis/incremental.rb +11 -1
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +4 -3
- data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
- data/lib/rigor/analysis/incremental_session.rb +306 -53
- data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -3
- data/lib/rigor/analysis/rule_catalog.rb +39 -3
- data/lib/rigor/analysis/run_cache_key.rb +27 -3
- data/lib/rigor/analysis/run_disclosure_credit.rb +26 -6
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -2
- data/lib/rigor/analysis/runner/declaration_position.rb +8 -8
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +264 -112
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +4 -9
- data/lib/rigor/analysis/runner/pool_coordinator.rb +173 -328
- data/lib/rigor/analysis/runner/project_pre_passes.rb +4 -37
- data/lib/rigor/analysis/runner/run_level_facts.rb +304 -0
- data/lib/rigor/analysis/runner/run_snapshots.rb +44 -54
- data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
- data/lib/rigor/analysis/runner.rb +298 -109
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +62 -23
- data/lib/rigor/analysis/template_unit_positions.rb +2 -3
- data/lib/rigor/analysis/template_units.rb +9 -1
- data/lib/rigor/analysis/worker_session.rb +65 -71
- data/lib/rigor/bleeding_edge.rb +22 -10
- data/lib/rigor/builtins/imported_refinements.rb +6 -8
- data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
- data/lib/rigor/builtins/regex_refinement.rb +4 -6
- data/lib/rigor/cache/descriptor.rb +6 -5
- data/lib/rigor/cache/file_digest.rb +39 -31
- data/lib/rigor/cache/incremental_snapshot.rb +95 -17
- data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
- data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
- data/lib/rigor/cache/rbs_constant_table.rb +1 -1
- data/lib/rigor/cache/rbs_descriptor.rb +90 -4
- data/lib/rigor/cache/rbs_environment.rb +8 -1
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
- data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
- data/lib/rigor/cache/store.rb +11 -10
- data/lib/rigor/ci_detector.rb +1 -1
- data/lib/rigor/cli/annotate_command.rb +24 -19
- data/lib/rigor/cli/baseline_command.rb +160 -57
- data/lib/rigor/cli/catalogue.rb +256 -0
- data/lib/rigor/cli/check_command.rb +112 -104
- data/lib/rigor/cli/check_runner_factory.rb +2 -2
- data/lib/rigor/cli/command.rb +15 -1
- data/lib/rigor/cli/coverage_command.rb +21 -8
- data/lib/rigor/cli/coverage_mutation.rb +5 -7
- data/lib/rigor/cli/coverage_renderer.rb +2 -1
- data/lib/rigor/cli/coverage_scan.rb +3 -5
- data/lib/rigor/cli/diff_command.rb +25 -20
- data/lib/rigor/cli/docs_command.rb +1 -2
- data/lib/rigor/cli/doctor_command.rb +12 -12
- data/lib/rigor/cli/effects_command.rb +50 -40
- data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
- data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
- data/lib/rigor/cli/effects_renderer.rb +2 -1
- data/lib/rigor/cli/effects_snapshot_command.rb +26 -18
- data/lib/rigor/cli/explain_command.rb +18 -8
- data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
- data/lib/rigor/cli/json_document.rb +41 -0
- data/lib/rigor/cli/lens_command.rb +29 -10
- data/lib/rigor/cli/lsp_command.rb +250 -23
- data/lib/rigor/cli/mcp_command.rb +12 -7
- data/lib/rigor/cli/mutation_fork_scan.rb +1 -1
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -2
- data/lib/rigor/cli/options.rb +40 -1
- data/lib/rigor/cli/plugin_command.rb +93 -46
- data/lib/rigor/cli/plugins_command.rb +17 -18
- data/lib/rigor/cli/plugins_renderer.rb +10 -6
- data/lib/rigor/cli/protection_fork_scan.rb +2 -2
- data/lib/rigor/cli/protection_renderer.rb +2 -1
- data/lib/rigor/cli/show_bleedingedge_command.rb +21 -15
- data/lib/rigor/cli/sig_gen_command.rb +92 -27
- data/lib/rigor/cli/skill_command.rb +32 -3
- data/lib/rigor/cli/skill_deep_probe.rb +1 -1
- data/lib/rigor/cli/skill_describe.rb +2 -1
- data/lib/rigor/cli/trace_command.rb +14 -9
- data/lib/rigor/cli/triage_command.rb +10 -11
- data/lib/rigor/cli/triage_renderer.rb +2 -1
- data/lib/rigor/cli/type_of_command.rb +16 -11
- data/lib/rigor/cli/type_of_project_scope.rb +1 -4
- data/lib/rigor/cli/type_of_renderer.rb +4 -1
- data/lib/rigor/cli/type_scan_command.rb +14 -9
- data/lib/rigor/cli/type_scan_renderer.rb +2 -1
- data/lib/rigor/cli/unused_command.rb +27 -17
- data/lib/rigor/cli/upgrade_command.rb +5 -0
- data/lib/rigor/cli.rb +214 -56
- data/lib/rigor/config_audit.rb +33 -19
- data/lib/rigor/configuration/severity_profile.rb +14 -5
- data/lib/rigor/configuration.rb +104 -35
- data/lib/rigor/effects/collector.rb +53 -21
- data/lib/rigor/effects/definition_context.rb +0 -2
- data/lib/rigor/effects/envelope.rb +1 -1
- data/lib/rigor/effects/framework_units.rb +1 -1
- data/lib/rigor/effects/mutation_classifier.rb +4 -7
- data/lib/rigor/effects/propagator.rb +1 -1
- data/lib/rigor/effects/registry.rb +2 -2
- data/lib/rigor/effects/snapshot.rb +1 -1
- data/lib/rigor/effects/taint_cause.rb +0 -2
- data/lib/rigor/effects/unit_scan.rb +9 -13
- data/lib/rigor/environment/bundle_sig_discovery.rb +64 -33
- data/lib/rigor/environment/class_registry.rb +3 -3
- data/lib/rigor/environment/constant_type_cache_holder.rb +5 -0
- data/lib/rigor/environment/core_root.rb +77 -0
- data/lib/rigor/environment/default_libraries.rb +18 -1
- data/lib/rigor/environment/deferred_signature_plan.rb +381 -0
- data/lib/rigor/environment/gated_signature_guard.rb +1 -1
- data/lib/rigor/environment/gated_signature_plan.rb +5 -2
- data/lib/rigor/environment/hkt_registry_holder.rb +6 -0
- data/lib/rigor/environment/lockfile_resolver.rb +28 -12
- data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
- data/lib/rigor/environment/member_consistency.rb +56 -7
- data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
- data/lib/rigor/environment/project_path_gems.rb +124 -0
- data/lib/rigor/environment/rbs_collection_discovery.rb +123 -8
- data/lib/rigor/environment/rbs_coverage_report.rb +12 -11
- data/lib/rigor/environment/rbs_hierarchy.rb +12 -15
- data/lib/rigor/environment/rbs_loader.rb +772 -110
- data/lib/rigor/environment/reflection.rb +7 -7
- data/lib/rigor/environment/required_features.rb +0 -3
- data/lib/rigor/environment/ruby_library_roots.rb +1 -1
- data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
- data/lib/rigor/environment.rb +169 -42
- data/lib/rigor/flow_contribution/merger.rb +4 -4
- data/lib/rigor/inference/acceptance.rb +136 -40
- data/lib/rigor/inference/alias_mutation_floor/foreign_locals.rb +117 -0
- data/lib/rigor/inference/alias_mutation_floor/memos.rb +85 -0
- data/lib/rigor/inference/alias_mutation_floor/reach.rb +119 -0
- data/lib/rigor/inference/alias_mutation_floor/site_collector.rb +252 -0
- data/lib/rigor/inference/alias_mutation_floor.rb +339 -0
- data/lib/rigor/inference/anonymous_meta_class.rb +18 -0
- data/lib/rigor/inference/block_auto_splat.rb +1 -1
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/block_run_count.rb +185 -0
- data/lib/rigor/inference/budget_trace.rb +8 -2
- data/lib/rigor/inference/builtins/array_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
- data/lib/rigor/inference/callee_mutation_chain.rb +88 -77
- data/lib/rigor/inference/callee_write_census.rb +815 -0
- data/lib/rigor/inference/captured_locals.rb +2 -0
- data/lib/rigor/inference/census_foreign_block.rb +354 -0
- data/lib/rigor/inference/clause_widening.rb +1 -1
- data/lib/rigor/inference/closure_escape_analyzer.rb +11 -4
- data/lib/rigor/inference/construction_escape.rb +258 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
- data/lib/rigor/inference/declaration_walk.rb +4 -0
- data/lib/rigor/inference/deep_erasure.rb +39 -7
- data/lib/rigor/inference/def_handle.rb +1 -1
- data/lib/rigor/inference/def_node_resolver.rb +33 -15
- data/lib/rigor/inference/def_return_typer.rb +47 -5
- data/lib/rigor/inference/definer_resolution.rb +19 -9
- data/lib/rigor/inference/definers_index.rb +162 -0
- data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
- data/lib/rigor/inference/element_read_widening.rb +49 -3
- data/lib/rigor/inference/error_info.rb +16 -0
- data/lib/rigor/inference/expression_typer.rb +1559 -320
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/flow_tracer.rb +31 -14
- data/lib/rigor/inference/fork_map.rb +3 -0
- data/lib/rigor/inference/fresh_frame_blocks.rb +52 -1
- data/lib/rigor/inference/global_write_census.rb +81 -5
- data/lib/rigor/inference/global_writer_reach.rb +260 -0
- data/lib/rigor/inference/hkt_body.rb +1 -2
- data/lib/rigor/inference/hkt_body_parser.rb +3 -5
- data/lib/rigor/inference/hkt_reducer.rb +2 -2
- data/lib/rigor/inference/hkt_registry.rb +3 -5
- data/lib/rigor/inference/in_effect_refinements.rb +7 -0
- data/lib/rigor/inference/indexed_narrowing.rb +61 -15
- data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
- data/lib/rigor/inference/ivar_writer_census.rb +154 -8
- data/lib/rigor/inference/key_presence_guard.rb +51 -24
- data/lib/rigor/inference/last_line/self_evidence.rb +30 -0
- data/lib/rigor/inference/last_line.rb +5 -2
- data/lib/rigor/inference/loop_value_record.rb +66 -14
- data/lib/rigor/inference/match_rebinding/frame.rb +57 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +101 -0
- data/lib/rigor/inference/match_rebinding.rb +28 -5
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +44 -30
- data/lib/rigor/inference/method_dispatcher/class_keyed_block_params.rb +65 -0
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +3 -3
- data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
- data/lib/rigor/inference/method_dispatcher/core_enumerable_receiver.rb +44 -0
- data/lib/rigor/inference/method_dispatcher/declared_constructor.rb +151 -0
- data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/interface_shape.rb +181 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +51 -27
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +606 -78
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +3 -3
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +9 -7
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +467 -84
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
- data/lib/rigor/inference/method_dispatcher/singleton_type_params.rb +104 -0
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +123 -25
- data/lib/rigor/inference/method_parameter_binder.rb +136 -56
- data/lib/rigor/inference/module_function_state/spellings.rb +2 -2
- data/lib/rigor/inference/module_function_state.rb +24 -3
- data/lib/rigor/inference/multi_target_binder.rb +2 -2
- data/lib/rigor/inference/mutation_rejoin.rb +240 -4
- data/lib/rigor/inference/mutation_widening.rb +199 -29
- data/lib/rigor/inference/narrowing.rb +238 -50
- data/lib/rigor/inference/operand_effects.rb +43 -2
- data/lib/rigor/inference/optimistic_origin.rb +134 -9
- data/lib/rigor/inference/origin_lookup.rb +1 -1
- data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +1 -1
- data/lib/rigor/inference/precision_scanner.rb +1 -1
- data/lib/rigor/inference/project_method_ownership.rb +30 -9
- data/lib/rigor/inference/project_patched_methods.rb +65 -13
- data/lib/rigor/inference/project_patched_scanner.rb +120 -25
- data/lib/rigor/inference/project_patched_visibility.rb +39 -3
- data/lib/rigor/inference/project_yielder.rb +11 -17
- data/lib/rigor/inference/rbs_type_translator.rb +68 -15
- data/lib/rigor/inference/receiver_alias.rb +20 -0
- data/lib/rigor/inference/receiver_writer_call.rb +67 -0
- data/lib/rigor/inference/recursion_rebinding.rb +157 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +57 -17
- data/lib/rigor/inference/refined_dispatch/own_method.rb +51 -16
- data/lib/rigor/inference/refined_dispatch.rb +13 -0
- data/lib/rigor/inference/reflective_ivar_write.rb +19 -11
- data/lib/rigor/inference/remover_holder_scan.rb +287 -0
- data/lib/rigor/inference/remover_narrowing.rb +224 -0
- data/lib/rigor/inference/rewrite_mutation.rb +53 -2
- data/lib/rigor/inference/scope_indexer/certainty.rb +0 -1
- data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +13 -6
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +70 -26
- data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
- data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
- data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +295 -0
- data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +136 -9
- data/lib/rigor/inference/scope_indexer.rb +2213 -226
- data/lib/rigor/inference/self_call_ivar_writes.rb +103 -12
- data/lib/rigor/inference/self_preserving_block.rb +87 -0
- data/lib/rigor/inference/set_mutation.rb +189 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +42 -8
- data/lib/rigor/inference/singleton_object_constant.rb +32 -2
- data/lib/rigor/inference/statement_evaluator.rb +3268 -358
- data/lib/rigor/inference/string_mutation.rb +14 -0
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_scanner.rb +40 -17
- data/lib/rigor/inference/toplevel_def_census.rb +67 -0
- data/lib/rigor/inference/unknown_store_widening.rb +103 -4
- data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
- data/lib/rigor/inference/untyped_rejoin.rb +1 -1
- data/lib/rigor/inference/void_tail_summary.rb +29 -9
- data/lib/rigor/language_server/buffer_table.rb +3 -3
- data/lib/rigor/language_server/completion_provider.rb +26 -20
- data/lib/rigor/language_server/config_warning_announcer.rb +28 -0
- data/lib/rigor/language_server/diagnostic_publisher.rb +8 -3
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -2
- data/lib/rigor/language_server/hover_provider.rb +1 -7
- data/lib/rigor/language_server/hover_renderer.rb +6 -8
- data/lib/rigor/language_server/project_context.rb +39 -1
- data/lib/rigor/language_server/publish_batcher.rb +3 -4
- data/lib/rigor/language_server/selection_range_provider.rb +2 -4
- data/lib/rigor/language_server/server.rb +7 -3
- data/lib/rigor/language_server/signature_help_provider.rb +4 -4
- data/lib/rigor/lens/claims/comparison.rb +78 -0
- data/lib/rigor/lens/claims/request.rb +131 -0
- data/lib/rigor/lens/claims/type_reader.rb +216 -0
- data/lib/rigor/lens/claims.rb +208 -0
- data/lib/rigor/lens/contract_reader.rb +5 -0
- data/lib/rigor/lens/document.rb +1 -1
- data/lib/rigor/lens/file_reader.rb +21 -8
- data/lib/rigor/lens/plugins.rb +1 -1
- data/lib/rigor/lens/run.rb +8 -0
- data/lib/rigor/lens.rb +1 -1
- data/lib/rigor/library_probe.rb +68 -0
- data/lib/rigor/mcp/server.rb +73 -2
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/memo_audit.rb +282 -0
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- data/lib/rigor/plugin/base.rb +15 -2
- data/lib/rigor/plugin/bundled_catalog.rb +1 -12
- data/lib/rigor/plugin/io_boundary.rb +26 -4
- data/lib/rigor/plugin/loader.rb +25 -8
- data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
- data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
- data/lib/rigor/plugin/manifest.rb +16 -16
- data/lib/rigor/plugin/registry.rb +16 -5
- data/lib/rigor/plugin/services.rb +2 -3
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
- data/lib/rigor/plugin/trust_policy.rb +48 -2
- data/lib/rigor/plugin/type_node_resolver.rb +2 -4
- data/lib/rigor/plugin.rb +6 -8
- data/lib/rigor/plugin_gap_advisory.rb +5 -1
- data/lib/rigor/project_environment.rb +16 -2
- data/lib/rigor/protection/analysis_guard.rb +1 -1
- data/lib/rigor/protection/discovery_seed.rb +44 -5
- data/lib/rigor/protection/mutation_cache.rb +4 -1
- data/lib/rigor/protection/mutator.rb +1 -1
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
- data/lib/rigor/rbs_extended/reporter.rb +27 -3
- data/lib/rigor/rbs_extended.rb +2 -3
- data/lib/rigor/reflection/constant_ancestors.rb +81 -22
- data/lib/rigor/reflection/declaration_collectors.rb +55 -0
- data/lib/rigor/reflection/declarations.rb +58 -0
- data/lib/rigor/reflection/project_method.rb +177 -0
- data/lib/rigor/reflection.rb +2 -0
- data/lib/rigor/rooted_glob.rb +32 -1
- data/lib/rigor/run_scoped_slots.rb +38 -0
- data/lib/rigor/runtime/jit.rb +1 -1
- data/lib/rigor/scope/discovery_index.rb +168 -8
- data/lib/rigor/scope/resolution_chain/relevance.rb +14 -1
- data/lib/rigor/scope/resolution_chain.rb +901 -54
- data/lib/rigor/scope.rb +644 -37
- data/lib/rigor/sig_gen/bot_proposal_guard.rb +306 -0
- data/lib/rigor/sig_gen/classification.rb +18 -1
- data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
- data/lib/rigor/sig_gen/generator.rb +588 -66
- data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
- data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
- data/lib/rigor/sig_gen/layout_index.rb +3 -3
- data/lib/rigor/sig_gen/method_candidate.rb +37 -4
- data/lib/rigor/sig_gen/observation_collector.rb +14 -7
- data/lib/rigor/sig_gen/observed_call.rb +1 -1
- data/lib/rigor/sig_gen/path_mapper.rb +6 -0
- data/lib/rigor/sig_gen/platform_guarded_files.rb +184 -0
- data/lib/rigor/sig_gen/renderer.rb +15 -9
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +62 -2
- data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
- data/lib/rigor/sig_gen/write_reporter.rb +63 -14
- data/lib/rigor/sig_gen/write_result.rb +23 -12
- data/lib/rigor/sig_gen/writer.rb +238 -37
- data/lib/rigor/sig_gen.rb +1 -0
- data/lib/rigor/source/forwardable_delegators.rb +122 -0
- data/lib/rigor/source/node_children.rb +13 -0
- data/lib/rigor/type/acceptance_router.rb +2 -2
- data/lib/rigor/type/accepts_result.rb +1 -1
- data/lib/rigor/type/app.rb +4 -6
- data/lib/rigor/type/combinator.rb +173 -21
- data/lib/rigor/type/constant.rb +4 -5
- data/lib/rigor/type/hash_shape.rb +21 -40
- data/lib/rigor/type/interface.rb +60 -0
- data/lib/rigor/type/union.rb +1 -1
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/identifier.rb +1 -2
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node/string_literal.rb +1 -2
- data/lib/rigor/type_node/symbol_literal.rb +1 -2
- data/lib/rigor/type_node/union.rb +3 -6
- data/lib/rigor/type_of_memo/serving.rb +99 -0
- data/lib/rigor/type_of_memo/stats.rb +43 -0
- data/lib/rigor/type_of_memo.rb +337 -0
- data/lib/rigor/typing_state.rb +1094 -0
- data/lib/rigor/value_semantics.rb +1 -10
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +38 -36
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +3 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +115 -36
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +3 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +25 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +16 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +4 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +132 -33
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +20 -6
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +7 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +54 -17
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +19 -5
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +11 -14
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +30 -9
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +55 -13
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +15 -21
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -6
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +1 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +6 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +5 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +1 -1
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +8 -7
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +12 -2
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +8 -2
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +200 -4
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +4 -3
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +5 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -2
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +3 -4
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +6 -8
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +3 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +210 -3
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +146 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +52 -34
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +97 -24
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +16 -16
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +4 -8
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
- data/sig/rigor/analysis/baseline.rbs +0 -3
- data/sig/rigor/analysis/check_rules/rule_walk.rbs +6 -0
- data/sig/rigor/analysis/fact_store.rbs +2 -2
- data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
- data/sig/rigor/cache/descriptor.rbs +14 -0
- data/sig/rigor/cli/diff_command.rbs +1 -1
- data/sig/rigor/cli/explain_command.rbs +1 -1
- data/sig/rigor/effects/registry.rbs +2 -2
- data/sig/rigor/environment.rbs +4 -1
- data/sig/rigor/inference.rbs +4 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +53 -18
- data/sig/rigor/plugin/effect_attribution.rbs +3 -3
- data/sig/rigor/plugin/effect_edge.rbs +1 -1
- data/sig/rigor/plugin/effect_entry_points.rbs +3 -3
- data/sig/rigor/plugin/interfaces.rbs +51 -0
- data/sig/rigor/plugin/io_boundary.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +9 -9
- data/sig/rigor/plugin/node_context.rbs +21 -0
- data/sig/rigor/plugin/protocol_contract.rbs +3 -7
- data/sig/rigor/reflection.rbs +37 -0
- data/sig/rigor/scope.rbs +43 -4
- data/sig/rigor/trinary.rbs +1 -0
- data/sig/rigor/type.rbs +9 -3
- data/sig/rigor.rbs +3 -2
- data/skills/rigor-ask/SKILL.md +1 -0
- data/skills/rigor-plugin-author/SKILL.md +4 -4
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
- data/skills/rigor-plugin-review/SKILL.md +3 -3
- data/skills/rigor-project-init/references/01-detect.md +1 -0
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +17 -2
- data/skills/rigor-project-init/references/06-agent-contract.md +4 -4
- data/skills/rigor-rbs-contract/SKILL.md +147 -0
- data/skills/rigor-rbs-contract/references/01-finding-candidates.md +212 -0
- data/skills/rigor-rbs-contract/references/02-contract-rules.md +157 -0
- data/skills/rigor-rbs-contract/references/03-target-own-code.md +90 -0
- data/skills/rigor-rbs-contract/references/04-target-gem-rbs-collection.md +210 -0
- data/skills/rigor-rbs-contract/references/05-declaration-mutants.md +182 -0
- data/skills/rigor-rbs-setup/SKILL.md +3 -1
- data/skills/rigor-type-oracle/SKILL.md +26 -1
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +4 -3
- metadata +82 -3
|
@@ -604,6 +604,35 @@ class Float
|
|
|
604
604
|
def gigabytes: () -> Float
|
|
605
605
|
end
|
|
606
606
|
|
|
607
|
+
# ---------------------------------------------------------------
|
|
608
|
+
# NumericWithFormat — ActiveSupport's `core_ext/numeric/conversions`, included into Integer, Float and
|
|
609
|
+
# BigDecimal. Declared on the module and included, not as a `def` on each class, so a project `sig/` that
|
|
610
|
+
# declares `Integer#to_fs` itself overrides it instead of raising `DuplicatedMethodDefinitionError` (the
|
|
611
|
+
# `process.rbs` shape). Not pure: the Symbol formats read `I18n` for their locale. An Integer or String
|
|
612
|
+
# format is `to_s(format)`, and no format is `to_s`, so every form answers a String. `options` is any
|
|
613
|
+
# Hash-like value: ActiveSupport calls `symbolize_keys` on it, so String keys and a HashWithIndifferentAccess
|
|
614
|
+
# are both valid.
|
|
615
|
+
# ---------------------------------------------------------------
|
|
616
|
+
|
|
617
|
+
module ActiveSupport
|
|
618
|
+
module NumericWithFormat
|
|
619
|
+
def to_fs: (?(Symbol | Integer | String | nil) format, ?Hash[untyped, untyped]? options) -> String
|
|
620
|
+
def to_formatted_s: (?(Symbol | Integer | String | nil) format, ?Hash[untyped, untyped]? options) -> String
|
|
621
|
+
end
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
class Integer
|
|
625
|
+
include ActiveSupport::NumericWithFormat
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
class Float
|
|
629
|
+
include ActiveSupport::NumericWithFormat
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
class BigDecimal
|
|
633
|
+
include ActiveSupport::NumericWithFormat
|
|
634
|
+
end
|
|
635
|
+
|
|
607
636
|
# ---------------------------------------------------------------
|
|
608
637
|
# ActiveSupport::Duration — issue #632
|
|
609
638
|
# ---------------------------------------------------------------
|
|
@@ -1565,28 +1594,35 @@ class Hash[unchecked out K, unchecked out V]
|
|
|
1565
1594
|
# non-bang half is `%a{pure}`. `assert_valid_keys` raises rather than
|
|
1566
1595
|
# mutating (raising is not an effect label).
|
|
1567
1596
|
%a{pure}
|
|
1568
|
-
|
|
1597
|
+
# `key.to_sym rescue key` keeps a key without `to_sym`, so no one key type is right: `Hash[untyped, V]` here,
|
|
1598
|
+
# and the plugin's `dynamic_return` hook (#2433) maps each key member where it can.
|
|
1599
|
+
def symbolize_keys: () -> Hash[untyped, V]
|
|
1600
|
+
# `symbolize_keys!` keeps `-> self`: `key.to_sym rescue key` leaves a key without `to_sym` as it is, so
|
|
1601
|
+
# `Hash[Symbol, V]` is wrong for an Integer-keyed receiver (#2424 review). The other bang forms below
|
|
1602
|
+
# answer the re-keyed or re-valued hash.
|
|
1569
1603
|
def symbolize_keys!: () -> self
|
|
1570
1604
|
%a{pure}
|
|
1571
1605
|
def deep_symbolize_keys: () -> Hash[untyped, untyped]
|
|
1572
|
-
def deep_symbolize_keys!: () ->
|
|
1606
|
+
def deep_symbolize_keys!: () -> Hash[untyped, untyped]
|
|
1573
1607
|
%a{pure}
|
|
1574
1608
|
def stringify_keys: () -> Hash[String, V]
|
|
1575
|
-
def stringify_keys!: () ->
|
|
1609
|
+
def stringify_keys!: () -> Hash[String, V]
|
|
1576
1610
|
%a{pure}
|
|
1577
1611
|
def deep_stringify_keys: () -> Hash[untyped, untyped]
|
|
1578
|
-
def deep_stringify_keys!: () ->
|
|
1612
|
+
def deep_stringify_keys!: () -> Hash[untyped, untyped]
|
|
1579
1613
|
%a{pure}
|
|
1580
1614
|
def assert_valid_keys: (*K | Array[K]) -> self
|
|
1615
|
+
# The block's result is the new key at every level, so the keys are its type, not `K`: `{ a: 1 }
|
|
1616
|
+
# .deep_transform_keys(&:to_s)` has String keys. Nested values are re-keyed too, so they read `untyped`.
|
|
1581
1617
|
%a{pure}
|
|
1582
|
-
def deep_transform_keys: () { (K) ->
|
|
1583
|
-
def deep_transform_keys!: () { (K) ->
|
|
1618
|
+
def deep_transform_keys: [X] () { (K) -> X } -> Hash[X, untyped]
|
|
1619
|
+
def deep_transform_keys!: [X] () { (K) -> X } -> Hash[X, untyped]
|
|
1584
1620
|
%a{pure}
|
|
1585
1621
|
def deep_transform_values: () { (V) -> untyped } -> Hash[K, untyped]
|
|
1586
|
-
def deep_transform_values!: () { (V) -> untyped } ->
|
|
1622
|
+
def deep_transform_values!: () { (V) -> untyped } -> Hash[K, untyped]
|
|
1587
1623
|
# `alias_method`s of `symbolize_keys` / `symbolize_keys!`.
|
|
1588
1624
|
%a{pure}
|
|
1589
|
-
def to_options: () -> Hash[
|
|
1625
|
+
def to_options: () -> Hash[untyped, V]
|
|
1590
1626
|
def to_options!: () -> self
|
|
1591
1627
|
|
|
1592
1628
|
# `core_ext/hash/deep_dup` — allocates a deep copy, never touches the
|
|
@@ -1600,11 +1636,17 @@ class Hash[unchecked out K, unchecked out V]
|
|
|
1600
1636
|
# `core_ext/hash/deep_merge` — the block receives `(key, this_val,
|
|
1601
1637
|
# other_val)` and returns the merged value; neither `deep_merge` nor its
|
|
1602
1638
|
# block mutates either hash (containment covers the block itself).
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1639
|
+
# The result carries the argument's keys and values as well as the
|
|
1640
|
+
# receiver's, and a key both hold a Hash under merges into a third Hash
|
|
1641
|
+
# that is neither side's value type (`{ n: { x: 1 } }.deep_merge(n: { y:
|
|
1642
|
+
# "s" })[:n]` is `{ x: 1, y: "s" }`), so the values read `untyped`.
|
|
1643
|
+
# `(Hash[K, V]) -> Hash[K, V]` typed `h.deep_merge(b: "x")[:b]` as the
|
|
1644
|
+
# receiver's value type (#2424 review). The bang form answers the receiver.
|
|
1645
|
+
%a{pure}
|
|
1646
|
+
def deep_merge: [A, B] (Hash[A, B] other_hash) -> Hash[K | A, untyped]
|
|
1647
|
+
| [A, B] (Hash[A, B] other_hash) { (K | A, untyped, untyped) -> untyped } -> Hash[K | A, untyped]
|
|
1648
|
+
def deep_merge!: [A, B] (Hash[A, B] other_hash) -> Hash[K | A, untyped]
|
|
1649
|
+
| [A, B] (Hash[A, B] other_hash) { (K | A, untyped, untyped) -> untyped } -> Hash[K | A, untyped]
|
|
1608
1650
|
# `other.is_a?(Hash)` — the hook `ActiveSupport::DeepMergeable` asks before
|
|
1609
1651
|
# recursing into a value. `:nodoc:`, but public.
|
|
1610
1652
|
%a{pure}
|
|
@@ -2,24 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
require "prism"
|
|
4
4
|
|
|
5
|
-
require "rigor/
|
|
5
|
+
require "rigor/reflection"
|
|
6
6
|
|
|
7
7
|
require_relative "resource_index"
|
|
8
8
|
|
|
9
9
|
module Rigor
|
|
10
10
|
module Plugin
|
|
11
11
|
class Alba < Rigor::Plugin::Base
|
|
12
|
-
# A
|
|
13
|
-
# `include Alba::Resource`s, and the association declarations whose resource
|
|
14
|
-
#
|
|
12
|
+
# A collector over one file, built on {Rigor::Reflection.class_declarations_in}: the classes it declares, whether
|
|
13
|
+
# each `include Alba::Resource`s, and the association declarations whose resource class alba infers from
|
|
14
|
+
# the name.
|
|
15
15
|
#
|
|
16
16
|
# An association is recorded only when alba would take the inference path (`Alba::Association#
|
|
17
17
|
# resource_from`): a literal Symbol/String name, exactly one positional argument, no block, and no
|
|
18
18
|
# `resource:` / `serializer:` keyword. A keyword splat (`**opts`) might carry either, so it is not
|
|
19
19
|
# recorded either — a skipped association costs a root, never invents one.
|
|
20
20
|
class ResourceCollector
|
|
21
|
-
include Rigor::Inference::DeclarationWalk::Collector
|
|
22
|
-
|
|
23
21
|
ASSOCIATION_METHODS = %i[association one many has_one has_many].freeze
|
|
24
22
|
RESOURCE_MODULE = "Alba::Resource"
|
|
25
23
|
RESOURCE_KEYWORDS = %i[resource serializer].freeze
|
|
@@ -40,22 +38,13 @@ module Rigor
|
|
|
40
38
|
@associations = []
|
|
41
39
|
end
|
|
42
40
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
@entries[name] = superclass_name(node.superclass)
|
|
49
|
-
@nestings[name] = context.nesting || []
|
|
41
|
+
# Walks `root` (a parsed file's `Prism::Node`) and records what it finds; answers the collector itself.
|
|
42
|
+
def collect(root)
|
|
43
|
+
Rigor::Reflection.class_declarations_in(root) { |call| on_call(call.node, call.owner) }.each do |declaration|
|
|
44
|
+
@entries[declaration.name] = superclass_name(declaration.node.superclass)
|
|
45
|
+
@nestings[declaration.name] = declaration.nesting
|
|
50
46
|
end
|
|
51
|
-
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def on_call(node, context)
|
|
55
|
-
owner = context.prefix.join("::")
|
|
56
|
-
record_call(node, owner) unless owner.empty? || !node.receiver.nil?
|
|
57
|
-
remember_block(node)
|
|
58
|
-
Rigor::Inference::DeclarationWalk::DESCEND
|
|
47
|
+
self
|
|
59
48
|
end
|
|
60
49
|
|
|
61
50
|
# The classes this file declares, as {ResourceIndex::ClassEntry} values.
|
|
@@ -70,6 +59,11 @@ module Rigor
|
|
|
70
59
|
|
|
71
60
|
private
|
|
72
61
|
|
|
62
|
+
def on_call(node, owner)
|
|
63
|
+
record_call(node, owner) unless owner.nil? || !node.receiver.nil?
|
|
64
|
+
remember_block(node)
|
|
65
|
+
end
|
|
66
|
+
|
|
73
67
|
def record_call(node, owner)
|
|
74
68
|
if node.name == :include
|
|
75
69
|
@includes[owner] = true if includes_resource?(node)
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
require "prism"
|
|
4
4
|
|
|
5
|
-
require "rigor/inference/declaration_walk"
|
|
6
|
-
|
|
7
5
|
require_relative "resource_collector"
|
|
8
6
|
require_relative "resource_index"
|
|
9
7
|
|
|
@@ -28,10 +26,7 @@ module Rigor
|
|
|
28
26
|
parsed = Prism.parse(contents)
|
|
29
27
|
next unless parsed.success?
|
|
30
28
|
|
|
31
|
-
collector = ResourceCollector.new
|
|
32
|
-
Rigor::Inference::DeclarationWalk.run(
|
|
33
|
-
parsed.value, [collector], Rigor::Inference::DeclarationWalk::Context.root(nesting: [])
|
|
34
|
-
)
|
|
29
|
+
collector = ResourceCollector.new.collect(parsed.value)
|
|
35
30
|
entries.concat(collector.class_entries)
|
|
36
31
|
associations.concat(collector.associations)
|
|
37
32
|
end
|
|
@@ -37,9 +37,9 @@ module Rigor
|
|
|
37
37
|
@classes.empty?
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
# The classes alba infers for the associations that name no resource, limited to
|
|
41
|
-
#
|
|
42
|
-
#
|
|
40
|
+
# The classes alba infers for the associations that name no resource, limited to those that exist in
|
|
41
|
+
# the project. The block receives each association name and returns alba's `classify` of it (the
|
|
42
|
+
# caller owns the inflector; ADR-39).
|
|
43
43
|
def inferred_roots(&classify)
|
|
44
44
|
roots = @associations.filter_map do |association|
|
|
45
45
|
next unless resource?(association.owner)
|
|
@@ -212,9 +212,7 @@ module Rigor
|
|
|
212
212
|
# ... end` row's own recursive call (see {#each_block_type_info}). It caps the recursion at ONE
|
|
213
213
|
# level deep: with `nested: true`, {#walk_predicate_chain} declines a FURTHER `each do ... end`
|
|
214
214
|
# rather than recursing again (issue #137 deliberately does not model doubly-nested arrays of
|
|
215
|
-
# arrays).
|
|
216
|
-
# is the only caller that ever passes `nested: true}` — so the call graph has no cycle for the
|
|
217
|
-
# engine's return-type inference to approximate through.
|
|
215
|
+
# arrays). The recursion through {#each_block_type_info} is therefore bounded and terminates.
|
|
218
216
|
def collect_schema_shape(block_node, type_aliases, nested:)
|
|
219
217
|
required = {}
|
|
220
218
|
optional = {}
|
|
@@ -34,9 +34,9 @@ module Rigor
|
|
|
34
34
|
#
|
|
35
35
|
# The synthetic reader's return type is resolved via ADR-18's `returns_from_arg:` fact lookup: the
|
|
36
36
|
# call-site's second argument (`Types::String` etc.) is looked up through the `:dry_type_aliases`
|
|
37
|
-
# fact published by `rigor-dry-types`, yielding `
|
|
38
|
-
# misses (e.g. inline method-chain argument whose chain-head isn't currently extracted), the row
|
|
39
|
-
# falls back to
|
|
37
|
+
# fact published by `rigor-dry-types`, yielding the aliased class (`String`) for common cases. When the
|
|
38
|
+
# lookup misses (e.g. inline method-chain argument whose chain-head isn't currently extracted), the row
|
|
39
|
+
# falls back to the untyped floor silently.
|
|
40
40
|
#
|
|
41
41
|
# ## Scope (slice 2c minimum)
|
|
42
42
|
#
|
|
@@ -58,8 +58,8 @@ module Rigor
|
|
|
58
58
|
"by consuming `rigor-dry-types`'s `:dry_type_aliases` fact.",
|
|
59
59
|
# ADR-9 consumption — the precision-promotion path below uses `:dry_type_aliases` published by
|
|
60
60
|
# `rigor-dry-types`. The fact is optional: when the `rigor-dry-types` plugin isn't loaded, the
|
|
61
|
-
# `returns_from_arg:` lookup misses and the synthetic readers fall back to
|
|
62
|
-
# pre-ADR-18
|
|
61
|
+
# `returns_from_arg:` lookup misses and the synthetic readers fall back to the untyped floor (the
|
|
62
|
+
# pre-ADR-18 behaviour).
|
|
63
63
|
consumes: [{ plugin_id: "dry-types", name: :dry_type_aliases, optional: true }],
|
|
64
64
|
heredoc_templates: [
|
|
65
65
|
Rigor::Plugin::Macro::HeredocTemplate.new(
|
|
@@ -69,7 +69,7 @@ module Rigor
|
|
|
69
69
|
# ADR-18 — the synthetic reader's return type comes from the call site's second argument
|
|
70
70
|
# (`Types::String` etc.), resolved through the `:dry_type_aliases` fact. When the lookup
|
|
71
71
|
# misses (e.g. inline `attribute :tag, Types::String.constrained(...)`, whose receiver chain
|
|
72
|
-
# head isn't currently extracted), the row falls back to
|
|
72
|
+
# head isn't currently extracted), the row falls back to the untyped floor.
|
|
73
73
|
emit: [{
|
|
74
74
|
name: "\#{name}",
|
|
75
75
|
returns_from_arg: {
|
|
@@ -33,10 +33,9 @@ module Rigor
|
|
|
33
33
|
# `Coercible::*` (everything-to-target coercion), `Strict::*` (no coercion; raise if mismatch),
|
|
34
34
|
# `Params::*` (HTTP / query-string-style coercion, used by Hanami / Roda / dry-web in request
|
|
35
35
|
# handling), `JSON::*` (JSON-shape coercion). Each category exposes the same set of names as the
|
|
36
|
-
# canonical shortcuts above
|
|
37
|
-
#
|
|
38
|
-
# coercion semantics are a runtime concern.
|
|
39
|
-
# mapping the upstream gem publishes so call-site references work uniformly.
|
|
36
|
+
# canonical shortcuts above; only those names are registered here, so a category-specific extra
|
|
37
|
+
# is not covered. For Rigor's purposes the underlying class is the same regardless of category —
|
|
38
|
+
# coercion semantics are a runtime concern.
|
|
40
39
|
NESTED_CATEGORIES = %w[Coercible Strict Params JSON].freeze
|
|
41
40
|
private_constant :NESTED_CATEGORIES
|
|
42
41
|
|
|
@@ -229,8 +228,8 @@ module Rigor
|
|
|
229
228
|
|
|
230
229
|
# `include Dry.Types()` at the top of the module body is the canonical alias declaration. We
|
|
231
230
|
# accept the call anywhere in the body (some projects guard it with a `if defined?` check). The
|
|
232
|
-
# argument list
|
|
233
|
-
#
|
|
231
|
+
# `Dry.Types(...)` argument list is not inspected: kwargs such as `default: :nominal` install the
|
|
232
|
+
# aliases too.
|
|
234
233
|
def contains_dry_types_include?(body)
|
|
235
234
|
return false if body.nil?
|
|
236
235
|
|
|
@@ -21,7 +21,7 @@ module Rigor
|
|
|
21
21
|
# cross-plugin fact (ADR-9). Other dry-rb adapter plugins consume this fact:
|
|
22
22
|
#
|
|
23
23
|
# - `rigor-dry-struct` reads it so `attribute :city, Types::String` promotes `address.city` from
|
|
24
|
-
#
|
|
24
|
+
# from an untyped value to `String` via ADR-18's `returns_from_arg:` fact lookup.
|
|
25
25
|
# - `rigor-dry-validation` / `rigor-dry-schema` read it for per-key type recognition in `schema { … }`
|
|
26
26
|
# / `params { … }` blocks (separate plugin slice).
|
|
27
27
|
#
|
|
@@ -44,9 +44,8 @@ module Rigor
|
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
# Renders `Foo` / `Foo::Bar` / `::Foo::Bar` as the `::`-joined String {ContractScanner}'s table is
|
|
47
|
-
# keyed by. Duplicated from rigor-dry-schema
|
|
48
|
-
#
|
|
49
|
-
# guard here too even though this leaf never touches dry-schema's own type/alias machinery.
|
|
47
|
+
# keyed by. Duplicated from rigor-dry-schema rather than shared: this leaf must not depend on that
|
|
48
|
+
# plugin, and reusing the helper would need a `registered_for` guard here too.
|
|
50
49
|
def constant_name(node)
|
|
51
50
|
case node
|
|
52
51
|
when Prism::ConstantReadNode then node.name.to_s
|
|
@@ -58,19 +58,16 @@ module Rigor
|
|
|
58
58
|
|
|
59
59
|
# ADR-37 dynamic return — refines `NewUserContract.new.call(input).to_h` to the per-contract
|
|
60
60
|
# `HashShape` when {#result_shape_for} finds one. Gated on the method name alone (mirrors
|
|
61
|
-
# rigor-dry-schema's
|
|
62
|
-
# generic `Result
|
|
63
|
-
#
|
|
64
|
-
# rigor-dry-schema isn't loaded, or the chain isn't a Contract call, this returns nil and the RBS
|
|
65
|
-
# overlay's generic `Hash[Symbol, untyped]` stands.
|
|
61
|
+
# rigor-dry-schema's `.to_h` rule); the chain match is in {ParamsShape.contract_name}, because the
|
|
62
|
+
# RBS overlay's generic `Result` cannot select a per-contract shape. Otherwise returns nil and the
|
|
63
|
+
# overlay's generic hash type stands.
|
|
66
64
|
dynamic_return methods: [:to_h] do |call_node, _scope|
|
|
67
65
|
result_shape_for(call_node)
|
|
68
66
|
end
|
|
69
67
|
|
|
70
68
|
# ADR-37 slice 1c two-pass — the "collect" half of `dry-validation.rule-key-mismatch`. Reads the
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
-
# in-block memo isn't needed here the way it is across many per-call `dynamic_return` invocations).
|
|
69
|
+
# same `:dry_type_aliases` fact as `#prepare`, straight from `services.fact_store` (no `#read_fact`
|
|
70
|
+
# memo: `services` here is run-wide, unlike the per-call `dynamic_return` blocks).
|
|
74
71
|
node_file_context do |root, _scope|
|
|
75
72
|
type_aliases = services.fact_store.read(plugin_id: "dry-types", name: :dry_type_aliases) || {}
|
|
76
73
|
ContractScanner.rule_key_issues(root, type_aliases)
|
|
@@ -14,7 +14,6 @@ module Rigor
|
|
|
14
14
|
signature_paths: ["sig"]
|
|
15
15
|
)
|
|
16
16
|
|
|
17
|
-
# Option catalogue and return types for Easy methods
|
|
18
17
|
EASY_RETURN_TYPES = {
|
|
19
18
|
perform: -> { Rigor::Type::Combinator.nominal_of("Integer") },
|
|
20
19
|
response_code: -> { Rigor::Type::Combinator.nominal_of("Integer") },
|
|
@@ -115,7 +115,7 @@ module Rigor
|
|
|
115
115
|
# 3. Inflected from the factory name — `:user` → `"User"`, `:admin_user` → `"AdminUser"`. The
|
|
116
116
|
# factory name is already singular by FactoryBot convention, so we only need camelization.
|
|
117
117
|
#
|
|
118
|
-
# Returns
|
|
118
|
+
# Returns the canonical class name.
|
|
119
119
|
def factory_model_class(call_node, factory_name)
|
|
120
120
|
explicit = explicit_class_option(call_node)
|
|
121
121
|
return explicit if explicit
|
|
@@ -84,27 +84,20 @@ module Rigor
|
|
|
84
84
|
@factory_search_paths = Array(config.fetch("factory_search_paths")).map(&:to_s)
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
-
# ADR-102 WD3 / #350 — the model classes
|
|
88
|
-
#
|
|
87
|
+
# ADR-102 WD3 / #350 — the factories' model classes are published as `:reachability_references`,
|
|
88
|
+
# not `:reachability_roots`.
|
|
89
89
|
#
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
# It is a
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
# It is a REFERENCE rather than a root because factories live in the test tree. Rooting them would make
|
|
97
|
-
# every factoried class production-reachable and erase ADR-102 WD8's "reachable only from tests"
|
|
98
|
-
# category for exactly the classes it is most likely to be about — a model kept alive by its factory
|
|
99
|
-
# and its spec and nothing else is the archetype of dead production code with a live test, and that is
|
|
100
|
-
# the single most actionable row this report produces. Carrying the `:test` role keeps the finding.
|
|
90
|
+
# It is a contribution because a factory names its class by a mechanism the constant scan cannot
|
|
91
|
+
# follow (`factory :user, class: "Admin::User"` is a string; a bare `factory :user` is constantized by
|
|
92
|
+
# FactoryBot). It is a reference rather than a root because factories live in the test tree: rooting
|
|
93
|
+
# them would make every factoried class production-reachable and erase ADR-102 WD8's "reachable only
|
|
94
|
+
# from tests" category. The `:test` role keeps that finding.
|
|
101
95
|
def prepare(services)
|
|
102
96
|
index = producer_value(:factory_index)
|
|
103
97
|
return if index.nil? || index.empty?
|
|
104
98
|
|
|
105
|
-
# The producer value and the ADR-9 fact are different channels (#921)
|
|
106
|
-
#
|
|
107
|
-
# what makes it visible to another plugin's `read_fact`, which is how rigor-rspec binds
|
|
99
|
+
# The producer value and the ADR-9 fact are different channels (#921): `fact_store.publish` is what
|
|
100
|
+
# makes the index visible to another plugin's `read_fact`, as rigor-rspec uses it to bind
|
|
108
101
|
# `create(:name)` to the factory's model class.
|
|
109
102
|
services.fact_store.publish(plugin_id: manifest.id, name: :factory_index, value: index)
|
|
110
103
|
|
|
@@ -118,8 +111,7 @@ module Rigor
|
|
|
118
111
|
|
|
119
112
|
# ADR-37 — per-call factory/attribute validation over the engine-owned walk. Each violation carries
|
|
120
113
|
# its own location (the call's message_loc, or the offending attribute key), so it is positioned
|
|
121
|
-
# via `diagnostic(node, location:)`.
|
|
122
|
-
# `diagnostics_for_file`.
|
|
114
|
+
# via `diagnostic(node, location:)`.
|
|
123
115
|
node_rule Prism::CallNode do |node, _scope, path|
|
|
124
116
|
index = producer_value(:factory_index)
|
|
125
117
|
next [] if index.nil? || index.empty?
|
|
@@ -149,7 +149,6 @@ module Rigor
|
|
|
149
149
|
end
|
|
150
150
|
end
|
|
151
151
|
when :callback
|
|
152
|
-
# Issue 4 fix: extract callback definitions
|
|
153
152
|
args = node.arguments&.arguments || []
|
|
154
153
|
if args.size >= 3
|
|
155
154
|
cb_name = Analyzer.extract_symbol(args[0])
|
|
@@ -193,7 +192,6 @@ module Rigor
|
|
|
193
192
|
end
|
|
194
193
|
end
|
|
195
194
|
else
|
|
196
|
-
# Issue 5 fix: pass mod_name to sub-plugin recognizers
|
|
197
195
|
FFI.binding_recognizers.each do |recognizer|
|
|
198
196
|
facts = recognizer.recognize(node, mod_name)
|
|
199
197
|
Array(facts).each do |fact|
|
|
@@ -38,17 +38,17 @@ module Rigor
|
|
|
38
38
|
# Dynamic return rule for attached FFI functions
|
|
39
39
|
# Gated on known FFI library receivers and attached function names
|
|
40
40
|
#
|
|
41
|
-
# Issue #701 — both receiver kinds are declared because the block below already
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
# on a `receivers:` entry answering for both.
|
|
41
|
+
# Issue #701 — both receiver kinds are declared because the block below already resolves `Nominal`
|
|
42
|
+
# and `Singleton` alike: `attach_function` installs the binding on the library module itself
|
|
43
|
+
# (`MyLib.strlen`), and `include`ing that module makes the same name an instance call. The
|
|
44
|
+
# kind-aware gate makes that intent explicit rather than relying on one `receivers:` entry for both.
|
|
46
45
|
dynamic_return receivers: -> { both_receiver_kinds(producer_value(:ffi_catalog)&.libraries) },
|
|
47
46
|
methods: -> { producer_value(:ffi_catalog)&.function_method_names || [] } do |call_node, scope|
|
|
48
47
|
catalog = producer_value(:ffi_catalog)
|
|
49
48
|
next nil if catalog.nil?
|
|
50
49
|
|
|
51
|
-
|
|
50
|
+
receiver = call_node.receiver
|
|
51
|
+
receiver_type = receiver ? scope&.type_of(receiver) : scope&.self_type
|
|
52
52
|
receiver_class = case receiver_type
|
|
53
53
|
when Rigor::Type::Nominal, Rigor::Type::Singleton then receiver_type.class_name
|
|
54
54
|
end
|
|
@@ -78,7 +78,8 @@ module Rigor
|
|
|
78
78
|
catalog = producer_value(:ffi_catalog)
|
|
79
79
|
next nil if catalog.nil?
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
receiver = call_node.receiver
|
|
82
|
+
receiver_type = receiver ? scope&.type_of(receiver) : scope&.self_type
|
|
82
83
|
receiver_class = case receiver_type
|
|
83
84
|
when Rigor::Type::Nominal, Rigor::Type::Singleton then receiver_type.class_name
|
|
84
85
|
end
|
|
@@ -85,7 +85,6 @@ module Rigor
|
|
|
85
85
|
method_names: NAMESPACE_METHODS,
|
|
86
86
|
self_type: "singleton(Grape::API::Instance)"
|
|
87
87
|
),
|
|
88
|
-
# `params do ... end` — body `instance_eval`s on a `ParamsScope` instance.
|
|
89
88
|
Rigor::Plugin::Macro::BlockAsMethod.new(
|
|
90
89
|
receiver_constraint: "Grape::API",
|
|
91
90
|
method_names: %i[params],
|
|
@@ -121,7 +120,6 @@ module Rigor
|
|
|
121
120
|
method_names: %i[desc],
|
|
122
121
|
self_type: "Grape::DSL::Desc::ConfigContext"
|
|
123
122
|
),
|
|
124
|
-
# Verb bodies run inside `Grape::Endpoint` instances.
|
|
125
123
|
Rigor::Plugin::Macro::BlockAsMethod.new(
|
|
126
124
|
receiver_constraint: "Grape::API",
|
|
127
125
|
method_names: VERB_METHODS,
|
|
@@ -23,9 +23,8 @@ module Rigor
|
|
|
23
23
|
}.freeze
|
|
24
24
|
|
|
25
25
|
# The base class name a Schema::Object subclass MUST inherit from to be recognised. Match is on
|
|
26
|
-
# the
|
|
27
|
-
#
|
|
28
|
-
# the canonical path.
|
|
26
|
+
# the last two segments of the superclass constant chain (see {#schema_subclass?}), so a locally
|
|
27
|
+
# aliased base such as `BaseObject = GraphQL::Schema::Object` is not followed.
|
|
29
28
|
SCHEMA_OBJECT_TAIL = "Object"
|
|
30
29
|
SCHEMA_ENUM_TAIL = "Enum"
|
|
31
30
|
SCHEMA_INPUT_OBJECT_TAIL = "InputObject"
|
|
@@ -183,7 +182,7 @@ module Rigor
|
|
|
183
182
|
private_class_method :statement_nodes
|
|
184
183
|
|
|
185
184
|
# Walks every top-level `value "..."` call inside an enum subclass body and returns the value
|
|
186
|
-
# names
|
|
185
|
+
# names. Both shapes graphql-ruby accepts work:
|
|
187
186
|
#
|
|
188
187
|
# value "ACTIVE"
|
|
189
188
|
# value "DISABLED", value: :off, description: "..."
|
|
@@ -299,7 +298,7 @@ module Rigor
|
|
|
299
298
|
end
|
|
300
299
|
private_class_method :parse_field_call
|
|
301
300
|
|
|
302
|
-
# Resolves the `Type` positional argument to a `
|
|
301
|
+
# Resolves the `Type` positional argument to a `type:` class name and a `list:` flag.
|
|
303
302
|
# ArrayNode forms (`[String]` / `[Types::User]`) unwrap the single element and mark `list: true`.
|
|
304
303
|
# Bare constant refs are not lists. Returns nil for unrecognised shapes (string types `"User"`,
|
|
305
304
|
# Proc lazy types, etc.) so callers drop the field.
|
|
@@ -350,8 +349,8 @@ module Rigor
|
|
|
350
349
|
end
|
|
351
350
|
private_class_method :extract_nullability
|
|
352
351
|
|
|
353
|
-
# Returns the constant chain
|
|
354
|
-
#
|
|
352
|
+
# Returns the constant chain's segments, e.g. `["GraphQL", "Schema", "Object"]`. Empty for
|
|
353
|
+
# unrecognised node kinds.
|
|
355
354
|
def constant_path_segments(node)
|
|
356
355
|
case node
|
|
357
356
|
when Prism::ConstantReadNode then [node.name.to_s]
|
|
@@ -77,8 +77,18 @@ module Rigor
|
|
|
77
77
|
signature_paths: ["sig"],
|
|
78
78
|
# ADR-43 WD4 — the classes the bundled sig covers completely enough that a Ruby-source subclass
|
|
79
79
|
# (`class PostType < GraphQL::Schema::Object`) may bridge inherited calls to the RBS ancestor.
|
|
80
|
-
#
|
|
81
|
-
#
|
|
80
|
+
# The bridge is a signature lookup only: an inherited DSL call resolves to the declaration's
|
|
81
|
+
# return type and block parameters, and a subclass `def` overriding a declared method other
|
|
82
|
+
# than `initialize` binds its parameters from it (#2681). The subclass itself stays outside
|
|
83
|
+
# RBS, so no signature-reading call rule fires on a call it makes, declared or not:
|
|
84
|
+
# `call.undefined-method`, `call.wrong-arity` and `call.argument-type-mismatch` all stay
|
|
85
|
+
# silent, while the same calls on a listed class AS THE DIRECT RECEIVER still report.
|
|
86
|
+
# `open_receivers:` is what covers those direct receivers; it is not what silences the
|
|
87
|
+
# subclass — the receiver gate is (ADR-43 Context § "Amendment 2026-11-15", which withdrew
|
|
88
|
+
# the claim that the rules follow the resolution). So listing a class here cannot cost a
|
|
89
|
+
# false positive on a subclass. The risk runs the other way: a declared member that does not exist at runtime makes a genuinely
|
|
90
|
+
# failing call resolve silently, so the sig must be accurate — it IS the authority
|
|
91
|
+
# (graphql-ruby ships none).
|
|
82
92
|
rbs_complete_ancestors: %w[
|
|
83
93
|
GraphQL::Schema
|
|
84
94
|
GraphQL::Schema::Member
|
|
@@ -20,10 +20,8 @@ module Rigor
|
|
|
20
20
|
@contracts = contracts
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
# Per-`ClassNode` check over the engine-owned walk (ADR-37): called once per class node the
|
|
24
|
-
# `node_rule` in `hanami.rb
|
|
25
|
-
# file. No cross-class-node state is needed, so the checker ships no `class_nodes` traversal of
|
|
26
|
-
# its own.
|
|
23
|
+
# Per-`ClassNode` check over the engine-owned walk (ADR-37): called once per class node by the
|
|
24
|
+
# `node_rule` in `hanami.rb`, against every contract whose `path_glob` matches the file.
|
|
27
25
|
def check_class(class_node, path:)
|
|
28
26
|
@contracts.filter_map do |contract|
|
|
29
27
|
next unless path_matches?(contract.path_glob, path)
|
|
@@ -87,11 +87,8 @@ module Rigor
|
|
|
87
87
|
@protocol_contracts || manifest.protocol_contracts
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
# ADR-37 — per-class-node validation over the engine-owned walk
|
|
91
|
-
#
|
|
92
|
-
# traversal; `ActionChecker#check_class` keeps the per-class contract logic. (A per-class contract
|
|
93
|
-
# check is exactly what `node_rule` is for — the return type is void, so no `scope` query is
|
|
94
|
-
# needed.)
|
|
90
|
+
# ADR-37 — per-class-node validation over the engine-owned walk; `ActionChecker#check_class` holds the
|
|
91
|
+
# per-class contract logic. The return type is void, so no `scope` query is needed.
|
|
95
92
|
node_rule Prism::ClassNode do |node, _scope, path|
|
|
96
93
|
contracts = protocol_contracts
|
|
97
94
|
next [] if contracts.empty?
|
|
@@ -51,8 +51,8 @@ module Rigor
|
|
|
51
51
|
#
|
|
52
52
|
# - `is_a?(Result::Ok)` / `Some` / `None` exhaustive narrowing — core control-flow analysis over a sealed
|
|
53
53
|
# hierarchy, not a plugin surface.
|
|
54
|
-
# - The `variants do variant Const, Type end` Enum DSL is
|
|
55
|
-
# this plugin's manifest (Slice A
|
|
54
|
+
# - The `variants do variant Const, Type end` Enum DSL is not part of the unwrap contribution; it is
|
|
55
|
+
# synthesised through the ADR-36 `nested_class_templates:` in this plugin's manifest (Slice A).
|
|
56
56
|
class Mangrove < Rigor::Plugin::Base
|
|
57
57
|
manifest(
|
|
58
58
|
id: "mangrove",
|
|
@@ -113,7 +113,13 @@ module Rigor
|
|
|
113
113
|
# ADR-37 slice 2 — a pure return-type contributor. The engine gates on the receiver being a known
|
|
114
114
|
# carrier class (so the block only runs on Result/Option receivers); the block then confirms the call
|
|
115
115
|
# is an unwrap method on an instantiated carrier and yields the carried `type_args[0]`.
|
|
116
|
+
# #2717 — the rule has no `methods:` gate, so the name gate does not screen it: the engine also offers it
|
|
117
|
+
# the compound-write nodes (`x.attr op= v`, `x[i] ||= v`, ...) and the `&:symbol` block's
|
|
118
|
+
# `Prism::BlockArgumentNode`. None of those has `name`, which the rule reads after `receiver`, so decline
|
|
119
|
+
# anything that is not a `CallNode` rather than raise into the rescue.
|
|
116
120
|
dynamic_return receivers: (RESULT_CARRIERS + OPTION_CARRIERS) do |call_node, scope|
|
|
121
|
+
next nil unless call_node.is_a?(Prism::CallNode)
|
|
122
|
+
|
|
117
123
|
receiver = call_node.receiver
|
|
118
124
|
next nil if receiver.nil?
|
|
119
125
|
|