rigortype 0.2.6 → 0.2.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 -3
- data/docs/manual/02-cli-reference.md +2 -1
- data/docs/manual/04-diagnostics.md +7 -4
- data/docs/manual/06-baseline.md +5 -2
- data/docs/manual/08-skills.md +21 -0
- data/docs/manual/15-type-protection-coverage.md +6 -4
- data/lib/rigor/analysis/baseline.rb +56 -96
- data/lib/rigor/analysis/buffer_binding.rb +13 -20
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +29 -48
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +35 -54
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +23 -37
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +16 -23
- data/lib/rigor/analysis/check_rules/rule_walk.rb +48 -71
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +19 -28
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +48 -67
- data/lib/rigor/analysis/check_rules.rb +174 -274
- data/lib/rigor/analysis/dependency_recorder.rb +26 -38
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +13 -25
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +17 -30
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +15 -26
- data/lib/rigor/analysis/dependency_source_inference/index.rb +45 -76
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +23 -40
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +50 -83
- data/lib/rigor/analysis/dependency_source_inference.rb +6 -10
- data/lib/rigor/analysis/diagnostic.rb +48 -75
- data/lib/rigor/analysis/erb_template_detector.rb +13 -18
- data/lib/rigor/analysis/fact_store.rb +12 -22
- data/lib/rigor/analysis/incremental.rb +47 -63
- data/lib/rigor/analysis/incremental_session.rb +74 -100
- data/lib/rigor/analysis/project_scan.rb +12 -23
- data/lib/rigor/analysis/result.rb +3 -5
- data/lib/rigor/analysis/rule_catalog.rb +67 -95
- data/lib/rigor/analysis/run_stats.rb +38 -61
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +101 -160
- data/lib/rigor/analysis/runner/pool_coordinator.rb +121 -188
- data/lib/rigor/analysis/runner/project_pre_passes.rb +62 -96
- data/lib/rigor/analysis/runner/run_snapshots.rb +12 -18
- data/lib/rigor/analysis/runner.rb +220 -333
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +30 -43
- data/lib/rigor/analysis/worker_session.rb +64 -102
- data/lib/rigor/ast/type_node.rb +6 -8
- data/lib/rigor/ast.rb +6 -10
- data/lib/rigor/bleeding_edge.rb +27 -35
- data/lib/rigor/builtins/hkt_builtins.rb +7 -14
- data/lib/rigor/builtins/imported_refinements.rb +106 -149
- data/lib/rigor/builtins/predefined_constant_refinements.rb +51 -65
- data/lib/rigor/builtins/regex_refinement.rb +32 -43
- data/lib/rigor/builtins/static_return_refinements.rb +57 -81
- data/lib/rigor/cache/descriptor.rb +58 -94
- data/lib/rigor/cache/incremental_snapshot.rb +32 -48
- data/lib/rigor/cache/rbs_cache_producer.rb +10 -15
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +10 -16
- data/lib/rigor/cache/rbs_class_type_param_names.rb +10 -16
- data/lib/rigor/cache/rbs_constant_table.rb +8 -13
- data/lib/rigor/cache/rbs_descriptor.rb +8 -13
- data/lib/rigor/cache/rbs_environment.rb +11 -19
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +11 -19
- data/lib/rigor/cache/rbs_known_class_names.rb +8 -12
- data/lib/rigor/cache/store.rb +96 -151
- data/lib/rigor/cli/annotate_command.rb +51 -88
- data/lib/rigor/cli/baseline_command.rb +10 -17
- data/lib/rigor/cli/check_command.rb +121 -199
- data/lib/rigor/cli/check_runner_factory.rb +8 -14
- data/lib/rigor/cli/ci_detector.rb +9 -14
- data/lib/rigor/cli/command.rb +8 -14
- data/lib/rigor/cli/coverage_command.rb +45 -20
- data/lib/rigor/cli/coverage_mutation.rb +14 -19
- data/lib/rigor/cli/coverage_report.rb +0 -2
- data/lib/rigor/cli/coverage_scan.rb +7 -11
- data/lib/rigor/cli/diagnostic_formats.rb +43 -62
- data/lib/rigor/cli/diff_command.rb +10 -20
- data/lib/rigor/cli/docs_command.rb +26 -40
- data/lib/rigor/cli/doctor_command.rb +8 -9
- data/lib/rigor/cli/explain_command.rb +4 -7
- data/lib/rigor/cli/fused_protection_renderer.rb +4 -5
- data/lib/rigor/cli/fused_protection_report.rb +6 -8
- data/lib/rigor/cli/lsp_command.rb +11 -19
- data/lib/rigor/cli/mcp_command.rb +4 -6
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -4
- data/lib/rigor/cli/mutation_protection_report.rb +6 -9
- data/lib/rigor/cli/options.rb +11 -19
- data/lib/rigor/cli/plugin_command.rb +18 -30
- data/lib/rigor/cli/plugins_command.rb +29 -51
- data/lib/rigor/cli/plugins_renderer.rb +12 -20
- data/lib/rigor/cli/prism_colorizer.rb +13 -19
- data/lib/rigor/cli/protection_renderer.rb +5 -7
- data/lib/rigor/cli/protection_report.rb +27 -17
- data/lib/rigor/cli/renderable.rb +4 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +5 -8
- data/lib/rigor/cli/sig_gen_command.rb +14 -26
- data/lib/rigor/cli/skill_command.rb +70 -36
- data/lib/rigor/cli/skill_describe.rb +40 -64
- data/lib/rigor/cli/trace_command.rb +9 -16
- data/lib/rigor/cli/trace_renderer.rb +35 -51
- data/lib/rigor/cli/triage_command.rb +6 -10
- data/lib/rigor/cli/triage_renderer.rb +1 -2
- data/lib/rigor/cli/type_of_command.rb +14 -23
- data/lib/rigor/cli/type_of_renderer.rb +3 -5
- data/lib/rigor/cli/type_scan_command.rb +6 -10
- data/lib/rigor/cli/type_scan_renderer.rb +3 -5
- data/lib/rigor/cli/type_scan_report.rb +2 -3
- data/lib/rigor/cli/upgrade_command.rb +2 -3
- data/lib/rigor/cli.rb +16 -28
- data/lib/rigor/config_audit.rb +35 -44
- data/lib/rigor/configuration/dependencies.rb +41 -70
- data/lib/rigor/configuration/severity_profile.rb +28 -42
- data/lib/rigor/configuration.rb +150 -241
- data/lib/rigor/environment/bundle_sig_discovery.rb +74 -118
- data/lib/rigor/environment/class_registry.rb +19 -26
- data/lib/rigor/environment/constant_type_cache_holder.rb +14 -21
- data/lib/rigor/environment/hkt_registry_holder.rb +6 -12
- data/lib/rigor/environment/lockfile_resolver.rb +31 -47
- data/lib/rigor/environment/rbs_collection_discovery.rb +39 -63
- data/lib/rigor/environment/rbs_coverage_report.rb +24 -42
- data/lib/rigor/environment/rbs_loader.rb +433 -503
- data/lib/rigor/environment/reflection.rb +28 -53
- data/lib/rigor/environment/reporters.rb +13 -24
- data/lib/rigor/environment.rb +176 -291
- data/lib/rigor/flow_contribution/conflict.rb +16 -27
- data/lib/rigor/flow_contribution/element.rb +7 -12
- data/lib/rigor/flow_contribution/fact.rb +33 -57
- data/lib/rigor/flow_contribution/merge_result.rb +6 -9
- data/lib/rigor/flow_contribution/merger.rb +32 -47
- data/lib/rigor/flow_contribution.rb +37 -55
- data/lib/rigor/inference/acceptance.rb +133 -219
- data/lib/rigor/inference/block_parameter_binder.rb +52 -82
- data/lib/rigor/inference/body_fixpoint.rb +22 -30
- data/lib/rigor/inference/budget_trace.rb +43 -63
- data/lib/rigor/inference/builtins/array_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/comparable_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/complex_catalog.rb +13 -19
- data/lib/rigor/inference/builtins/date_catalog.rb +38 -67
- data/lib/rigor/inference/builtins/encoding_catalog.rb +22 -36
- data/lib/rigor/inference/builtins/enumerable_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/exception_catalog.rb +38 -57
- data/lib/rigor/inference/builtins/hash_catalog.rb +9 -14
- data/lib/rigor/inference/builtins/method_catalog.rb +29 -48
- data/lib/rigor/inference/builtins/numeric_catalog.rb +13 -18
- data/lib/rigor/inference/builtins/pathname_catalog.rb +8 -13
- data/lib/rigor/inference/builtins/proc_catalog.rb +38 -62
- data/lib/rigor/inference/builtins/random_catalog.rb +21 -32
- data/lib/rigor/inference/builtins/range_catalog.rb +12 -21
- data/lib/rigor/inference/builtins/rational_catalog.rb +9 -15
- data/lib/rigor/inference/builtins/re_catalog.rb +29 -48
- data/lib/rigor/inference/builtins/set_catalog.rb +18 -27
- data/lib/rigor/inference/builtins/string_catalog.rb +12 -19
- data/lib/rigor/inference/builtins/struct_catalog.rb +18 -30
- data/lib/rigor/inference/builtins/time_catalog.rb +28 -44
- data/lib/rigor/inference/closure_escape_analyzer.rb +44 -67
- data/lib/rigor/inference/coverage_scanner.rb +13 -18
- data/lib/rigor/inference/def_return_typer.rb +8 -14
- data/lib/rigor/inference/dynamic_origin.rb +23 -20
- data/lib/rigor/inference/expression_typer.rb +774 -1152
- data/lib/rigor/inference/fallback.rb +8 -12
- data/lib/rigor/inference/fallback_tracer.rb +4 -10
- data/lib/rigor/inference/flow_tracer.rb +25 -36
- data/lib/rigor/inference/hkt_body.rb +45 -68
- data/lib/rigor/inference/hkt_body_parser.rb +33 -52
- data/lib/rigor/inference/hkt_reducer.rb +38 -59
- data/lib/rigor/inference/hkt_registry.rb +50 -76
- data/lib/rigor/inference/indexed_narrowing.rb +55 -81
- data/lib/rigor/inference/macro_block_self_type.rb +21 -34
- data/lib/rigor/inference/method_dispatcher/array_to_h_folding.rb +11 -18
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +49 -83
- data/lib/rigor/inference/method_dispatcher/call_context.rb +16 -23
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +13 -22
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +307 -454
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +24 -34
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +22 -37
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +52 -81
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +51 -78
- data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +60 -97
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +23 -34
- data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +18 -26
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +37 -71
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +114 -182
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +122 -193
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +15 -24
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +64 -93
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +31 -45
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +6 -9
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +356 -511
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +22 -32
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +15 -21
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +66 -82
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +10 -15
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +9 -13
- data/lib/rigor/inference/method_dispatcher.rb +378 -524
- data/lib/rigor/inference/method_parameter_binder.rb +81 -114
- data/lib/rigor/inference/multi_target_binder.rb +51 -68
- data/lib/rigor/inference/mutation_widening.rb +115 -158
- data/lib/rigor/inference/narrowing.rb +509 -727
- data/lib/rigor/inference/origin_lookup.rb +38 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +100 -100
- data/lib/rigor/inference/precision_scanner.rb +26 -35
- data/lib/rigor/inference/project_patched_methods.rb +20 -32
- data/lib/rigor/inference/project_patched_scanner.rb +23 -37
- data/lib/rigor/inference/protection_scanner.rb +16 -18
- data/lib/rigor/inference/rbs_type_translator.rb +49 -66
- data/lib/rigor/inference/scope_indexer.rb +473 -817
- data/lib/rigor/inference/statement_evaluator.rb +706 -1115
- data/lib/rigor/inference/struct_fold_safety.rb +34 -48
- data/lib/rigor/inference/synthetic_method.rb +9 -16
- data/lib/rigor/inference/synthetic_method_index.rb +20 -35
- data/lib/rigor/inference/synthetic_method_scanner.rb +63 -103
- data/lib/rigor/language_server/buffer_resolution.rb +6 -9
- data/lib/rigor/language_server/buffer_table.rb +11 -18
- data/lib/rigor/language_server/completion_provider.rb +69 -116
- data/lib/rigor/language_server/debouncer.rb +16 -25
- data/lib/rigor/language_server/diagnostic_publisher.rb +26 -40
- data/lib/rigor/language_server/document_symbol_provider.rb +14 -19
- data/lib/rigor/language_server/folding_range_provider.rb +11 -19
- data/lib/rigor/language_server/hover_provider.rb +8 -11
- data/lib/rigor/language_server/hover_renderer.rb +56 -93
- data/lib/rigor/language_server/loop.rb +14 -19
- data/lib/rigor/language_server/project_context.rb +46 -74
- data/lib/rigor/language_server/selection_range_provider.rb +9 -12
- data/lib/rigor/language_server/server.rb +55 -83
- data/lib/rigor/language_server/signature_help_provider.rb +29 -46
- data/lib/rigor/language_server/synchronized_writer.rb +4 -7
- data/lib/rigor/language_server/uri.rb +8 -13
- data/lib/rigor/language_server.rb +4 -6
- data/lib/rigor/mcp/loop.rb +2 -3
- data/lib/rigor/mcp/server.rb +4 -7
- data/lib/rigor/mcp.rb +3 -6
- data/lib/rigor/plugin/access_denied_error.rb +5 -8
- data/lib/rigor/plugin/additional_initializer.rb +21 -31
- data/lib/rigor/plugin/base.rb +335 -518
- data/lib/rigor/plugin/blueprint.rb +14 -23
- data/lib/rigor/plugin/box.rb +18 -29
- data/lib/rigor/plugin/fact_store.rb +16 -26
- data/lib/rigor/plugin/inflector.rb +37 -53
- data/lib/rigor/plugin/io_boundary.rb +33 -56
- data/lib/rigor/plugin/isolation.rb +42 -55
- data/lib/rigor/plugin/load_error.rb +10 -15
- data/lib/rigor/plugin/loader.rb +30 -49
- data/lib/rigor/plugin/macro/block_as_method.rb +20 -32
- data/lib/rigor/plugin/macro/heredoc_template.rb +35 -58
- data/lib/rigor/plugin/macro/nested_class_template.rb +22 -36
- data/lib/rigor/plugin/macro/trait_registry.rb +34 -51
- data/lib/rigor/plugin/macro.rb +10 -15
- data/lib/rigor/plugin/manifest.rb +85 -144
- data/lib/rigor/plugin/node_context.rb +14 -22
- data/lib/rigor/plugin/node_rule_walk.rb +49 -74
- data/lib/rigor/plugin/protocol_contract.rb +25 -39
- data/lib/rigor/plugin/registry.rb +132 -205
- data/lib/rigor/plugin/services.rb +21 -33
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -19
- data/lib/rigor/plugin/trust_policy.rb +24 -38
- data/lib/rigor/plugin/type_node_resolver.rb +15 -24
- data/lib/rigor/protection/diagnostic_oracle.rb +11 -13
- data/lib/rigor/protection/mutation_scanner.rb +27 -35
- data/lib/rigor/protection/mutator.rb +50 -70
- data/lib/rigor/protection/test_suite_oracle.rb +20 -27
- data/lib/rigor/rbs_extended/conformance_checker.rb +48 -68
- data/lib/rigor/rbs_extended/hkt_directives.rb +28 -54
- data/lib/rigor/rbs_extended/reporter.rb +24 -40
- data/lib/rigor/rbs_extended.rb +107 -197
- data/lib/rigor/reflection.rb +68 -86
- data/lib/rigor/scope/discovery_index.rb +14 -19
- data/lib/rigor/scope.rb +255 -310
- data/lib/rigor/sig_gen/classification.rb +6 -10
- data/lib/rigor/sig_gen/generator.rb +208 -309
- data/lib/rigor/sig_gen/layout_index.rb +12 -20
- data/lib/rigor/sig_gen/method_candidate.rb +15 -15
- data/lib/rigor/sig_gen/observation_collector.rb +38 -70
- data/lib/rigor/sig_gen/observed_call.rb +13 -23
- data/lib/rigor/sig_gen/path_mapper.rb +17 -29
- data/lib/rigor/sig_gen/renderer.rb +7 -13
- data/lib/rigor/sig_gen/type_elaborator.rb +15 -28
- data/lib/rigor/sig_gen/write_result.rb +8 -16
- data/lib/rigor/sig_gen/writer.rb +141 -173
- data/lib/rigor/sig_gen.rb +3 -6
- data/lib/rigor/signature_path_audit.rb +24 -30
- data/lib/rigor/source/constant_path.rb +10 -14
- data/lib/rigor/source/literals.rb +31 -45
- data/lib/rigor/source/node_locator.rb +9 -11
- data/lib/rigor/source/node_walker.rb +9 -13
- data/lib/rigor/source.rb +3 -4
- data/lib/rigor/testing.rb +16 -20
- data/lib/rigor/triage/catalogue.rb +38 -62
- data/lib/rigor/triage.rb +31 -52
- data/lib/rigor/trinary.rb +9 -13
- data/lib/rigor/type/acceptance_router.rb +4 -6
- data/lib/rigor/type/accepts_result.rb +10 -14
- data/lib/rigor/type/app.rb +19 -27
- data/lib/rigor/type/bot.rb +4 -6
- data/lib/rigor/type/bound_method.rb +10 -15
- data/lib/rigor/type/combinator.rb +165 -257
- data/lib/rigor/type/constant.rb +23 -34
- data/lib/rigor/type/data_class.rb +10 -15
- data/lib/rigor/type/data_instance.rb +14 -20
- data/lib/rigor/type/difference.rb +21 -32
- data/lib/rigor/type/dynamic.rb +3 -5
- data/lib/rigor/type/hash_shape.rb +32 -18
- data/lib/rigor/type/integer_range.rb +11 -16
- data/lib/rigor/type/intersection.rb +27 -42
- data/lib/rigor/type/nominal.rb +10 -15
- data/lib/rigor/type/plain_lattice.rb +9 -13
- data/lib/rigor/type/refined.rb +67 -114
- data/lib/rigor/type/singleton.rb +4 -6
- data/lib/rigor/type/struct_class.rb +11 -16
- data/lib/rigor/type/struct_instance.rb +15 -21
- data/lib/rigor/type/tuple.rb +14 -19
- data/lib/rigor/type/union.rb +30 -42
- data/lib/rigor/type_node/generic.rb +14 -26
- data/lib/rigor/type_node/identifier.rb +12 -19
- data/lib/rigor/type_node.rb +3 -12
- data/lib/rigor/value_semantics.rb +16 -21
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +18 -30
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +16 -29
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +12 -23
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +22 -39
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +26 -46
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +51 -87
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +10 -17
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +26 -39
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +118 -176
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +30 -51
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +57 -92
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +107 -83
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +13 -22
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +15 -25
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +8 -13
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +16 -24
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +30 -47
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +79 -129
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +36 -57
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +27 -41
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +16 -24
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +148 -236
- data/plugins/rigor-activerecord/lib/rigor-activerecord.rb +3 -5
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +8 -14
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +9 -15
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +4 -7
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +23 -41
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +3 -5
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +6 -10
- data/plugins/rigor-activesupport-core-ext/lib/rigor-activesupport-core-ext.rb +8 -11
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +30 -45
- data/plugins/rigor-devise/lib/rigor-devise.rb +3 -5
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +43 -69
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +26 -35
- data/plugins/rigor-dry-schema/lib/rigor-dry-schema.rb +3 -5
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +30 -47
- data/plugins/rigor-dry-struct/lib/rigor-dry-struct.rb +3 -5
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +67 -113
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +29 -46
- data/plugins/rigor-dry-types/lib/rigor-dry-types.rb +3 -5
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -20
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +14 -23
- data/plugins/rigor-dry-validation/lib/rigor-dry-validation.rb +2 -3
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +22 -37
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +28 -46
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +13 -17
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +30 -47
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +52 -80
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +21 -31
- data/plugins/rigor-graphql/lib/rigor-graphql.rb +3 -5
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +20 -36
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +23 -30
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +45 -75
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +22 -36
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +22 -41
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +17 -32
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +6 -12
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +4 -8
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +17 -32
- data/plugins/rigor-rails/lib/rigor-rails.rb +11 -18
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +58 -92
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +20 -34
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +20 -31
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +49 -82
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +55 -92
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +51 -90
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +17 -30
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +38 -70
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +42 -74
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +280 -497
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +54 -89
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +32 -54
- data/plugins/rigor-rbs-inline/lib/rigor-rbs-inline.rb +8 -13
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +9 -16
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +24 -39
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +13 -22
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +34 -56
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +22 -42
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +43 -75
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +27 -39
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +14 -19
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +20 -34
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +28 -47
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +21 -37
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +13 -22
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +18 -31
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +6 -10
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +14 -29
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +23 -41
- data/plugins/rigor-sinatra/lib/rigor-sinatra.rb +3 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +23 -38
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +57 -96
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +22 -35
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +41 -69
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +6 -10
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +24 -41
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +29 -50
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +55 -88
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +182 -328
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +21 -29
- data/plugins/rigor-statesman/lib/rigor-statesman.rb +3 -5
- data/plugins/rigor-typescript-utility-types/lib/rigor/plugin/typescript_utility_types.rb +23 -37
- data/plugins/rigor-typescript-utility-types/lib/rigor-typescript-utility-types.rb +4 -6
- data/sig/rigor/scope.rbs +6 -0
- data/skills/rigor-ask/SKILL.md +21 -1
- data/skills/rigor-baseline-reduce/SKILL.md +16 -0
- data/skills/rigor-ci-setup/SKILL.md +96 -249
- data/skills/rigor-doctor/SKILL.md +39 -49
- data/skills/rigor-doctor/references/01-checks.md +52 -0
- data/skills/rigor-editor-setup/SKILL.md +14 -0
- data/skills/rigor-mcp-setup/SKILL.md +14 -0
- data/skills/rigor-monkeypatch-resolve/SKILL.md +15 -0
- data/skills/rigor-plugin-author/SKILL.md +16 -0
- data/skills/rigor-plugin-review/SKILL.md +174 -0
- data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +214 -0
- data/skills/rigor-plugin-tune/SKILL.md +21 -2
- data/skills/rigor-project-init/SKILL.md +16 -0
- data/skills/rigor-protection-uplift/SKILL.md +15 -0
- data/skills/rigor-rbs-setup/SKILL.md +15 -0
- data/skills/rigor-upgrade/SKILL.md +16 -0
- metadata +8 -4
|
@@ -8,72 +8,61 @@ require_relative "rbs_hierarchy"
|
|
|
8
8
|
|
|
9
9
|
module Rigor
|
|
10
10
|
class Environment
|
|
11
|
-
# Loads RBS class declarations and method definitions from disk and
|
|
12
|
-
#
|
|
11
|
+
# Loads RBS class declarations and method definitions from disk and exposes them to the inference engine
|
|
12
|
+
# in a small, stable surface.
|
|
13
13
|
#
|
|
14
|
-
# Slice 4 phase 1 only enabled the RBS *core* signatures shipped with
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# `
|
|
18
|
-
#
|
|
19
|
-
# default on `RbsLoader.default` so the core-only fast path stays
|
|
20
|
-
# cheap; project-aware loading is opted into through
|
|
21
|
-
# {Environment.for_project} or by constructing a custom loader.
|
|
14
|
+
# Slice 4 phase 1 only enabled the RBS *core* signatures shipped with the `rbs` gem (`Object`, `Integer`,
|
|
15
|
+
# `String`, `Array`, ...). Phase 2a adds opt-in stdlib library loading (`pathname`, `json`, `tempfile`,
|
|
16
|
+
# ...) and arbitrary-directory signature loading (typically the project's local `sig/` tree). Both are
|
|
17
|
+
# off by default on `RbsLoader.default` so the core-only fast path stays cheap; project-aware loading is
|
|
18
|
+
# opted into through {Environment.for_project} or by constructing a custom loader.
|
|
22
19
|
#
|
|
23
|
-
# The default instance is shared across the process: building the
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
# and `RBS::DefinitionBuilder` -- the user-visible API stays purely
|
|
28
|
-
# functional.
|
|
20
|
+
# The default instance is shared across the process: building the core RBS environment costs hundreds of
|
|
21
|
+
# milliseconds and the data is read-only. The shared instance is frozen, but holds a mutable state hash
|
|
22
|
+
# for lazy memoization of the heavy `RBS::Environment` and `RBS::DefinitionBuilder` -- the user-visible
|
|
23
|
+
# API stays purely functional.
|
|
29
24
|
#
|
|
30
25
|
# See docs/internal-spec/inference-engine.md for the binding contract.
|
|
31
26
|
# rubocop:disable Metrics/ClassLength
|
|
32
27
|
class RbsLoader
|
|
33
|
-
# Buffer name stamped on the `module` declarations synthesized by
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
# — robust across the marshalled env cache, since the sentinel
|
|
38
|
-
# rides along on each synthetic declaration's location.
|
|
28
|
+
# Buffer name stamped on the `module` declarations synthesized by {.synthesize_missing_namespaces}.
|
|
29
|
+
# Re-read off the built env by {#synthesized_namespaces} so the analysis layer can surface an `:info`
|
|
30
|
+
# diagnostic naming the project's malformed-RBS namespaces — robust across the marshalled env cache,
|
|
31
|
+
# since the sentinel rides along on each synthetic declaration's location.
|
|
39
32
|
SYNTHETIC_NAMESPACE_BUFFER = "(rigor: synthesized namespaces)"
|
|
40
33
|
|
|
41
|
-
# Buffer name stamped on the stub `class` / `module` declarations
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
# {#
|
|
45
|
-
# the
|
|
46
|
-
# {#synthesized_type_names} folds them together with the
|
|
47
|
-
# namespace stubs into the set {MethodDispatcher} resolves to
|
|
48
|
-
# `Dynamic[Top]` (no false `call.undefined-method`).
|
|
34
|
+
# Buffer name stamped on the stub `class` / `module` declarations synthesized by
|
|
35
|
+
# {.stub_missing_referenced_types} for types the project's RBS references but no loaded signature
|
|
36
|
+
# declares. {#synthesized_stub_types} reads them back off the built env (so the answer survives the
|
|
37
|
+
# marshalled env cache), and {#synthesized_type_names} folds them together with the namespace stubs
|
|
38
|
+
# into the set {MethodDispatcher} resolves to `Dynamic[Top]` (no false `call.undefined-method`).
|
|
49
39
|
SYNTHETIC_STUB_BUFFER = "(rigor: synthesized stub types)"
|
|
50
40
|
|
|
41
|
+
# Cap on how many quarantined `signature_paths:` files {#warn_about_quarantined_signatures} lists by name
|
|
42
|
+
# before collapsing the tail to "… and N more" — a broken generator can emit many, and a wall of parse
|
|
43
|
+
# errors buries the signal.
|
|
44
|
+
QUARANTINE_WARN_LIMIT = 10
|
|
45
|
+
|
|
51
46
|
class << self
|
|
52
47
|
def default
|
|
53
48
|
@default ||= new.freeze
|
|
54
49
|
end
|
|
55
50
|
|
|
56
|
-
# Used by tests to discard the cached default loader; production
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
# cost of rebuilding it.
|
|
51
|
+
# Used by tests to discard the cached default loader; production code MUST NOT call this. The shared
|
|
52
|
+
# loader holds a several-MB RBS::Environment, so dropping it during a normal run wastes the cost of
|
|
53
|
+
# rebuilding it.
|
|
60
54
|
def reset_default!
|
|
61
55
|
@default = nil
|
|
62
56
|
end
|
|
63
57
|
|
|
64
|
-
# Builds an `RBS::Environment` from explicit `libraries` and
|
|
65
|
-
#
|
|
66
|
-
# {
|
|
67
|
-
#
|
|
68
|
-
# instance-side {#build_env} delegates here so the
|
|
69
|
-
# implementation stays single-rooted.
|
|
58
|
+
# Builds an `RBS::Environment` from explicit `libraries` and `signature_paths`. Stateless surface so
|
|
59
|
+
# the v0.0.9 {Cache::RbsEnvironment} producer can build an env on cache miss without holding a loader
|
|
60
|
+
# instance, and the instance-side {#build_env} delegates here so the implementation stays
|
|
61
|
+
# single-rooted.
|
|
70
62
|
#
|
|
71
|
-
# Vendored gem stubs (`data/vendored_gem_sigs/<gem>/`) are
|
|
72
|
-
#
|
|
73
|
-
#
|
|
74
|
-
# run. The gem stubs are intentionally read-only and
|
|
75
|
-
# appended LAST so user-supplied `signature_paths` win on
|
|
76
|
-
# name conflicts.
|
|
63
|
+
# Vendored gem stubs (`data/vendored_gem_sigs/<gem>/`) are loaded on top of `signature_paths` so the
|
|
64
|
+
# per-gem RBS bundled with Rigor itself is in scope for every analysis run. The gem stubs are
|
|
65
|
+
# intentionally read-only and appended LAST so user-supplied `signature_paths` win on name conflicts.
|
|
77
66
|
def build_env_for(libraries:, signature_paths:, virtual_rbs: [])
|
|
78
67
|
rbs_loader = RBS::EnvironmentLoader.new
|
|
79
68
|
libraries.each do |library|
|
|
@@ -81,59 +70,51 @@ module Rigor
|
|
|
81
70
|
|
|
82
71
|
rbs_loader.add(library: library, version: nil)
|
|
83
72
|
end
|
|
84
|
-
signature_paths.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
73
|
+
# Project `signature_paths:` are loaded per-file by {.add_project_signatures} AFTER `from_loader`,
|
|
74
|
+
# NOT added to the loader here: `RBS::Environment.from_loader` parses every added file all-or-nothing,
|
|
75
|
+
# so one unparseable user `.rbs` raises `RBS::ParsingError` and collapses the WHOLE env to nil (every
|
|
76
|
+
# type-of query then degrades to `Dynamic[top]` — the "sig looks harmful" failure of the 2026-07-06
|
|
77
|
+
# mastodon coverage note). Per-file loading quarantines the broken file instead. Vendored / core-overlay
|
|
78
|
+
# sigs are Rigor-shipped and trusted, so they stay on the loader's fast batch path.
|
|
88
79
|
vendored_gem_sig_paths.each do |path|
|
|
89
80
|
rbs_loader.add(path: path) if path.directory?
|
|
90
81
|
end
|
|
91
|
-
# Rigor-owned core overlay — loaded LAST so an upstream
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
# the abstract `Numeric` that Rigor's arithmetic-chain widening
|
|
96
|
-
# produces).
|
|
82
|
+
# Rigor-owned core overlay — loaded LAST so an upstream declaration always wins on conflict; these
|
|
83
|
+
# reopenings only fill genuine holes (e.g. `Numeric#to_f`/`to_i`/`to_r`, which upstream RBS
|
|
84
|
+
# declares on the concrete subclasses but not on the abstract `Numeric` that Rigor's
|
|
85
|
+
# arithmetic-chain widening produces).
|
|
97
86
|
core_overlay_sig_paths.each do |path|
|
|
98
87
|
rbs_loader.add(path: path) if path.directory?
|
|
99
88
|
end
|
|
100
89
|
env = RBS::Environment.from_loader(rbs_loader)
|
|
90
|
+
add_project_signatures(env, signature_paths)
|
|
101
91
|
add_virtual_rbs(env, virtual_rbs)
|
|
102
92
|
synthesize_missing_namespaces(env)
|
|
103
93
|
resolved = env.resolve_type_names
|
|
104
94
|
stub_missing_referenced_types(env, resolved, project_sig_files(signature_paths))
|
|
105
95
|
end
|
|
106
96
|
|
|
107
|
-
# ADR-5 robustness, second tier. A project `signature_paths:`
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
#
|
|
112
|
-
# `
|
|
113
|
-
# `
|
|
114
|
-
# build) that single unresolved reference takes down EVERY
|
|
115
|
-
# method on the class, not just the one signature. Observed on
|
|
116
|
-
# shugo/textbringer: one `DRb::DRbServer` reference left the
|
|
117
|
-
# whole `Textbringer::Commands` module — including its
|
|
118
|
-
# 186-call-site `define_command` DSL — resolving as
|
|
97
|
+
# ADR-5 robustness, second tier. A project `signature_paths:` RBS that *references* a type no loaded
|
|
98
|
+
# signature declares — `def x: () -> DRb::DRbServer` when the `drb` RBS is not available, or a stale
|
|
99
|
+
# reference to its own removed `Textbringer::EditorError` — makes
|
|
100
|
+
# `RBS::DefinitionBuilder#build_instance` raise `NoTypeFoundError`, and (per RBS's all-or-nothing
|
|
101
|
+
# per-class build) that single unresolved reference takes down EVERY method on the class, not just
|
|
102
|
+
# the one signature. Observed on shugo/textbringer: one `DRb::DRbServer` reference left the whole
|
|
103
|
+
# `Textbringer::Commands` module — including its 186-call-site `define_command` DSL — resolving as
|
|
119
104
|
# `Dynamic[Top]`.
|
|
120
105
|
#
|
|
121
|
-
# We synthesize an empty stub for each such referenced-but-
|
|
122
|
-
#
|
|
123
|
-
#
|
|
124
|
-
#
|
|
125
|
-
#
|
|
126
|
-
#
|
|
127
|
-
# resolves such calls to `Dynamic[Top]` instead (the same
|
|
128
|
-
# no-false-positive contract as the dependency-source tier).
|
|
106
|
+
# We synthesize an empty stub for each such referenced-but- undeclared type so the rest of the class
|
|
107
|
+
# builds. A leaf type is stubbed as `class`, its enclosing namespaces as `module`. Stubbed types
|
|
108
|
+
# carry no methods, so a call against a value of a stubbed type would otherwise mis-fire
|
|
109
|
+
# `call.undefined-method`; {MethodDispatcher} consults {#synthesized_type_names} and resolves such
|
|
110
|
+
# calls to `Dynamic[Top]` instead (the same no-false-positive contract as the dependency-source
|
|
111
|
+
# tier).
|
|
129
112
|
#
|
|
130
|
-
# Detection re-uses RBS's own builder (correct by construction):
|
|
131
|
-
#
|
|
132
|
-
#
|
|
133
|
-
#
|
|
134
|
-
#
|
|
135
|
-
# first build error hid, but empty stubs reference nothing, so
|
|
136
|
-
# the fixpoint converges quickly.
|
|
113
|
+
# Detection re-uses RBS's own builder (correct by construction): build every PROJECT class and read
|
|
114
|
+
# the missing name out of the raised error. Bounded to `signature_paths` classes (stdlib / vendored
|
|
115
|
+
# RBS is well-formed) and to {MAX_STUB_PASSES} iterations — a fresh stub can expose a deeper
|
|
116
|
+
# reference the first build error hid, but empty stubs reference nothing, so the fixpoint converges
|
|
117
|
+
# quickly.
|
|
137
118
|
MAX_STUB_PASSES = 5
|
|
138
119
|
|
|
139
120
|
def stub_missing_referenced_types(base_env, resolved, project_files)
|
|
@@ -149,20 +130,15 @@ module Rigor
|
|
|
149
130
|
resolved
|
|
150
131
|
end
|
|
151
132
|
|
|
152
|
-
# Robustness (ADR-5): a project whose RBS declares qualified
|
|
153
|
-
#
|
|
154
|
-
#
|
|
155
|
-
#
|
|
156
|
-
# `
|
|
157
|
-
#
|
|
158
|
-
#
|
|
159
|
-
#
|
|
160
|
-
#
|
|
161
|
-
# otherwise-usable signature set contribute nothing, synthesize
|
|
162
|
-
# an empty `module` declaration for each undeclared enclosing
|
|
163
|
-
# namespace so the definitions build. We only ever add names
|
|
164
|
-
# that are absent — a genuinely-declared namespace (module or
|
|
165
|
-
# class, here or in a loaded gem) is left untouched.
|
|
133
|
+
# Robustness (ADR-5): a project whose RBS declares qualified names (`class Foo::Bar`) without ever
|
|
134
|
+
# declaring the enclosing namespace (`module Foo`) is invalid by upstream RBS rules —
|
|
135
|
+
# `RBS::DefinitionBuilder#build_instance` raises `NoTypeFoundError: Could not find ::Foo`, which the
|
|
136
|
+
# loader's fail-soft rescue turns into a silent dispatch miss (every method on every such class
|
|
137
|
+
# degrades to `Dynamic[Top]`). This is a common authoring mistake (e.g. shugo/textbringer ships a
|
|
138
|
+
# `sig/` that `rbs validate` itself rejects). Rather than let an otherwise-usable signature set
|
|
139
|
+
# contribute nothing, synthesize an empty `module` declaration for each undeclared enclosing
|
|
140
|
+
# namespace so the definitions build. We only ever add names that are absent — a genuinely-declared
|
|
141
|
+
# namespace (module or class, here or in a loaded gem) is left untouched.
|
|
166
142
|
def synthesize_missing_namespaces(env)
|
|
167
143
|
missing = collect_missing_namespaces(env)
|
|
168
144
|
return if missing.empty?
|
|
@@ -172,16 +148,14 @@ module Rigor
|
|
|
172
148
|
_, directives, decls = ::RBS::Parser.parse_signature(buffer)
|
|
173
149
|
add_parsed_decls(env, buffer, directives, decls)
|
|
174
150
|
rescue ::RBS::BaseError
|
|
175
|
-
# Fail-soft: synthesis is an opportunistic uplift, never a
|
|
176
|
-
#
|
|
177
|
-
# as it was (dispatch misses on the affected classes).
|
|
151
|
+
# Fail-soft: synthesis is an opportunistic uplift, never a hard requirement. A parse failure here
|
|
152
|
+
# just leaves the env as it was (dispatch misses on the affected classes).
|
|
178
153
|
nil
|
|
179
154
|
end
|
|
180
155
|
|
|
181
|
-
# Returns the `::`-stripped names of every enclosing namespace
|
|
182
|
-
#
|
|
183
|
-
#
|
|
184
|
-
# before `Foo::Bar`.
|
|
156
|
+
# Returns the `::`-stripped names of every enclosing namespace that some declaration references but
|
|
157
|
+
# no declaration defines, shallowest-first so the synthesized source declares `Foo` before
|
|
158
|
+
# `Foo::Bar`.
|
|
185
159
|
def collect_missing_namespaces(env)
|
|
186
160
|
declared = env.class_decls.keys.to_set
|
|
187
161
|
missing = {}
|
|
@@ -196,10 +170,9 @@ module Rigor
|
|
|
196
170
|
missing.sort_by { |_name, depth| depth }.map(&:first)
|
|
197
171
|
end
|
|
198
172
|
|
|
199
|
-
# The absolute paths of every `.rbs` file under the project's
|
|
200
|
-
#
|
|
201
|
-
#
|
|
202
|
-
# time). Used to scope the referenced-type build sweep.
|
|
173
|
+
# The absolute paths of every `.rbs` file under the project's `signature_paths:` (NOT vendored /
|
|
174
|
+
# stdlib RBS — those are well-formed, so attempting to build them would only waste time). Used to
|
|
175
|
+
# scope the referenced-type build sweep.
|
|
203
176
|
def project_sig_files(signature_paths)
|
|
204
177
|
signature_paths.flat_map do |path|
|
|
205
178
|
path = Pathname(path) unless path.is_a?(Pathname)
|
|
@@ -209,10 +182,57 @@ module Rigor
|
|
|
209
182
|
end.to_set
|
|
210
183
|
end
|
|
211
184
|
|
|
212
|
-
#
|
|
213
|
-
#
|
|
214
|
-
#
|
|
215
|
-
#
|
|
185
|
+
# Load the project's `signature_paths:` RBS files into `env` ONE FILE AT A TIME, quarantining any file
|
|
186
|
+
# that fails to parse rather than letting it collapse the whole env (the `from_loader` batch parse is
|
|
187
|
+
# all-or-nothing — see {.build_env_for}). A quarantined file's declarations are simply absent, so calls
|
|
188
|
+
# into the types it would have declared read `Dynamic[top]`; the rest of the project's (and all
|
|
189
|
+
# bundled) RBS still loads. The user is told which files were skipped via
|
|
190
|
+
# {RbsLoader#warn_about_quarantined_signatures}, so this is a graceful degrade, not a silent one.
|
|
191
|
+
#
|
|
192
|
+
# Buffer names are the file's absolute path (matching {.project_sig_files}) so {.project_entry?} — which
|
|
193
|
+
# attributes a `class_decls` entry to the project by buffer name — still recognises these declarations.
|
|
194
|
+
# Sorted for a deterministic add order (the env feeds the cache, ADR-54).
|
|
195
|
+
def add_project_signatures(env, signature_paths)
|
|
196
|
+
project_sig_files(signature_paths).sort.each do |file|
|
|
197
|
+
parsed = parse_signature_file(file)
|
|
198
|
+
next if parsed.nil? # quarantined (unparseable) or unreadable — skip so the env survives
|
|
199
|
+
|
|
200
|
+
buffer, directives, decls = parsed
|
|
201
|
+
add_parsed_decls(env, buffer, directives, decls)
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Parse one project `.rbs` into `[buffer, directives, decls]`, or nil when it is unparseable /
|
|
206
|
+
# unreadable. Mirrors `RBS::EnvironmentLoader#each_signature`'s per-file parse so the decls register
|
|
207
|
+
# identically to the loader's batch path.
|
|
208
|
+
def parse_signature_file(file)
|
|
209
|
+
buffer = ::RBS::Buffer.new(name: file, content: File.read(file, encoding: "UTF-8"))
|
|
210
|
+
_buffer, directives, decls = ::RBS::Parser.parse_signature(buffer)
|
|
211
|
+
[buffer, directives, decls]
|
|
212
|
+
rescue ::RBS::ParsingError, Errno::ENOENT, Errno::EISDIR, Errno::EACCES
|
|
213
|
+
nil
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# The project `signature_paths:` files that FAIL to parse, as `[absolute_path, first_error_line]` pairs
|
|
217
|
+
# (sorted, deterministic). Detection is independent of {.add_project_signatures} so the warning fires
|
|
218
|
+
# even on a cache hit (where the env was already built with the file quarantined). Cheap: it only
|
|
219
|
+
# re-parses the user's own (usually small) `sig/` set, and returns empty immediately when there is no
|
|
220
|
+
# `signature_paths:`.
|
|
221
|
+
def quarantined_project_signatures(signature_paths)
|
|
222
|
+
project_sig_files(signature_paths).sort.filter_map do |file|
|
|
223
|
+
buffer = ::RBS::Buffer.new(name: file, content: File.read(file, encoding: "UTF-8"))
|
|
224
|
+
::RBS::Parser.parse_signature(buffer)
|
|
225
|
+
nil
|
|
226
|
+
rescue ::RBS::ParsingError => e
|
|
227
|
+
[file, e.message.to_s.lines.first.to_s.strip]
|
|
228
|
+
rescue Errno::ENOENT, Errno::EISDIR, Errno::EACCES
|
|
229
|
+
nil
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Builds every project class (instance + singleton side) and returns the `::`-stripped names of the
|
|
234
|
+
# types whose absence raised `NoTypeFoundError`. Only the FIRST missing reference per class surfaces
|
|
235
|
+
# per build, which is why the caller loops.
|
|
216
236
|
def unresolved_referenced_types(env, project_files)
|
|
217
237
|
builder = ::RBS::DefinitionBuilder.new(env: env)
|
|
218
238
|
missing = []
|
|
@@ -225,21 +245,18 @@ module Rigor
|
|
|
225
245
|
name = e.message[/Could not find (\S+)/, 1]
|
|
226
246
|
missing << name.sub(/\A::/, "") if name
|
|
227
247
|
rescue ::RBS::BaseError
|
|
228
|
-
# Other build failures (duplicate decl, mixin cycle, ...)
|
|
229
|
-
#
|
|
248
|
+
# Other build failures (duplicate decl, mixin cycle, ...) are not ours to repair here — leave
|
|
249
|
+
# them fail-soft.
|
|
230
250
|
end
|
|
231
251
|
end
|
|
232
252
|
missing.uniq
|
|
233
253
|
end
|
|
234
254
|
|
|
235
|
-
# Normalises a `class_decls` entry's representative declaration
|
|
236
|
-
#
|
|
237
|
-
# RBS
|
|
238
|
-
#
|
|
239
|
-
#
|
|
240
|
-
# nil when neither accessor is present. Without this guard,
|
|
241
|
-
# `class_decl_paths` crashed under RBS 3.x with
|
|
242
|
-
# `undefined method 'primary_decl'`.
|
|
255
|
+
# Normalises a `class_decls` entry's representative declaration across the gemspec's supported RBS
|
|
256
|
+
# range (`rbs >= 3.0, < 5.0`). RBS 4.x exposes it as `entry.primary_decl` (the AST declaration
|
|
257
|
+
# directly); RBS 3.x exposes `entry.primary` (a wrapper whose `#decl` is the AST declaration).
|
|
258
|
+
# Returns the AST declaration, or nil when neither accessor is present. Without this guard,
|
|
259
|
+
# `class_decl_paths` crashed under RBS 3.x with `undefined method 'primary_decl'`.
|
|
243
260
|
def primary_decl_for(entry)
|
|
244
261
|
if entry.respond_to?(:primary_decl)
|
|
245
262
|
entry.primary_decl
|
|
@@ -249,18 +266,14 @@ module Rigor
|
|
|
249
266
|
end
|
|
250
267
|
end
|
|
251
268
|
|
|
252
|
-
# Appends freshly-parsed declarations to an `RBS::Environment`
|
|
253
|
-
#
|
|
254
|
-
# RBS
|
|
255
|
-
#
|
|
256
|
-
# `
|
|
257
|
-
#
|
|
258
|
-
# `
|
|
259
|
-
#
|
|
260
|
-
# declarations silently vanish on resolve). Without this guard
|
|
261
|
-
# the synthesis paths (`synthesize_missing_namespaces`,
|
|
262
|
-
# `append_stub_declarations`, `add_virtual_rbs`) crashed under
|
|
263
|
-
# RBS 3.x with `uninitialized constant RBS::Source`.
|
|
269
|
+
# Appends freshly-parsed declarations to an `RBS::Environment` across the gemspec's supported RBS
|
|
270
|
+
# range (`rbs >= 3.0, < 5.0`). RBS 4.x wraps the declarations in an `RBS::Source::RBS` and takes them
|
|
271
|
+
# through `env.add_source`; RBS 3.x has neither `RBS::Source` nor `add_source` and instead registers
|
|
272
|
+
# them with `env.add_signature(buffer:, directives:, decls:)` (a bare `env << decl` is NOT enough —
|
|
273
|
+
# it skips the `signatures` table that `resolve_type_names` rebuilds from, so the synthesized
|
|
274
|
+
# declarations silently vanish on resolve). Without this guard the synthesis paths
|
|
275
|
+
# (`synthesize_missing_namespaces`, `append_stub_declarations`, `add_virtual_rbs`) crashed under RBS
|
|
276
|
+
# 3.x with `uninitialized constant RBS::Source`.
|
|
264
277
|
def add_parsed_decls(env, buffer, directives, decls)
|
|
265
278
|
decls ||= []
|
|
266
279
|
directives ||= []
|
|
@@ -273,9 +286,8 @@ module Rigor
|
|
|
273
286
|
end
|
|
274
287
|
end
|
|
275
288
|
|
|
276
|
-
# True when a `class_decls` entry was declared in one of the
|
|
277
|
-
#
|
|
278
|
-
# the sweep skips the bundled stdlib / vendored universe.
|
|
289
|
+
# True when a `class_decls` entry was declared in one of the project's own signature files (by
|
|
290
|
+
# declaration location), so the sweep skips the bundled stdlib / vendored universe.
|
|
279
291
|
def project_entry?(entry, project_files)
|
|
280
292
|
decl = primary_decl_for(entry)
|
|
281
293
|
location = decl&.location
|
|
@@ -285,18 +297,29 @@ module Rigor
|
|
|
285
297
|
project_files.include?(File.expand_path(buffer_name.to_s))
|
|
286
298
|
end
|
|
287
299
|
|
|
288
|
-
# Adds empty stub declarations for the missing referenced types
|
|
289
|
-
#
|
|
290
|
-
#
|
|
291
|
-
# prefix of another name is declared `module` (it is a
|
|
292
|
-
# namespace); a leaf is declared `class` (referenced types
|
|
300
|
+
# Adds empty stub declarations for the missing referenced types (and any enclosing namespace they
|
|
301
|
+
# need) to the pre-resolve env, tagged with {SYNTHETIC_STUB_BUFFER}. A name that is a prefix of
|
|
302
|
+
# another name is declared `module` (it is a namespace); a leaf is declared `class` (referenced types
|
|
293
303
|
# appear in instance position far more often than as mixins).
|
|
304
|
+
#
|
|
305
|
+
# Names already declared in `base_env` are skipped — exactly the `declared.include?` guard
|
|
306
|
+
# {.collect_missing_namespaces} applies. Without it, stubbing a nested reference (`Foo::Bar::Baz`)
|
|
307
|
+
# re-emits its enclosing prefix (`Foo::Bar`) as a `module`, and when that prefix is already a `class`
|
|
308
|
+
# in the project's own `sig/` the kind mismatch makes `resolve_type_names` raise
|
|
309
|
+
# `RBS::DuplicatedDeclarationError`, collapsing the WHOLE env to nil (every type-of query then
|
|
310
|
+
# degrades to `Dynamic[Top]`). One malformed `.rbs` must not disproportionately blind the analysis: a
|
|
311
|
+
# subclass sig that references an inherited nested type (`class GitAdapter; def x: () ->
|
|
312
|
+
# GitAdapter::Revision`) was the real-world trigger — see the 2026-07-04 redmine onboarding note.
|
|
294
313
|
def append_stub_declarations(base_env, missing)
|
|
314
|
+
declared = declared_type_names(base_env)
|
|
295
315
|
names = missing.to_set
|
|
296
316
|
missing.each do |name|
|
|
297
317
|
parts = name.split("::")
|
|
298
318
|
(1...parts.length).each { |i| names << parts[0, i].join("::") }
|
|
299
319
|
end
|
|
320
|
+
names = names.reject { |name| declared.include?(name) }.to_set
|
|
321
|
+
return if names.empty?
|
|
322
|
+
|
|
300
323
|
source = names.sort_by { |n| n.count(":") }.map do |name|
|
|
301
324
|
keyword = names.any? { |other| other != name && other.start_with?("#{name}::") } ? "module" : "class"
|
|
302
325
|
"#{keyword} #{name}\nend\n"
|
|
@@ -308,15 +331,21 @@ module Rigor
|
|
|
308
331
|
nil
|
|
309
332
|
end
|
|
310
333
|
|
|
311
|
-
#
|
|
312
|
-
#
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
334
|
+
# The `::`-stripped names of every class / module / class-alias declaration already present in
|
|
335
|
+
# `env`, so the synthesis paths never re-declare (and thereby duplicate) a real declaration.
|
|
336
|
+
def declared_type_names(env)
|
|
337
|
+
names = env.class_decls.keys.map { |n| n.to_s.sub(/\A::/, "") }
|
|
338
|
+
if env.respond_to?(:class_alias_decls)
|
|
339
|
+
names.concat(env.class_alias_decls.keys.map { |n| n.to_s.sub(/\A::/, "") })
|
|
340
|
+
end
|
|
341
|
+
names.to_set
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
# ADR-32 WD4 — merge synthesised-from-source RBS strings into the freshly-built environment. Each
|
|
345
|
+
# entry is a `[virtual_filename, rbs_source]` pair. `virtual_filename` is purely for diagnostic
|
|
346
|
+
# provenance (RBS parse errors cite it) — it is not a real file path. Per WD6 the synthesizer-emit
|
|
347
|
+
# path is responsible for catching its own parse errors and returning `nil` rather than garbage; this
|
|
348
|
+
# method assumes its input is parseable and only rescues `RBS::ParsingError` as a fail-soft.
|
|
320
349
|
def add_virtual_rbs(env, virtual_rbs)
|
|
321
350
|
return if virtual_rbs.nil? || virtual_rbs.empty?
|
|
322
351
|
|
|
@@ -327,31 +356,27 @@ module Rigor
|
|
|
327
356
|
_, directives, decls = ::RBS::Parser.parse_signature(buffer)
|
|
328
357
|
add_parsed_decls(env, buffer, directives, decls)
|
|
329
358
|
rescue ::RBS::BaseError
|
|
330
|
-
# WD6 fail-soft: a single broken virtual RBS contribution
|
|
331
|
-
#
|
|
332
|
-
#
|
|
333
|
-
# in slice 2; here we just skip the entry.
|
|
359
|
+
# WD6 fail-soft: a single broken virtual RBS contribution does not pull the whole env down. The
|
|
360
|
+
# plugin layer records a `source-rbs-synthesis-failed` info diagnostic in slice 2; here we just
|
|
361
|
+
# skip the entry.
|
|
334
362
|
end
|
|
335
363
|
end
|
|
336
364
|
|
|
337
|
-
# Per-gem `data/vendored_gem_sigs/<gem>/` directories that
|
|
338
|
-
#
|
|
339
|
-
#
|
|
340
|
-
#
|
|
341
|
-
#
|
|
342
|
-
# dominated `call.undefined-method` noise in the real-world
|
|
343
|
-
# survey at `docs/notes/20260515-real-world-rails-survey.md`.
|
|
365
|
+
# Per-gem `data/vendored_gem_sigs/<gem>/` directories that ship with Rigor. Each subdirectory is one
|
|
366
|
+
# gem's RBS surface (the `<gem>.rbs` file is the typical content; `LICENSE.upstream` records
|
|
367
|
+
# provenance). Coverage is deliberately scoped to the native-extension and "everywhere in Rails" gems
|
|
368
|
+
# whose absence dominated `call.undefined-method` noise in the real-world survey at
|
|
369
|
+
# `docs/notes/20260515-real-world-rails-survey.md`.
|
|
344
370
|
VENDORED_GEM_SIGS_ROOT = File.expand_path(
|
|
345
371
|
"../../../data/vendored_gem_sigs",
|
|
346
372
|
__dir__
|
|
347
373
|
).freeze
|
|
348
374
|
private_constant :VENDORED_GEM_SIGS_ROOT
|
|
349
375
|
|
|
350
|
-
# Rigor-owned core-overlay RBS (`data/core_overlay/`). Reopens
|
|
351
|
-
#
|
|
352
|
-
#
|
|
353
|
-
#
|
|
354
|
-
# can digest these files into the env-blob key.
|
|
376
|
+
# Rigor-owned core-overlay RBS (`data/core_overlay/`). Reopens Ruby core classes to add methods
|
|
377
|
+
# upstream `ruby/rbs` omits but which every concrete value answers at runtime — loaded last so
|
|
378
|
+
# upstream always wins on conflict. Public so the cache descriptor can digest these files into the
|
|
379
|
+
# env-blob key.
|
|
355
380
|
CORE_OVERLAY_SIGS_ROOT = File.expand_path(
|
|
356
381
|
"../../../data/core_overlay",
|
|
357
382
|
__dir__
|
|
@@ -363,24 +388,20 @@ module Rigor
|
|
|
363
388
|
[Pathname(CORE_OVERLAY_SIGS_ROOT)]
|
|
364
389
|
end
|
|
365
390
|
|
|
366
|
-
# Rigor-owned per-gem RBS overlays (`data/gem_overlay/<gem>/`),
|
|
367
|
-
#
|
|
368
|
-
#
|
|
369
|
-
#
|
|
370
|
-
#
|
|
371
|
-
# already-filtered gem-name set here. One directory per gem name
|
|
372
|
-
# keeps the membership check a cheap `File.directory?`.
|
|
391
|
+
# Rigor-owned per-gem RBS overlays (`data/gem_overlay/<gem>/`), ADR-72. Unlike the unconditional
|
|
392
|
+
# `core_overlay`, each gem's overlay is loaded ONLY when that gem is locked in the project's
|
|
393
|
+
# Gemfile.lock but ships no RBS of its own — {Environment.for_project} decides eligibility and passes
|
|
394
|
+
# the already-filtered gem-name set here. One directory per gem name keeps the membership check a
|
|
395
|
+
# cheap `File.directory?`.
|
|
373
396
|
GEM_OVERLAY_SIGS_ROOT = File.expand_path(
|
|
374
397
|
"../../../data/gem_overlay",
|
|
375
398
|
__dir__
|
|
376
399
|
).freeze
|
|
377
400
|
|
|
378
|
-
# @param gem_names [Enumerable<String>] overlay-eligible
|
|
379
|
-
#
|
|
380
|
-
#
|
|
381
|
-
#
|
|
382
|
-
# each gem that ships one; empty when none match or the
|
|
383
|
-
# overlay root is absent.
|
|
401
|
+
# @param gem_names [Enumerable<String>] overlay-eligible Gemfile.lock gem names (the caller filters
|
|
402
|
+
# to the `:missing`-coverage, no-conflicting-plugin set).
|
|
403
|
+
# @return [Array<Pathname>] the bundled overlay directory for each gem that ships one; empty when
|
|
404
|
+
# none match or the overlay root is absent.
|
|
384
405
|
def gem_overlay_sig_paths(gem_names)
|
|
385
406
|
return [] unless File.directory?(GEM_OVERLAY_SIGS_ROOT)
|
|
386
407
|
|
|
@@ -398,14 +419,10 @@ module Rigor
|
|
|
398
419
|
end
|
|
399
420
|
end
|
|
400
421
|
|
|
401
|
-
# Gem names whose RBS ships under
|
|
402
|
-
# `
|
|
403
|
-
#
|
|
404
|
-
#
|
|
405
|
-
# this set to drop the matching `rbs collection install`
|
|
406
|
-
# directory before it double-declares against the
|
|
407
|
-
# vendored copy — the same hazard `DEFAULT_LIBRARIES`
|
|
408
|
-
# creates for stdlib-extracted gems. See
|
|
422
|
+
# Gem names whose RBS ships under `data/vendored_gem_sigs/<gem>/`. The directory walk is the source
|
|
423
|
+
# of truth (the `README.md` sibling is not a gem and is excluded). Callers building the RBS env use
|
|
424
|
+
# this set to drop the matching `rbs collection install` directory before it double-declares against
|
|
425
|
+
# the vendored copy — the same hazard `DEFAULT_LIBRARIES` creates for stdlib-extracted gems. See
|
|
409
426
|
# `RbsCollectionDiscovery`'s `skip_gem_names:`.
|
|
410
427
|
def vendored_gem_names
|
|
411
428
|
return [] unless File.directory?(VENDORED_GEM_SIGS_ROOT)
|
|
@@ -418,48 +435,32 @@ module Rigor
|
|
|
418
435
|
|
|
419
436
|
attr_reader :libraries, :signature_paths, :cache_store, :virtual_rbs
|
|
420
437
|
|
|
421
|
-
# @param libraries [Array<String, Symbol>] stdlib library names to
|
|
422
|
-
#
|
|
423
|
-
#
|
|
424
|
-
# `
|
|
425
|
-
#
|
|
426
|
-
#
|
|
427
|
-
#
|
|
428
|
-
#
|
|
429
|
-
# `
|
|
430
|
-
#
|
|
431
|
-
#
|
|
432
|
-
# @param
|
|
433
|
-
#
|
|
434
|
-
# `
|
|
435
|
-
#
|
|
436
|
-
# producers. Pass `nil` (the default) to skip caching; the
|
|
437
|
-
# runner threads its own Store through here when enabled.
|
|
438
|
-
# @param virtual_rbs [Array<[String, String]>] ADR-32 WD4 —
|
|
439
|
-
# `[virtual_filename, rbs_source]` pairs synthesised from
|
|
440
|
-
# project source by a plugin's
|
|
441
|
-
# `Manifest#source_rbs_synthesizer`. Merged into the env
|
|
442
|
-
# after `signature_paths:` and the vendored stubs. Pass
|
|
443
|
-
# `[]` (the default) when no synthesizer-emitting plugin
|
|
444
|
-
# is loaded.
|
|
438
|
+
# @param libraries [Array<String, Symbol>] stdlib library names to load on top of core (e.g.,
|
|
439
|
+
# `["pathname", "json"]`). Empty by default. Each entry MUST correspond to a directory under the
|
|
440
|
+
# `rbs` gem's `stdlib/` tree; unknown names are silently dropped on environment build (the underlying
|
|
441
|
+
# `RBS::EnvironmentLoader` raises and we fail-soft).
|
|
442
|
+
# @param signature_paths [Array<String, Pathname>] additional directories of `.rbs` files to load
|
|
443
|
+
# (typically the project's `sig/` tree). Non-existent or non-directory paths are filtered out at
|
|
444
|
+
# build time so the loader stays robust to fixtures and bare repositories.
|
|
445
|
+
# @param cache_store [Rigor::Cache::Store, nil] the persistent cache the loader threads through to
|
|
446
|
+
# `RbsEnvironment`, `RbsKnownClassNames`, `RbsConstantTable`, `RbsClassAncestorTable`, and
|
|
447
|
+
# `RbsClassTypeParamNames` producers. Pass `nil` (the default) to skip caching; the runner threads
|
|
448
|
+
# its own Store through here when enabled.
|
|
449
|
+
# @param virtual_rbs [Array<[String, String]>] ADR-32 WD4 — `[virtual_filename, rbs_source]` pairs
|
|
450
|
+
# synthesised from project source by a plugin's `Manifest#source_rbs_synthesizer`. Merged into the
|
|
451
|
+
# env after `signature_paths:` and the vendored stubs. Pass `[]` (the default) when no
|
|
452
|
+
# synthesizer-emitting plugin is loaded.
|
|
445
453
|
def initialize(libraries: [], signature_paths: [], cache_store: nil, virtual_rbs: [])
|
|
446
454
|
@libraries = libraries.map(&:to_s).freeze
|
|
447
455
|
@signature_paths = signature_paths.map { |p| Pathname(p) }.freeze
|
|
448
456
|
@cache_store = cache_store
|
|
449
457
|
@virtual_rbs = virtual_rbs.map { |name, content| [name.to_s.dup.freeze, content.to_s.dup.freeze].freeze }.freeze
|
|
450
|
-
# Per-loader memoization bucket. Held as a single
|
|
451
|
-
#
|
|
452
|
-
#
|
|
453
|
-
#
|
|
454
|
-
#
|
|
455
|
-
#
|
|
456
|
-
# `:instance_definitions_table`,
|
|
457
|
-
# `:singleton_definitions_table`. Constructed via
|
|
458
|
-
# `Hash.new` (NOT a `{ ... }` literal) so Rigor's
|
|
459
|
-
# `HashShape` narrowing doesn't infer a fixed key set
|
|
460
|
-
# from the initial state and fold post-initial slot
|
|
461
|
-
# reads (e.g. `@state[:env_loaded]`) to a constant
|
|
462
|
-
# `nil`.
|
|
458
|
+
# Per-loader memoization bucket. Held as a single mutable Hash so the loader instance itself can be
|
|
459
|
+
# `.freeze`d (per ADR-15 reflection-facade contract) without losing the lazy-memo behaviour. Slot
|
|
460
|
+
# names currently consulted: `:env`, `:env_loaded`, `:env_build_warned`, `:builder`, `:reflection`,
|
|
461
|
+
# `:instance_definitions_table`, `:singleton_definitions_table`. Constructed via `Hash.new` (NOT a
|
|
462
|
+
# `{ ... }` literal) so Rigor's `HashShape` narrowing doesn't infer a fixed key set from the initial
|
|
463
|
+
# state and fold post-initial slot reads (e.g. `@state[:env_loaded]`) to a constant `nil`.
|
|
463
464
|
@state = Hash.new # rubocop:disable Style/EmptyLiteral
|
|
464
465
|
@instance_definition_cache = {}
|
|
465
466
|
@singleton_definition_cache = {}
|
|
@@ -467,51 +468,41 @@ module Rigor
|
|
|
467
468
|
@hierarchy = RbsHierarchy.new(self)
|
|
468
469
|
end
|
|
469
470
|
|
|
470
|
-
# The enclosing namespaces {.synthesize_missing_namespaces} had to
|
|
471
|
-
#
|
|
472
|
-
#
|
|
473
|
-
#
|
|
474
|
-
#
|
|
475
|
-
#
|
|
476
|
-
# collector would). Returns `::`-stripped names, shallowest-first.
|
|
477
|
-
# Empty for a well-formed sig set (the common case) and whenever
|
|
478
|
-
# the env failed to build.
|
|
471
|
+
# The enclosing namespaces {.synthesize_missing_namespaces} had to invent because the project's
|
|
472
|
+
# `signature_paths:` RBS declared qualified names (`class Foo::Bar`) without ever declaring `Foo`.
|
|
473
|
+
# Recovered by scanning the built env for class/module entries whose every declaration originated from
|
|
474
|
+
# the synthetic buffer, so the answer survives the marshalled-env cache (where no build-time collector
|
|
475
|
+
# would). Returns `::`-stripped names, shallowest-first. Empty for a well-formed sig set (the common
|
|
476
|
+
# case) and whenever the env failed to build.
|
|
479
477
|
def synthesized_namespaces
|
|
480
478
|
names_synthesized_in(SYNTHETIC_NAMESPACE_BUFFER)
|
|
481
479
|
end
|
|
482
480
|
|
|
483
|
-
# The referenced-but-undeclared types
|
|
484
|
-
#
|
|
485
|
-
#
|
|
486
|
-
#
|
|
487
|
-
# Recovered off the built env like {#synthesized_namespaces}, so
|
|
488
|
-
# it survives the marshalled-env cache.
|
|
481
|
+
# The referenced-but-undeclared types {.stub_missing_referenced_types} stubbed so the project classes
|
|
482
|
+
# that mention them could build (e.g. an unavailable `DRb::DRbServer`, or a stale
|
|
483
|
+
# `Textbringer::EditorError`). Recovered off the built env like {#synthesized_namespaces}, so it
|
|
484
|
+
# survives the marshalled-env cache.
|
|
489
485
|
def synthesized_stub_types
|
|
490
486
|
names_synthesized_in(SYNTHETIC_STUB_BUFFER)
|
|
491
487
|
end
|
|
492
488
|
|
|
493
|
-
# Every type name Rigor invented to make an otherwise-inert /
|
|
494
|
-
#
|
|
495
|
-
#
|
|
496
|
-
#
|
|
497
|
-
#
|
|
498
|
-
# mis-fires `call.undefined-method`. Memoised; empty (and cheap)
|
|
499
|
-
# for the common well-formed sig set.
|
|
489
|
+
# Every type name Rigor invented to make an otherwise-inert / unbuildable project signature set resolve
|
|
490
|
+
# — both the namespace stubs and the referenced-type stubs. {MethodDispatcher} resolves a call whose
|
|
491
|
+
# receiver is one of these (and that no real signature answered) to `Dynamic[Top]`, so the empty stub
|
|
492
|
+
# never mis-fires `call.undefined-method`. Memoised; empty (and cheap) for the common well-formed sig
|
|
493
|
+
# set.
|
|
500
494
|
def synthesized_type_names
|
|
501
495
|
@state[:synthesized_type_names] ||= (synthesized_namespaces + synthesized_stub_types).to_set
|
|
502
496
|
end
|
|
503
497
|
|
|
504
|
-
# Returns true when an RBS class or module declaration with the given
|
|
505
|
-
#
|
|
506
|
-
# ("Integer" or "::Integer"). Memoized per-name (positive and
|
|
498
|
+
# Returns true when an RBS class or module declaration with the given name is loaded. Accepts
|
|
499
|
+
# unprefixed or top-level-prefixed names ("Integer" or "::Integer"). Memoized per-name (positive and
|
|
507
500
|
# negative results both cache).
|
|
508
501
|
#
|
|
509
|
-
# When `cache_store` is set, the loader fetches the entire set of
|
|
510
|
-
#
|
|
511
|
-
#
|
|
512
|
-
#
|
|
513
|
-
# persist the result; warm runs (and a separate loader sharing
|
|
514
|
-
# the same Store) skip the env walk entirely.
|
|
502
|
+
# When `cache_store` is set, the loader fetches the entire set of known class / module / alias names
|
|
503
|
+
# once (per process) through {Cache::RbsKnownClassNames.fetch} and answers `class_known?` from the
|
|
504
|
+
# in-memory Set. Cold runs pay a single env walk and persist the result; warm runs (and a separate
|
|
505
|
+
# loader sharing the same Store) skip the env walk entirely.
|
|
515
506
|
def class_known?(name)
|
|
516
507
|
key = name.to_s
|
|
517
508
|
return @class_known_cache[key] if @class_known_cache.key?(key)
|
|
@@ -523,16 +514,14 @@ module Rigor
|
|
|
523
514
|
end
|
|
524
515
|
end
|
|
525
516
|
|
|
526
|
-
# Returns true when the named RBS declaration is a Module
|
|
527
|
-
#
|
|
528
|
-
# `
|
|
529
|
-
# `Nominal[
|
|
530
|
-
#
|
|
531
|
-
# fallback, because every concrete includer of M sees Kernel
|
|
532
|
-
# / Object instance methods as part of its own ancestor chain.
|
|
517
|
+
# Returns true when the named RBS declaration is a Module (`RBS::AST::Declarations::Module`) rather
|
|
518
|
+
# than a Class. The `user_class_fallback_receiver` tier consults this to route
|
|
519
|
+
# `Nominal[M].some_kernel_method` (where M is a module mixin like `PP::ObjectMixin`) through the
|
|
520
|
+
# `Nominal[Object]` fallback, because every concrete includer of M sees Kernel / Object instance
|
|
521
|
+
# methods as part of its own ancestor chain.
|
|
533
522
|
#
|
|
534
|
-
# Returns false for classes, for unknown names, and when the
|
|
535
|
-
#
|
|
523
|
+
# Returns false for classes, for unknown names, and when the RBS environment failed to build
|
|
524
|
+
# (fail-soft).
|
|
536
525
|
def rbs_module?(name)
|
|
537
526
|
return false if env.nil?
|
|
538
527
|
|
|
@@ -545,10 +534,9 @@ module Rigor
|
|
|
545
534
|
false
|
|
546
535
|
end
|
|
547
536
|
|
|
548
|
-
# Yields every known class / module / alias name (top-level
|
|
549
|
-
#
|
|
550
|
-
#
|
|
551
|
-
# it never recurses back through {#class_known?}.
|
|
537
|
+
# Yields every known class / module / alias name (top-level prefixed) currently loaded into the
|
|
538
|
+
# environment. The cache producer that materialises the known-name set uses this so it never recurses
|
|
539
|
+
# back through {#class_known?}.
|
|
552
540
|
def each_known_class_name
|
|
553
541
|
return enum_for(:each_known_class_name) unless block_given?
|
|
554
542
|
return if env.nil?
|
|
@@ -556,22 +544,16 @@ module Rigor
|
|
|
556
544
|
env.class_decls.each_key { |rbs_name| yield rbs_name.to_s }
|
|
557
545
|
env.class_alias_decls.each_key { |rbs_name| yield rbs_name.to_s }
|
|
558
546
|
rescue ::RBS::BaseError
|
|
559
|
-
# fail-soft: a broken RBS environment yields no names.
|
|
560
|
-
#
|
|
561
|
-
#
|
|
562
|
-
# must surface so they don't hide silently the way the
|
|
563
|
-
# v0.0.9 cache `Cache::Descriptor` regression did.
|
|
547
|
+
# fail-soft: a broken RBS environment yields no names. Analyzer-internal errors (NameError,
|
|
548
|
+
# NoMethodError, LoadError) are NOT swallowed — those are bugs and must surface so they don't hide
|
|
549
|
+
# silently the way the v0.0.9 cache `Cache::Descriptor` regression did.
|
|
564
550
|
end
|
|
565
551
|
|
|
566
|
-
# ADR-20 slice 2e — iterates over every `%a{...}`
|
|
567
|
-
#
|
|
568
|
-
#
|
|
569
|
-
# `
|
|
570
|
-
#
|
|
571
|
-
# find `rigor:v1:hkt_register` / `rigor:v1:hkt_define`
|
|
572
|
-
# directives in user-authored overlays and merge them
|
|
573
|
-
# into the per-`Environment` HKT registry. Yields nothing
|
|
574
|
-
# when the env failed to build (fail-soft, same shape as
|
|
552
|
+
# ADR-20 slice 2e — iterates over every `%a{...}` annotation attached to a class- or module-level
|
|
553
|
+
# declaration in the loaded RBS environment, yielding `(annotation_string, source_location)` pairs.
|
|
554
|
+
# Used by {Rigor::Inference::HktRegistry.scan_rbs_loader} to find `rigor:v1:hkt_register` /
|
|
555
|
+
# `rigor:v1:hkt_define` directives in user-authored overlays and merge them into the per-`Environment`
|
|
556
|
+
# HKT registry. Yields nothing when the env failed to build (fail-soft, same shape as
|
|
575
557
|
# {#each_known_class_name}).
|
|
576
558
|
def each_class_decl_annotation
|
|
577
559
|
return enum_for(:each_class_decl_annotation) unless block_given?
|
|
@@ -585,20 +567,15 @@ module Rigor
|
|
|
585
567
|
end
|
|
586
568
|
end
|
|
587
569
|
rescue ::RBS::BaseError, ::Ractor::IsolationError
|
|
588
|
-
# fail-soft: matches each_known_class_name's policy.
|
|
589
|
-
# Ractor
|
|
590
|
-
#
|
|
591
|
-
# ADR-15's full deep-freeze migration completes — the
|
|
592
|
-
# worker falls back to the base (builtins-only)
|
|
593
|
-
# registry rather than crashing.
|
|
570
|
+
# fail-soft: matches each_known_class_name's policy. Ractor::IsolationError surfaces when the scan
|
|
571
|
+
# is invoked from a non-main Ractor pool worker before ADR-15's full deep-freeze migration completes
|
|
572
|
+
# — the worker falls back to the base (builtins-only) registry rather than crashing.
|
|
594
573
|
end
|
|
595
574
|
|
|
596
|
-
# Like {#each_class_decl_annotation}, but also yields the
|
|
597
|
-
#
|
|
598
|
-
#
|
|
599
|
-
#
|
|
600
|
-
# `rigor:v1:conforms-to` directive back to the class it
|
|
601
|
-
# annotates. Same fail-soft policy as the un-named variant.
|
|
575
|
+
# Like {#each_class_decl_annotation}, but also yields the owning class / module's RBS name as the
|
|
576
|
+
# first block argument: `(class_name, annotation_string, location)`. Used by
|
|
577
|
+
# {Rigor::RbsExtended::ConformanceChecker} to resolve a `rigor:v1:conforms-to` directive back to the
|
|
578
|
+
# class it annotates. Same fail-soft policy as the un-named variant.
|
|
602
579
|
def each_class_decl_annotation_with_name
|
|
603
580
|
return enum_for(:each_class_decl_annotation_with_name) unless block_given?
|
|
604
581
|
return if env.nil?
|
|
@@ -614,16 +591,12 @@ module Rigor
|
|
|
614
591
|
# fail-soft: see #each_class_decl_annotation.
|
|
615
592
|
end
|
|
616
593
|
|
|
617
|
-
# Returns a frozen `Hash<String, String>` mapping each loaded
|
|
618
|
-
#
|
|
619
|
-
#
|
|
620
|
-
#
|
|
621
|
-
#
|
|
622
|
-
#
|
|
623
|
-
# core, stdlib libraries, gem-bundled RBS). Each value is a
|
|
624
|
-
# frozen `String` so the whole result is `Ractor.shareable?`
|
|
625
|
-
# — the Phase 4b worker pool ships a snapshot back to the
|
|
626
|
-
# coordinator on the first `:prepare` message.
|
|
594
|
+
# Returns a frozen `Hash<String, String>` mapping each loaded class / module name (top-level prefixed)
|
|
595
|
+
# to the file path of its FIRST declaration's RBS source. Used by {Rigor::Analysis::RunStats} to
|
|
596
|
+
# attribute the type universe between "project sig/" (paths under the configured `signature_paths`)
|
|
597
|
+
# and "bundled" (everything else — RBS core, stdlib libraries, gem-bundled RBS). Each value is a frozen
|
|
598
|
+
# `String` so the whole result is `Ractor.shareable?` — the Phase 4b worker pool ships a snapshot back
|
|
599
|
+
# to the coordinator on the first `:prepare` message.
|
|
627
600
|
def class_decl_paths
|
|
628
601
|
return {}.freeze if env.nil?
|
|
629
602
|
|
|
@@ -646,17 +619,14 @@ module Rigor
|
|
|
646
619
|
{}.freeze
|
|
647
620
|
end
|
|
648
621
|
|
|
649
|
-
# @return [RBS::Definition, nil] the resolved instance definition
|
|
650
|
-
#
|
|
651
|
-
#
|
|
652
|
-
# we fail-soft and return nil so the caller can fall back).
|
|
622
|
+
# @return [RBS::Definition, nil] the resolved instance definition for `class_name`, or nil when the
|
|
623
|
+
# class is unknown or its definition cannot be built (RBS may raise on broken hierarchies; we
|
|
624
|
+
# fail-soft and return nil so the caller can fall back).
|
|
653
625
|
#
|
|
654
|
-
# Built on demand from the (possibly cache-loaded) env; the
|
|
655
|
-
#
|
|
656
|
-
#
|
|
657
|
-
#
|
|
658
|
-
# measurably slower (and allocation-heavier) than rebuilding
|
|
659
|
-
# the ones a run actually touches.
|
|
626
|
+
# Built on demand from the (possibly cache-loaded) env; the in-memory `@instance_definition_cache`
|
|
627
|
+
# keeps the per-process short-circuit. ADR-54 WD1 retired the definitions disk blob: given a cached
|
|
628
|
+
# env, `Marshal.load`-ing every definition was measurably slower (and allocation-heavier) than
|
|
629
|
+
# rebuilding the ones a run actually touches.
|
|
660
630
|
def instance_definition(class_name)
|
|
661
631
|
key = class_name.to_s
|
|
662
632
|
return @instance_definition_cache[key] if @instance_definition_cache.key?(key)
|
|
@@ -672,13 +642,10 @@ module Rigor
|
|
|
672
642
|
definition.methods[method_name.to_sym]
|
|
673
643
|
end
|
|
674
644
|
|
|
675
|
-
# @return [Array<Symbol>, nil] every instance-method name on
|
|
676
|
-
#
|
|
677
|
-
#
|
|
678
|
-
#
|
|
679
|
-
# "no methods" apart from "unknown class". Used by the
|
|
680
|
-
# `rigor:v1:conforms-to` presence check
|
|
681
|
-
# ({Rigor::RbsExtended::ConformanceChecker}).
|
|
645
|
+
# @return [Array<Symbol>, nil] every instance-method name on `class_name` — own, inherited, and
|
|
646
|
+
# included — as resolved by `RBS::DefinitionBuilder`. Returns `nil` (NOT `[]`) when the class
|
|
647
|
+
# definition cannot be built so callers can tell "no methods" apart from "unknown class". Used by the
|
|
648
|
+
# `rigor:v1:conforms-to` presence check ({Rigor::RbsExtended::ConformanceChecker}).
|
|
682
649
|
def instance_method_names(class_name)
|
|
683
650
|
definition = instance_definition(class_name)
|
|
684
651
|
return nil unless definition
|
|
@@ -686,12 +653,10 @@ module Rigor
|
|
|
686
653
|
definition.methods.keys
|
|
687
654
|
end
|
|
688
655
|
|
|
689
|
-
# @return [RBS::Definition, nil] the built definition for the RBS
|
|
690
|
-
#
|
|
691
|
-
#
|
|
692
|
-
#
|
|
693
|
-
# (a typo, or the defining library / sig set is not on the load
|
|
694
|
-
# path). Fail-soft on RBS build errors.
|
|
656
|
+
# @return [RBS::Definition, nil] the built definition for the RBS interface `interface_name`
|
|
657
|
+
# (`_RewindableStream`), whose `.methods` are the required members (including interface-ancestor
|
|
658
|
+
# members). Returns `nil` when the name does not resolve to a loaded interface (a typo, or the
|
|
659
|
+
# defining library / sig set is not on the load path). Fail-soft on RBS build errors.
|
|
695
660
|
def interface_definition(interface_name)
|
|
696
661
|
rbs_name = parse_type_name(interface_name)
|
|
697
662
|
return nil unless rbs_name
|
|
@@ -703,24 +668,20 @@ module Rigor
|
|
|
703
668
|
nil
|
|
704
669
|
end
|
|
705
670
|
|
|
706
|
-
# @return [Array<Symbol>, nil] every method name required by the RBS
|
|
707
|
-
#
|
|
708
|
-
# accessor over {#interface_definition} for the presence check.
|
|
671
|
+
# @return [Array<Symbol>, nil] every method name required by the RBS interface `interface_name`, or nil
|
|
672
|
+
# when it does not resolve. Thin accessor over {#interface_definition} for the presence check.
|
|
709
673
|
def interface_method_names(interface_name)
|
|
710
674
|
interface_definition(interface_name)&.methods&.keys
|
|
711
675
|
end
|
|
712
676
|
|
|
713
|
-
# @param rbs_alias [RBS::Types::Alias] a type-alias reference (`string`,
|
|
714
|
-
#
|
|
715
|
-
# @return [RBS::Types::t, nil] the alias's aliased type one level out,
|
|
716
|
-
#
|
|
717
|
-
#
|
|
718
|
-
# ::
|
|
719
|
-
#
|
|
720
|
-
#
|
|
721
|
-
# does not reject `nil`. `expand_alias2` handles the (rarer) generic
|
|
722
|
-
# case — a `range[T]` param previously fell back to "admits", which
|
|
723
|
-
# suppressed e.g. `MatchData#[](nil)`.
|
|
677
|
+
# @param rbs_alias [RBS::Types::Alias] a type-alias reference (`string`, `int`, `range[int?]`, …)
|
|
678
|
+
# appearing in a method signature.
|
|
679
|
+
# @return [RBS::Types::t, nil] the alias's aliased type one level out, with type arguments substituted
|
|
680
|
+
# for a generic alias (`string` → `::String | ::_ToStr`; `range[int?]` → `::Range[int?] |
|
|
681
|
+
# ::_Range[int?]`), or nil for an unresolved name. Lets a caller see through the alias that
|
|
682
|
+
# {Inference::RbsTypeTranslator} otherwise degrades to `untyped`, which is why an interface/alias
|
|
683
|
+
# parameter does not reject `nil`. `expand_alias2` handles the (rarer) generic case — a `range[T]`
|
|
684
|
+
# param previously fell back to "admits", which suppressed e.g. `MatchData#[](nil)`.
|
|
724
685
|
def expand_type_alias(rbs_alias)
|
|
725
686
|
return nil if env.nil?
|
|
726
687
|
|
|
@@ -733,15 +694,13 @@ module Rigor
|
|
|
733
694
|
nil
|
|
734
695
|
end
|
|
735
696
|
|
|
736
|
-
# @return [RBS::Definition, nil] the resolved singleton (class
|
|
737
|
-
#
|
|
738
|
-
#
|
|
739
|
-
#
|
|
740
|
-
# Returns nil for unknown names and on RBS build errors (fail-soft).
|
|
697
|
+
# @return [RBS::Definition, nil] the resolved singleton (class object) definition for `class_name`. The
|
|
698
|
+
# methods on this definition are the *class methods* of `class_name`, including those inherited from
|
|
699
|
+
# `Class` and `Module` for class types. Returns nil for unknown names and on RBS build errors
|
|
700
|
+
# (fail-soft).
|
|
741
701
|
#
|
|
742
|
-
# Built on demand from the env with a per-process memo; the
|
|
743
|
-
#
|
|
744
|
-
# WD1).
|
|
702
|
+
# Built on demand from the env with a per-process memo; the same on-demand discipline as
|
|
703
|
+
# {#instance_definition} (ADR-54 WD1).
|
|
745
704
|
def singleton_definition(class_name)
|
|
746
705
|
key = class_name.to_s
|
|
747
706
|
return @singleton_definition_cache[key] if @singleton_definition_cache.key?(key)
|
|
@@ -749,11 +708,10 @@ module Rigor
|
|
|
749
708
|
@singleton_definition_cache[key] = build_singleton_definition(class_name)
|
|
750
709
|
end
|
|
751
710
|
|
|
752
|
-
# @return [RBS::Definition::Method, nil] the class method on
|
|
753
|
-
# `class_name
|
|
754
|
-
#
|
|
755
|
-
#
|
|
756
|
-
# method_name: :new)` returns Class#new for any class type.
|
|
711
|
+
# @return [RBS::Definition::Method, nil] the class method on `class_name`. For example,
|
|
712
|
+
# `singleton_method(class_name: "Integer", method_name: :sqrt)` returns the definition for
|
|
713
|
+
# `Integer.sqrt`, while `singleton_method(class_name: "Foo", method_name: :new)` returns Class#new
|
|
714
|
+
# for any class type.
|
|
757
715
|
def singleton_method(class_name:, method_name:)
|
|
758
716
|
definition = singleton_definition(class_name)
|
|
759
717
|
return nil unless definition
|
|
@@ -761,25 +719,19 @@ module Rigor
|
|
|
761
719
|
definition.methods[method_name.to_sym]
|
|
762
720
|
end
|
|
763
721
|
|
|
764
|
-
# Slice 4 phase 2d. Returns the class's declared type-parameter
|
|
765
|
-
#
|
|
766
|
-
# `
|
|
767
|
-
#
|
|
768
|
-
# instance definition is the canonical source because singleton
|
|
769
|
-
# methods (e.g., `Array.new`) parameterize over the same `Elem`
|
|
770
|
-
# as instance methods.
|
|
722
|
+
# Slice 4 phase 2d. Returns the class's declared type-parameter names as Symbols (e.g., `[:Elem]` for
|
|
723
|
+
# `Array`, `[:K, :V]` for `Hash`). Used by the dispatcher to build the substitution map from receiver
|
|
724
|
+
# `type_args` into the method's return type. The instance definition is the canonical source because
|
|
725
|
+
# singleton methods (e.g., `Array.new`) parameterize over the same `Elem` as instance methods.
|
|
771
726
|
#
|
|
772
|
-
# Returns an empty array for non-generic classes and for unknown
|
|
773
|
-
#
|
|
774
|
-
# `
|
|
775
|
-
# not the AST `TypeParam` object (those live on the AST level).
|
|
727
|
+
# Returns an empty array for non-generic classes and for unknown names (the loader stays fail-soft).
|
|
728
|
+
# NOTE: in the `rbs` gem, `RBS::Definition#type_params` returns `Array<Symbol>` directly, not the AST
|
|
729
|
+
# `TypeParam` object (those live on the AST level).
|
|
776
730
|
#
|
|
777
|
-
# When `cache_store` is set, the loader fetches the entire
|
|
778
|
-
#
|
|
779
|
-
#
|
|
780
|
-
#
|
|
781
|
-
# it; warm runs (and a separate loader sharing the same Store)
|
|
782
|
-
# skip the env walk entirely.
|
|
731
|
+
# When `cache_store` is set, the loader fetches the entire type-parameter-name table once (per
|
|
732
|
+
# process) through {Cache::RbsClassTypeParamNames.fetch} and answers point lookups from it. Cold runs
|
|
733
|
+
# build the table once and persist it; warm runs (and a separate loader sharing the same Store) skip
|
|
734
|
+
# the env walk entirely.
|
|
783
735
|
def class_type_param_names(class_name)
|
|
784
736
|
if cache_store
|
|
785
737
|
key = class_name.to_s.delete_prefix("::")
|
|
@@ -796,11 +748,9 @@ module Rigor
|
|
|
796
748
|
@hierarchy.class_ordering(lhs, rhs)
|
|
797
749
|
end
|
|
798
750
|
|
|
799
|
-
# @return [Array<String>] every RBS-declared constant name
|
|
800
|
-
#
|
|
801
|
-
#
|
|
802
|
-
# materialises the constant-type table; ordinary callers
|
|
803
|
-
# should keep using {#constant_type} for point lookups.
|
|
751
|
+
# @return [Array<String>] every RBS-declared constant name (top-level prefixed, e.g., `"::Math::PI"`)
|
|
752
|
+
# currently loaded into the environment. Used by the cache producer that materialises the
|
|
753
|
+
# constant-type table; ordinary callers should keep using {#constant_type} for point lookups.
|
|
804
754
|
def constant_names
|
|
805
755
|
return [] if env.nil?
|
|
806
756
|
|
|
@@ -809,11 +759,9 @@ module Rigor
|
|
|
809
759
|
[]
|
|
810
760
|
end
|
|
811
761
|
|
|
812
|
-
# Yields `(name, entry)` for every RBS constant declaration
|
|
813
|
-
#
|
|
814
|
-
#
|
|
815
|
-
# going back through {#constant_type} (which would recurse
|
|
816
|
-
# back into the cache when `cache_store` is set).
|
|
762
|
+
# Yields `(name, entry)` for every RBS constant declaration currently loaded into the environment. The
|
|
763
|
+
# cache producer uses this to materialise the constant-type table without going back through
|
|
764
|
+
# {#constant_type} (which would recurse back into the cache when `cache_store` is set).
|
|
817
765
|
def each_constant_decl
|
|
818
766
|
return enum_for(:each_constant_decl) unless block_given?
|
|
819
767
|
return if env.nil?
|
|
@@ -825,22 +773,16 @@ module Rigor
|
|
|
825
773
|
# fail-soft: a broken RBS environment yields no entries.
|
|
826
774
|
end
|
|
827
775
|
|
|
828
|
-
# Slice A constant-value lookup. Returns the translated
|
|
829
|
-
# `
|
|
830
|
-
#
|
|
831
|
-
#
|
|
832
|
-
#
|
|
833
|
-
# value as authoritative when present and as "unknown" when
|
|
834
|
-
# nil; the loader does NOT consult the class declarations
|
|
835
|
-
# here — class objects are still resolved through
|
|
836
|
-
# {#class_known?} and `Environment#singleton_for_name`.
|
|
776
|
+
# Slice A constant-value lookup. Returns the translated `Rigor::Type` for a non-class constant
|
|
777
|
+
# declaration (`BUCKETS: Array[Symbol]`, `DEFAULT_PATH: String`, ...) or `nil` when no constant entry
|
|
778
|
+
# exists for `name` in the loaded RBS environment. Callers MUST treat the return value as authoritative
|
|
779
|
+
# when present and as "unknown" when nil; the loader does NOT consult the class declarations here —
|
|
780
|
+
# class objects are still resolved through {#class_known?} and `Environment#singleton_for_name`.
|
|
837
781
|
#
|
|
838
|
-
# When `cache_store` is set, the loader fetches the entire
|
|
839
|
-
#
|
|
840
|
-
#
|
|
841
|
-
#
|
|
842
|
-
# write the result to disk; warm runs skip the translation
|
|
843
|
-
# entirely and pay only a `Marshal.load` of the table.
|
|
782
|
+
# When `cache_store` is set, the loader fetches the entire translated constant table once (per
|
|
783
|
+
# process) through {Cache::RbsConstantTable.fetch} and answers point lookups from it. Cold runs pay
|
|
784
|
+
# the translation cost up-front and write the result to disk; warm runs skip the translation entirely
|
|
785
|
+
# and pay only a `Marshal.load` of the table.
|
|
844
786
|
def constant_type(name)
|
|
845
787
|
rbs_name = parse_type_name(name)
|
|
846
788
|
return nil unless rbs_name
|
|
@@ -854,27 +796,18 @@ module Rigor
|
|
|
854
796
|
nil
|
|
855
797
|
end
|
|
856
798
|
|
|
857
|
-
# ADR-15 Phase 4b.x — eagerly drives every cached
|
|
858
|
-
#
|
|
859
|
-
#
|
|
860
|
-
#
|
|
861
|
-
#
|
|
862
|
-
#
|
|
863
|
-
#
|
|
864
|
-
#
|
|
865
|
-
# (`RBS::EnvironmentLoader::DEFAULT_CORE_ROOT`,
|
|
866
|
-
# `RBS::Repository::DEFAULT_STDLIB_ROOT`,
|
|
867
|
-
# `Gem::Requirement::DefaultRequirement`, …) and trips
|
|
868
|
-
# `Ractor::IsolationError`. Pre-warming on the main
|
|
869
|
-
# Ractor — env blob loaded, derived tables built — keeps
|
|
870
|
-
# workers off that chain (`RBS::DefinitionBuilder` over
|
|
871
|
-
# an already-built env does not touch it).
|
|
799
|
+
# ADR-15 Phase 4b.x — eagerly drives every cached producer (plus the eager definitions tables, computed
|
|
800
|
+
# from the cached env since ADR-54 WD1) so a subsequent worker Ractor can serve all of its RBS queries
|
|
801
|
+
# without ever calling `RBS::EnvironmentLoader.new`. The loader path that calls
|
|
802
|
+
# `EnvironmentLoader.new` transitively reads a chain of non-`Ractor.shareable?` module constants
|
|
803
|
+
# (`RBS::EnvironmentLoader::DEFAULT_CORE_ROOT`, `RBS::Repository::DEFAULT_STDLIB_ROOT`,
|
|
804
|
+
# `Gem::Requirement::DefaultRequirement`, …) and trips `Ractor::IsolationError`. Pre-warming on the
|
|
805
|
+
# main Ractor — env blob loaded, derived tables built — keeps workers off that chain
|
|
806
|
+
# (`RBS::DefinitionBuilder` over an already-built env does not touch it).
|
|
872
807
|
#
|
|
873
|
-
# No-op when `cache_store` is nil — without a Store the
|
|
874
|
-
#
|
|
875
|
-
#
|
|
876
|
-
# enabled. Returns `self` so the call chains cleanly
|
|
877
|
-
# from the `Runner` pre-spawn hook.
|
|
808
|
+
# No-op when `cache_store` is nil — without a Store the worker has no choice but to build env via the
|
|
809
|
+
# loader, so the caller MUST ensure pool mode runs with caching enabled. Returns `self` so the call
|
|
810
|
+
# chains cleanly from the `Runner` pre-spawn hook.
|
|
878
811
|
def prewarm
|
|
879
812
|
return self if cache_store.nil?
|
|
880
813
|
|
|
@@ -888,13 +821,10 @@ module Rigor
|
|
|
888
821
|
self
|
|
889
822
|
end
|
|
890
823
|
|
|
891
|
-
# ADR-54 WD4 — the shared cache descriptor for every RBS-derived
|
|
892
|
-
#
|
|
893
|
-
#
|
|
894
|
-
#
|
|
895
|
-
# memoised per loader (on `@state`, alongside `:env` — the env
|
|
896
|
-
# itself is loader-lifetime-memoised, so this adds no new
|
|
897
|
-
# staleness class).
|
|
824
|
+
# ADR-54 WD4 — the shared cache descriptor for every RBS-derived producer consulting this loader.
|
|
825
|
+
# Building it digests every `.rbs` file under `signature_paths` + the vendored gem sigs, and the
|
|
826
|
+
# result is identical across producers, so one build is memoised per loader (on `@state`, alongside
|
|
827
|
+
# `:env` — the env itself is loader-lifetime-memoised, so this adds no new staleness class).
|
|
898
828
|
def rbs_cache_descriptor
|
|
899
829
|
@state[:rbs_cache_descriptor] ||= begin
|
|
900
830
|
require_relative "../cache/rbs_descriptor"
|
|
@@ -902,19 +832,14 @@ module Rigor
|
|
|
902
832
|
end
|
|
903
833
|
end
|
|
904
834
|
|
|
905
|
-
# ADR-15 Phase 2b — return the loader's read-only
|
|
906
|
-
#
|
|
907
|
-
#
|
|
908
|
-
# access; the loader memoises so repeated calls return
|
|
909
|
-
# the same instance.
|
|
835
|
+
# ADR-15 Phase 2b — return the loader's read-only query surface as a frozen, `Ractor.shareable?`
|
|
836
|
+
# {Reflection} value object. Built lazily on first access; the loader memoises so repeated calls
|
|
837
|
+
# return the same instance.
|
|
910
838
|
#
|
|
911
|
-
# The Reflection consumes the loader's already-warmed
|
|
912
|
-
#
|
|
913
|
-
#
|
|
914
|
-
#
|
|
915
|
-
# and never re-consults the loader — making it safe to
|
|
916
|
-
# share across Ractors while the loader stays per-
|
|
917
|
-
# process / per-Ractor for write-path operations.
|
|
839
|
+
# The Reflection consumes the loader's already-warmed cache producers (or, when no `cache_store` is
|
|
840
|
+
# set, eagerly walks the env). Once constructed, the Reflection carries the derived tables
|
|
841
|
+
# independently and never re-consults the loader — making it safe to share across Ractors while the
|
|
842
|
+
# loader stays per- process / per-Ractor for write-path operations.
|
|
918
843
|
def reflection
|
|
919
844
|
@state[:reflection] ||= begin
|
|
920
845
|
require_relative "reflection"
|
|
@@ -931,11 +856,10 @@ module Rigor
|
|
|
931
856
|
|
|
932
857
|
private
|
|
933
858
|
|
|
934
|
-
# The `::`-stripped names of every class/module entry whose
|
|
935
|
-
#
|
|
936
|
-
#
|
|
937
|
-
#
|
|
938
|
-
# env cache; shallowest-first. Empty when the env failed to build.
|
|
859
|
+
# The `::`-stripped names of every class/module entry whose declarations ALL originated from the given
|
|
860
|
+
# sentinel buffer — i.e. names Rigor synthesized, not names the project declared. Reads off the built
|
|
861
|
+
# env so the answer survives the marshalled env cache; shallowest-first. Empty when the env failed to
|
|
862
|
+
# build.
|
|
939
863
|
def names_synthesized_in(buffer_name)
|
|
940
864
|
e = env
|
|
941
865
|
return [] if e.nil?
|
|
@@ -950,13 +874,11 @@ module Rigor
|
|
|
950
874
|
names.sort_by { |name| name.count("::") }
|
|
951
875
|
end
|
|
952
876
|
|
|
953
|
-
# Collects the AST declaration nodes behind a `class_decls`
|
|
954
|
-
#
|
|
955
|
-
# RBS
|
|
956
|
-
#
|
|
957
|
-
#
|
|
958
|
-
# The single-`decl` shape is handled defensively so the loader
|
|
959
|
-
# survives an rbs-gem minor bump.
|
|
877
|
+
# Collects the AST declaration nodes behind a `class_decls` entry across the supported RBS range (`rbs
|
|
878
|
+
# >= 3.0, < 5.0`). RBS 4's `ModuleEntry` / `ClassEntry` expose `each_decl` yielding bare AST
|
|
879
|
+
# declarations; RBS 3.x exposes `decls`, an array of `MultiEntry::D` wrappers whose `#decl` is the AST
|
|
880
|
+
# declaration. The single-`decl` shape is handled defensively so the loader survives an rbs-gem minor
|
|
881
|
+
# bump.
|
|
960
882
|
def entry_declarations(entry)
|
|
961
883
|
if entry.respond_to?(:each_decl)
|
|
962
884
|
[].tap { |acc| entry.each_decl { |decl| acc << decl } }
|
|
@@ -969,9 +891,8 @@ module Rigor
|
|
|
969
891
|
end
|
|
970
892
|
end
|
|
971
893
|
|
|
972
|
-
# True when an AST declaration was emitted into `buffer_name`
|
|
973
|
-
#
|
|
974
|
-
# buffer name on its location.
|
|
894
|
+
# True when an AST declaration was emitted into `buffer_name` (one of the synthetic-source sentinels)
|
|
895
|
+
# — identified by the buffer name on its location.
|
|
975
896
|
def synthetic_decl?(decl, buffer_name)
|
|
976
897
|
location = decl.respond_to?(:location) ? decl.location : nil
|
|
977
898
|
location&.buffer&.name.to_s == buffer_name
|
|
@@ -998,23 +919,19 @@ module Rigor
|
|
|
998
919
|
end
|
|
999
920
|
end
|
|
1000
921
|
|
|
1001
|
-
# ADR-15 Phase 2b — the `Reflection` build path
|
|
1002
|
-
#
|
|
1003
|
-
#
|
|
1004
|
-
#
|
|
1005
|
-
# producer's `.fetch` when a store IS available, and
|
|
1006
|
-
# falls back to the producer's `.compute` otherwise.
|
|
922
|
+
# ADR-15 Phase 2b — the `Reflection` build path consumes these tables even when `cache_store` is nil
|
|
923
|
+
# (e.g. tests that build a `Reflection` without a persistent cache). The helper routes through the
|
|
924
|
+
# producer's `.fetch` when a store IS available, and falls back to the producer's `.compute`
|
|
925
|
+
# otherwise.
|
|
1007
926
|
def fetch_or_compute_producer(producer)
|
|
1008
927
|
return producer.fetch(loader: self, store: cache_store) if cache_store
|
|
1009
928
|
|
|
1010
929
|
producer.send(:compute, self)
|
|
1011
930
|
end
|
|
1012
931
|
|
|
1013
|
-
# ADR-15 Phase 2b — `Hash<String, Array<String>>` of
|
|
1014
|
-
#
|
|
1015
|
-
#
|
|
1016
|
-
# `cache_store` is set; falls back to the producer's
|
|
1017
|
-
# `compute` otherwise. Used by {#reflection}.
|
|
932
|
+
# ADR-15 Phase 2b — `Hash<String, Array<String>>` of normalised ancestor chains per class. Consumes
|
|
933
|
+
# the existing `RbsClassAncestorTable` producer when `cache_store` is set; falls back to the
|
|
934
|
+
# producer's `compute` otherwise. Used by {#reflection}.
|
|
1018
935
|
def ancestor_names_table
|
|
1019
936
|
@ancestor_names_table ||= begin
|
|
1020
937
|
require_relative "../cache/rbs_class_ancestor_table"
|
|
@@ -1041,27 +958,50 @@ module Rigor
|
|
|
1041
958
|
translated unless translated.is_a?(Type::Bot)
|
|
1042
959
|
end
|
|
1043
960
|
|
|
1044
|
-
# The RBS environment for this loader. Memoised both on
|
|
1045
|
-
#
|
|
1046
|
-
#
|
|
1047
|
-
#
|
|
1048
|
-
#
|
|
1049
|
-
#
|
|
1050
|
-
# whole sig set per AST node touched during analysis,
|
|
1051
|
-
# multiplying per-file analysis cost by ~100x. Failures
|
|
1052
|
-
# short-circuit to `nil` here and are surfaced to the user
|
|
1053
|
-
# via `warn_about_env_build_failure_once` so the broken
|
|
1054
|
-
# `signature_paths:` entry is identifiable.
|
|
961
|
+
# The RBS environment for this loader. Memoised both on success AND on failure: when the env build
|
|
962
|
+
# raises (typically `RBS::DuplicatedDeclarationError` because a `signature_paths:` entry redeclares a
|
|
963
|
+
# constant or class already shipped by stdlib RBS), retrying on every subsequent `env` call would
|
|
964
|
+
# re-parse and re-resolve the whole sig set per AST node touched during analysis, multiplying per-file
|
|
965
|
+
# analysis cost by ~100x. Failures short-circuit to `nil` here and are surfaced to the user via
|
|
966
|
+
# `warn_about_env_build_failure_once` so the broken `signature_paths:` entry is identifiable.
|
|
1055
967
|
def env
|
|
1056
968
|
return @state[:env] if @state[:env_loaded]
|
|
1057
969
|
|
|
1058
970
|
@state[:env_loaded] = true
|
|
1059
971
|
@state[:env] = cache_store ? cached_env : build_env
|
|
972
|
+
warn_about_quarantined_signatures
|
|
973
|
+
@state[:env]
|
|
1060
974
|
rescue ::RBS::BaseError => e
|
|
1061
975
|
warn_about_env_build_failure_once(e)
|
|
1062
976
|
@state[:env] = nil
|
|
1063
977
|
end
|
|
1064
978
|
|
|
979
|
+
# Tell the user, once per run, which of their `signature_paths:` `.rbs` files were skipped because they
|
|
980
|
+
# do not parse ({RbsLoader.add_project_signatures} quarantines them so the rest of the env survives). A
|
|
981
|
+
# skipped file's types are absent, so calls into them read `Dynamic[top]` — silently, without this. This
|
|
982
|
+
# is the visibility half of the fix: a shrinking diagnostic count must never be mistaken for a clean run
|
|
983
|
+
# when it actually means "your sig/ stopped loading". No-op when `signature_paths:` is empty (the cost is
|
|
984
|
+
# then a single empty-set check) or every file parses.
|
|
985
|
+
def warn_about_quarantined_signatures
|
|
986
|
+
return if @state[:quarantine_warned]
|
|
987
|
+
|
|
988
|
+
quarantined = self.class.quarantined_project_signatures(@signature_paths)
|
|
989
|
+
return if quarantined.empty?
|
|
990
|
+
|
|
991
|
+
@state[:quarantine_warned] = true
|
|
992
|
+
listed = quarantined.first(QUARANTINE_WARN_LIMIT)
|
|
993
|
+
more = quarantined.size - listed.size
|
|
994
|
+
lines = listed.map { |_path, first_line| " - #{first_line}" }
|
|
995
|
+
lines << " … and #{more} more" if more.positive?
|
|
996
|
+
warn(
|
|
997
|
+
"rigor: skipped #{quarantined.size} unparseable RBS file(s) under `signature_paths:`.\n " \
|
|
998
|
+
"They were QUARANTINED so the rest of your RBS env still loads, but the types they\n " \
|
|
999
|
+
"declare are absent — calls into them read `Dynamic[top]`, so coverage and diagnostics\n " \
|
|
1000
|
+
"are reduced. Fix the parse error(s) to restore that coverage:\n" \
|
|
1001
|
+
"#{lines.join("\n")}"
|
|
1002
|
+
)
|
|
1003
|
+
end
|
|
1004
|
+
|
|
1065
1005
|
def warn_about_env_build_failure_once(error)
|
|
1066
1006
|
return if @state[:env_build_warned]
|
|
1067
1007
|
|
|
@@ -1083,15 +1023,12 @@ module Rigor
|
|
|
1083
1023
|
Cache::RbsEnvironment.fetch(loader: self, store: cache_store)
|
|
1084
1024
|
end
|
|
1085
1025
|
|
|
1086
|
-
# Full `Hash<String, RBS::Definition>` tables for the
|
|
1087
|
-
#
|
|
1088
|
-
#
|
|
1089
|
-
# from
|
|
1090
|
-
#
|
|
1091
|
-
#
|
|
1092
|
-
# the eager-table cost is now a compute, not a load. Keys stay
|
|
1093
|
-
# in `RBS::TypeName#to_s` form (top-level prefixed `"::Hash"`)
|
|
1094
|
-
# — the shape {Environment::Reflection} documents.
|
|
1026
|
+
# Full `Hash<String, RBS::Definition>` tables for the {#prewarm} / {#reflection} consumers (ADR-15
|
|
1027
|
+
# Phase 2b/4b.x), which need every definition materialised up front. Built from the (cached) env via
|
|
1028
|
+
# `RBS::DefinitionBuilder` — ADR-54 WD1 retired the disk blobs these used to `Marshal.load` (building
|
|
1029
|
+
# all definitions from a cached env is faster), so the eager-table cost is now a compute, not a load.
|
|
1030
|
+
# Keys stay in `RBS::TypeName#to_s` form (top-level prefixed `"::Hash"`) — the shape
|
|
1031
|
+
# {Environment::Reflection} documents.
|
|
1095
1032
|
def instance_definitions_table
|
|
1096
1033
|
@state[:instance_definitions_table] ||= build_definitions_table do |name|
|
|
1097
1034
|
build_instance_definition(name)
|
|
@@ -1151,15 +1088,12 @@ module Rigor
|
|
|
1151
1088
|
nil
|
|
1152
1089
|
end
|
|
1153
1090
|
|
|
1154
|
-
# Resolve an RBS class/module ALIAS to its canonical declared name.
|
|
1155
|
-
#
|
|
1156
|
-
# `
|
|
1157
|
-
#
|
|
1158
|
-
#
|
|
1159
|
-
#
|
|
1160
|
-
# resolution) before the `class_decls` guard fixes dispatch AND the
|
|
1161
|
-
# `call.undefined-method` existence check on them. A non-alias name, or
|
|
1162
|
-
# one that does not normalise, is returned unchanged.
|
|
1091
|
+
# Resolve an RBS class/module ALIAS to its canonical declared name. `class Mutex = Thread::Mutex`
|
|
1092
|
+
# lives only in `class_alias_decls`, so `class_known?` reports it (it checks that table) but the
|
|
1093
|
+
# definition builder — which only knows `class_decls` — could not enumerate its methods, leaving alias
|
|
1094
|
+
# classes (`Mutex`, and any `X = Y`) with no resolvable method surface. Normalising via the env (RBS's
|
|
1095
|
+
# own alias resolution) before the `class_decls` guard fixes dispatch AND the `call.undefined-method`
|
|
1096
|
+
# existence check on them. A non-alias name, or one that does not normalise, is returned unchanged.
|
|
1163
1097
|
def canonical_module_name(rbs_name)
|
|
1164
1098
|
return rbs_name unless env.class_alias_decls.key?(rbs_name)
|
|
1165
1099
|
|
|
@@ -1168,14 +1102,12 @@ module Rigor
|
|
|
1168
1102
|
rbs_name
|
|
1169
1103
|
end
|
|
1170
1104
|
|
|
1171
|
-
# Memoised on `@state` (the per-loader store also holding `:env` /
|
|
1172
|
-
#
|
|
1173
|
-
#
|
|
1174
|
-
#
|
|
1175
|
-
#
|
|
1176
|
-
#
|
|
1177
|
-
# on nearly every call-site dispatch, so this was a top allocation
|
|
1178
|
-
# site; caching the immutable result (nil included) removes it.
|
|
1105
|
+
# Memoised on `@state` (the per-loader store also holding `:env` / `:builder`): `RBS::TypeName.parse`
|
|
1106
|
+
# is a pure, deterministic function of the normalised string, and the `RBS::TypeName` it returns is a
|
|
1107
|
+
# frozen value object safe to share across callers (every consumer only reads it —
|
|
1108
|
+
# `env.class_decls.key?` / `builder.build_*`). The same handful of class names are parsed on nearly
|
|
1109
|
+
# every call-site dispatch, so this was a top allocation site; caching the immutable result (nil
|
|
1110
|
+
# included) removes it.
|
|
1179
1111
|
def parse_type_name(name)
|
|
1180
1112
|
s = name.to_s
|
|
1181
1113
|
return nil if s.empty?
|
|
@@ -1197,10 +1129,8 @@ module Rigor
|
|
|
1197
1129
|
return false unless rbs_name
|
|
1198
1130
|
return false if env.nil?
|
|
1199
1131
|
|
|
1200
|
-
# `RBS::Environment#class_decls` after `resolve_type_names`
|
|
1201
|
-
#
|
|
1202
|
-
# them under one map post-resolution. Aliases live in their
|
|
1203
|
-
# own table.
|
|
1132
|
+
# `RBS::Environment#class_decls` after `resolve_type_names` holds entries for both classes AND
|
|
1133
|
+
# modules; the gem unifies them under one map post-resolution. Aliases live in their own table.
|
|
1204
1134
|
env.class_decls.key?(rbs_name) || env.class_alias_decls.key?(rbs_name)
|
|
1205
1135
|
rescue ::RBS::BaseError
|
|
1206
1136
|
false
|