rigortype 0.2.7 → 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 +1 -1
- data/docs/manual/04-diagnostics.md +7 -4
- data/docs/manual/06-baseline.md +5 -2
- 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 +19 -26
- 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 +30 -47
- 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 +420 -518
- 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 -1111
- 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 +197 -323
- data/lib/rigor/sig_gen/layout_index.rb +12 -20
- data/lib/rigor/sig_gen/method_candidate.rb +12 -17
- 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 +95 -174
- 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 +71 -108
- 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 -235
- 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 +11 -17
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +20 -34
- 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 +159 -262
- 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
- metadata +2 -1
|
@@ -6,40 +6,27 @@ module Rigor
|
|
|
6
6
|
class Environment
|
|
7
7
|
# Target-project Bundler awareness (O4, implemented).
|
|
8
8
|
#
|
|
9
|
-
# Walks a Bundler-installed gem tree (e.g., the project's
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
# 10 of 343 gems shipped sig — `prism`, `aws-sdk-s3`,
|
|
16
|
-
# `aws-sdk-kms`, `aws-sdk-core`, `playwright-ruby-client`,
|
|
17
|
-
# `mutex_m`, `webrick`, `base64`, `stoplight`, `ffi`), this
|
|
18
|
-
# discovery surfaces the typed contract the gem author
|
|
19
|
-
# explicitly published.
|
|
9
|
+
# Walks a Bundler-installed gem tree (e.g., the project's `vendor/bundle` or a Docker-mounted bundle
|
|
10
|
+
# root) and returns the per-gem `sig/` directories to feed into `RbsLoader`'s `signature_paths:`. Of the
|
|
11
|
+
# ~3% of gems that ship `sig/` in their gem package today (per the four-project Mastodon Docker
|
|
12
|
+
# bundle-install measurement on 2026-05-15: 10 of 343 gems shipped sig — `prism`, `aws-sdk-s3`,
|
|
13
|
+
# `aws-sdk-kms`, `aws-sdk-core`, `playwright-ruby-client`, `mutex_m`, `webrick`, `base64`, `stoplight`,
|
|
14
|
+
# `ffi`), this discovery surfaces the typed contract the gem author explicitly published.
|
|
20
15
|
#
|
|
21
|
-
# Conflicts with rigor's bundled stdlib RBS (the prism case
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
# the offending file is emitted and analysis continues with
|
|
25
|
-
# `Dynamic[top]` everywhere rather than hanging.
|
|
16
|
+
# Conflicts with rigor's bundled stdlib RBS (the prism case was the motivating example) degrade
|
|
17
|
+
# gracefully via O7's failure-memo in `RbsLoader#env`: a single warning naming the offending file is
|
|
18
|
+
# emitted and analysis continues with `Dynamic[top]` everywhere rather than hanging.
|
|
26
19
|
#
|
|
27
|
-
# The discovery is intentionally a pure file-system walk —
|
|
28
|
-
#
|
|
29
|
-
# doesn't need the target project's Bundler context.
|
|
20
|
+
# The discovery is intentionally a pure file-system walk — no `Bundler` API call, no `Gemfile.lock`
|
|
21
|
+
# parse — so rigor doesn't need the target project's Bundler context.
|
|
30
22
|
module BundleSigDiscovery
|
|
31
|
-
# Gems already covered by rigor's `DEFAULT_LIBRARIES`
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
# `
|
|
35
|
-
# motivating example — Ruby 4.0 ships prism's RBS in
|
|
36
|
-
# stdlib, and the gem also ships its own `sig/`, so loading
|
|
37
|
-
# both raises on `Prism::BACKEND` etc.).
|
|
23
|
+
# Gems already covered by rigor's `DEFAULT_LIBRARIES` (stdlib RBS) plus the `data/vendored_gem_sigs/`
|
|
24
|
+
# bundle. Skipping these from bundle discovery prevents `RBS::DuplicatedDeclarationError` (the prism
|
|
25
|
+
# case was the motivating example — Ruby 4.0 ships prism's RBS in stdlib, and the gem also ships its
|
|
26
|
+
# own `sig/`, so loading both raises on `Prism::BACKEND` etc.).
|
|
38
27
|
#
|
|
39
|
-
# The list is hard-coded for the MVP because it tracks
|
|
40
|
-
#
|
|
41
|
-
# under `data/vendored_gem_sigs/` or added to
|
|
42
|
-
# `DEFAULT_LIBRARIES`, add its name here.
|
|
28
|
+
# The list is hard-coded for the MVP because it tracks rigor's bundled coverage 1:1. When a new gem is
|
|
29
|
+
# vendored under `data/vendored_gem_sigs/` or added to `DEFAULT_LIBRARIES`, add its name here.
|
|
43
30
|
SKIPPED_GEMS_BY_DEFAULT = Set[
|
|
44
31
|
# DEFAULT_LIBRARIES (lib/rigor/environment.rb)
|
|
45
32
|
"pathname", "optparse", "json", "yaml", "fileutils",
|
|
@@ -58,31 +45,22 @@ module Rigor
|
|
|
58
45
|
"pg", "mysql2", "nokogiri", "bcrypt", "redis", "idn-ruby"
|
|
59
46
|
].freeze
|
|
60
47
|
|
|
61
|
-
# @param bundle_path [String, Pathname, nil] explicit path
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
# @param
|
|
65
|
-
# `
|
|
66
|
-
# @param
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
-
#
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
# version)
|
|
77
|
-
# as the source of truth for "what gems this project
|
|
78
|
-
# actually declares". Pass `nil` (the default) to keep
|
|
79
|
-
# the pre-Layer-3 behaviour of returning every non-skipped
|
|
80
|
-
# `sig/` under the bundle.
|
|
81
|
-
# @return [Array<Pathname>] every `<gem-dir>/sig` directory
|
|
82
|
-
# under the resolved bundle path, minus any whose gem
|
|
83
|
-
# name is in `skip_gems` and (when `locked_gems` is
|
|
84
|
-
# supplied) minus any whose `(name, version, platform)`
|
|
85
|
-
# does not match a lockfile entry.
|
|
48
|
+
# @param bundle_path [String, Pathname, nil] explicit path to the bundler install root. When `nil`,
|
|
49
|
+
# falls back to `auto_detect` if `auto_detect:` is true.
|
|
50
|
+
# @param project_root [String] resolution base for relative `bundle_path:` and the auto-detect search.
|
|
51
|
+
# @param auto_detect [Boolean] when true and `bundle_path:` is nil, try `.bundle/config`'s
|
|
52
|
+
# `BUNDLE_PATH:` and `vendor/bundle/` under `project_root`.
|
|
53
|
+
# @param skip_gems [Set<String>] gem names to exclude from discovery. Defaults to
|
|
54
|
+
# {SKIPPED_GEMS_BY_DEFAULT}.
|
|
55
|
+
# @param locked_gems [Hash{String => LockfileResolver::LockedGem}, nil] Optional O4-Layer-3 filter.
|
|
56
|
+
# When non-nil and non-empty, only `sig/` directories whose gem `(name, version, platform)` tuple
|
|
57
|
+
# matches a lockfile entry are returned. Bundle entries absent from the lockfile (or at a drifted
|
|
58
|
+
# version) are silently dropped — the lockfile is treated as the source of truth for "what gems this
|
|
59
|
+
# project actually declares". Pass `nil` (the default) to keep the pre-Layer-3 behaviour of
|
|
60
|
+
# returning every non-skipped `sig/` under the bundle.
|
|
61
|
+
# @return [Array<Pathname>] every `<gem-dir>/sig` directory under the resolved bundle path, minus any
|
|
62
|
+
# whose gem name is in `skip_gems` and (when `locked_gems` is supplied) minus any whose `(name,
|
|
63
|
+
# version, platform)` does not match a lockfile entry.
|
|
86
64
|
def self.discover(bundle_path:, project_root: Dir.pwd, auto_detect: true,
|
|
87
65
|
skip_gems: SKIPPED_GEMS_BY_DEFAULT, locked_gems: nil, home: nil)
|
|
88
66
|
resolved = resolve_bundle_path(
|
|
@@ -93,9 +71,8 @@ module Rigor
|
|
|
93
71
|
)
|
|
94
72
|
return [] if resolved.nil?
|
|
95
73
|
|
|
96
|
-
# `<bundle>/ruby/X.Y.Z/gems/<name>-<ver>/sig/` is the
|
|
97
|
-
#
|
|
98
|
-
# picks up whichever Ruby the bundle was installed for.
|
|
74
|
+
# `<bundle>/ruby/X.Y.Z/gems/<name>-<ver>/sig/` is the canonical bundler layout. `*` on the ruby
|
|
75
|
+
# version dir picks up whichever Ruby the bundle was installed for.
|
|
99
76
|
all = Dir.glob(resolved.join("ruby", "*", "gems", "*", "sig")).map { |d| Pathname.new(d) }
|
|
100
77
|
filtered = all.reject { |sig_dir| skip_gems.include?(gem_name_from_sig_path(sig_dir)) }
|
|
101
78
|
return filtered if locked_gems.nil? || locked_gems.empty?
|
|
@@ -104,12 +81,10 @@ module Rigor
|
|
|
104
81
|
filtered.select { |sig_dir| expected_dirs.include?(sig_dir.parent.basename.to_s) }
|
|
105
82
|
end
|
|
106
83
|
|
|
107
|
-
# `{name => LockedGem}` → set of canonical bundler gem
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
# Lockfile platform `"ruby"` is the pure-Ruby case; any
|
|
112
|
-
# other value is treated as a platform tag.
|
|
84
|
+
# `{name => LockedGem}` → set of canonical bundler gem directory basenames. Pure-Ruby gems install as
|
|
85
|
+
# `<name>-<version>`; platform-specific gems install as `<name>-<version>-<platform>` (e.g.
|
|
86
|
+
# `ffi-1.17.4-aarch64-linux-gnu`). Lockfile platform `"ruby"` is the pure-Ruby case; any other value is
|
|
87
|
+
# treated as a platform tag.
|
|
113
88
|
def self.expected_gem_dirs(locked_gems)
|
|
114
89
|
locked_gems.each_value.with_object(Set.new) do |locked, set|
|
|
115
90
|
base = "#{locked.name}-#{locked.version}"
|
|
@@ -122,28 +97,22 @@ module Rigor
|
|
|
122
97
|
end
|
|
123
98
|
private_class_method :expected_gem_dirs
|
|
124
99
|
|
|
125
|
-
# `<bundle>/ruby/X.Y.Z/gems/<name>-<ver>/sig` → `<name>`.
|
|
126
|
-
#
|
|
127
|
-
#
|
|
128
|
-
#
|
|
129
|
-
# variants like `ffi-1.17.4-aarch64-linux-gnu/` keep their
|
|
130
|
-
# platform suffix in the version part, so the first hyphen
|
|
131
|
-
# from the right is still the name boundary.)
|
|
100
|
+
# `<bundle>/ruby/X.Y.Z/gems/<name>-<ver>/sig` → `<name>`. The gem directory follows the canonical
|
|
101
|
+
# `<name>-<version>` pattern; we strip everything from the last hyphen onwards to recover the name.
|
|
102
|
+
# (Platform-tagged variants like `ffi-1.17.4-aarch64-linux-gnu/` keep their platform suffix in the
|
|
103
|
+
# version part, so the first hyphen from the right is still the name boundary.)
|
|
132
104
|
#
|
|
133
|
-
# Public so the O4 Layer 3 slice-3 coverage report
|
|
134
|
-
#
|
|
135
|
-
# against locked gem names without re-running discovery.
|
|
105
|
+
# Public so the O4 Layer 3 slice-3 coverage report (`RbsCoverageReport`) can classify discovered bundle
|
|
106
|
+
# sigs against locked gem names without re-running discovery.
|
|
136
107
|
def self.gem_name_from_sig_path(sig_dir)
|
|
137
108
|
gem_dir = sig_dir.parent.basename.to_s
|
|
138
|
-
# Strip `-<version>` and any platform suffix. The version
|
|
139
|
-
#
|
|
140
|
-
# `-` followed by a digit.
|
|
109
|
+
# Strip `-<version>` and any platform suffix. The version always starts with a digit, so split at
|
|
110
|
+
# the first `-` followed by a digit.
|
|
141
111
|
gem_dir.sub(/-\d.*\z/, "")
|
|
142
112
|
end
|
|
143
113
|
|
|
144
|
-
# Returns `Pathname` resolved bundle path, or `nil` when
|
|
145
|
-
#
|
|
146
|
-
# banner so end users can see what rigor picked up.
|
|
114
|
+
# Returns `Pathname` resolved bundle path, or `nil` when neither explicit nor auto-detected. Public
|
|
115
|
+
# for the stats banner so end users can see what rigor picked up.
|
|
147
116
|
def self.resolve_bundle_path(bundle_path:, project_root: Dir.pwd, auto_detect: true, home: nil)
|
|
148
117
|
if bundle_path
|
|
149
118
|
path = Pathname.new(File.expand_path(bundle_path.to_s, project_root))
|
|
@@ -158,38 +127,28 @@ module Rigor
|
|
|
158
127
|
Pathname.new(detected) if detected
|
|
159
128
|
end
|
|
160
129
|
|
|
161
|
-
# Auto-detection order — project-local strategies win over the
|
|
162
|
-
#
|
|
163
|
-
# 1. `<project_root>/.bundle/config` carries `BUNDLE_PATH:`
|
|
164
|
-
#
|
|
165
|
-
# 2. `<project_root>/vendor/bundle/` — the conventional
|
|
166
|
-
# in-tree install location when a developer ran
|
|
130
|
+
# Auto-detection order — project-local strategies win over the user-global one, mirroring Bundler's own
|
|
131
|
+
# config precedence:
|
|
132
|
+
# 1. `<project_root>/.bundle/config` carries `BUNDLE_PATH:` set by `bundle config set --local path
|
|
133
|
+
# <dir>`.
|
|
134
|
+
# 2. `<project_root>/vendor/bundle/` — the conventional in-tree install location when a developer ran
|
|
167
135
|
# `bundle install --path vendor/bundle`.
|
|
168
|
-
# 3. The user-global bundler config `<home>/.bundle/config`
|
|
169
|
-
#
|
|
170
|
-
#
|
|
171
|
-
#
|
|
172
|
-
#
|
|
173
|
-
# consulted only when steps 1–2 found nothing, so it never
|
|
174
|
-
# changes an already-working detection.
|
|
175
|
-
# 4. `nil` — let the caller proceed without bundle sig
|
|
176
|
-
# discovery (rigor's vendored RBS still loads).
|
|
136
|
+
# 3. The user-global bundler config `<home>/.bundle/config` `BUNDLE_PATH:` (`bundle config set
|
|
137
|
+
# --global path <dir>`), resolved relative to the project root and used only when it points at an
|
|
138
|
+
# existing directory — the last resort for a project with no in-tree bundle. Purely additive: it is
|
|
139
|
+
# consulted only when steps 1–2 found nothing, so it never changes an already-working detection.
|
|
140
|
+
# 4. `nil` — let the caller proceed without bundle sig discovery (rigor's vendored RBS still loads).
|
|
177
141
|
#
|
|
178
|
-
# Note (ADR-27): rigor reads the *project* as data, so
|
|
179
|
-
#
|
|
180
|
-
#
|
|
181
|
-
#
|
|
182
|
-
#
|
|
183
|
-
#
|
|
184
|
-
#
|
|
185
|
-
# supply signatures via `rbs collection install` /
|
|
186
|
-
# `dependencies.source_inference:`. `BUNDLE_PATH` from rigor's
|
|
187
|
-
# own environment is deliberately NOT consulted — it describes
|
|
188
|
-
# rigor's bundle, not the analyzed project's.
|
|
142
|
+
# Note (ADR-27): rigor reads the *project* as data, so detection is limited to paths recorded in
|
|
143
|
+
# project-local or user-global Bundler config files. The pure-default install location — gems in the
|
|
144
|
+
# active Ruby's GEM_HOME with no `path` configured — is the *project's* Ruby's gem home, which the
|
|
145
|
+
# isolated analyzer cannot know without running the project's toolchain. Point rigor at it with
|
|
146
|
+
# `bundler.bundle_path:`, or supply signatures via `rbs collection install` /
|
|
147
|
+
# `dependencies.source_inference:`. `BUNDLE_PATH` from rigor's own environment is deliberately NOT
|
|
148
|
+
# consulted — it describes rigor's bundle, not the analyzed project's.
|
|
189
149
|
#
|
|
190
|
-
# `home:` defaults to the invoking user's home directory; it is
|
|
191
|
-
#
|
|
192
|
-
# `~/.bundle/config`).
|
|
150
|
+
# `home:` defaults to the invoking user's home directory; it is a parameter so tests stay hermetic (no
|
|
151
|
+
# read of the real `~/.bundle/config`).
|
|
193
152
|
def self.auto_detect(project_root:, home: nil)
|
|
194
153
|
from_config = read_bundle_config_path(File.join(project_root, ".bundle", "config"))
|
|
195
154
|
return File.expand_path(from_config, project_root) if from_config
|
|
@@ -200,10 +159,9 @@ module Rigor
|
|
|
200
159
|
global_bundle_path(project_root: project_root, home: home)
|
|
201
160
|
end
|
|
202
161
|
|
|
203
|
-
# Resolves the user-global `BUNDLE_PATH` against the project
|
|
204
|
-
#
|
|
205
|
-
#
|
|
206
|
-
# configured path does not exist.
|
|
162
|
+
# Resolves the user-global `BUNDLE_PATH` against the project root, returning it only when it is an
|
|
163
|
+
# existing directory. Returns nil when there is no global config, no home, or the configured path does
|
|
164
|
+
# not exist.
|
|
207
165
|
def self.global_bundle_path(project_root:, home:)
|
|
208
166
|
home ||= default_home
|
|
209
167
|
return nil if home.nil?
|
|
@@ -226,17 +184,15 @@ module Rigor
|
|
|
226
184
|
def self.read_bundle_config_path(config_path)
|
|
227
185
|
return nil unless config_path && File.exist?(config_path)
|
|
228
186
|
|
|
229
|
-
# `.bundle/config` is YAML with all-caps env-style keys.
|
|
230
|
-
#
|
|
231
|
-
# `--path` flag sets it.
|
|
187
|
+
# `.bundle/config` is YAML with all-caps env-style keys. `BUNDLE_PATH:` is the canonical key
|
|
188
|
+
# (Bundler 2.x); the `--path` flag sets it.
|
|
232
189
|
data = YAML.safe_load_file(config_path)
|
|
233
190
|
return nil unless data.is_a?(Hash)
|
|
234
191
|
|
|
235
192
|
path = data["BUNDLE_PATH"]
|
|
236
193
|
path && !path.to_s.empty? ? path.to_s : nil
|
|
237
194
|
rescue StandardError
|
|
238
|
-
# Malformed `.bundle/config` should not break analysis;
|
|
239
|
-
# silently skip auto-detection.
|
|
195
|
+
# Malformed `.bundle/config` should not break analysis; silently skip auto-detection.
|
|
240
196
|
nil
|
|
241
197
|
end
|
|
242
198
|
|
|
@@ -4,14 +4,13 @@ require_relative "../type"
|
|
|
4
4
|
|
|
5
5
|
module Rigor
|
|
6
6
|
class Environment
|
|
7
|
-
# Resolves Ruby Class/Module objects to Rigor::Type::Nominal instances.
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# to `RbsLoader` for all other names — the two tiers are complementary.
|
|
7
|
+
# Resolves Ruby Class/Module objects to Rigor::Type::Nominal instances. The hardcoded list spans the core
|
|
8
|
+
# classes the literal typer (Slice 1) and the constant-resolution path (Slice 2 strengthening) need.
|
|
9
|
+
# This is the static fast path for built-ins; `Environment` falls through to `RbsLoader` for all other
|
|
10
|
+
# names — the two tiers are complementary.
|
|
12
11
|
#
|
|
13
|
-
# See docs/internal-spec/inference-engine.md for the binding contract
|
|
14
|
-
#
|
|
12
|
+
# See docs/internal-spec/inference-engine.md for the binding contract (every entry below MUST always be
|
|
13
|
+
# recognised).
|
|
15
14
|
class ClassRegistry
|
|
16
15
|
SLICE_1_BUILT_INS = [
|
|
17
16
|
Integer,
|
|
@@ -25,11 +24,10 @@ module Rigor
|
|
|
25
24
|
BasicObject
|
|
26
25
|
].freeze
|
|
27
26
|
|
|
28
|
-
# Common Ruby core classes that user code routinely names by constant
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
# extends coverage to all other RBS-declared names (Slice 4, landed).
|
|
27
|
+
# Common Ruby core classes that user code routinely names by constant reference. Adding them to the
|
|
28
|
+
# registry lets `nominal_for_name` resolve `Array`, `Hash`, etc. without each call site re-listing
|
|
29
|
+
# them. The static registry is the cheap fast path; `RbsLoader` extends coverage to all other
|
|
30
|
+
# RBS-declared names (Slice 4, landed).
|
|
33
31
|
SLICE_2_BUILT_INS = [
|
|
34
32
|
Array,
|
|
35
33
|
Hash,
|
|
@@ -68,15 +66,11 @@ module Rigor
|
|
|
68
66
|
|
|
69
67
|
private
|
|
70
68
|
|
|
71
|
-
# ADR-15 Phase 4b — the default registry MUST be
|
|
72
|
-
# `
|
|
73
|
-
# `
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
# via `register`, then `Ractor.make_shareable`
|
|
77
|
-
# recursively freezes the registry, the two Hashes,
|
|
78
|
-
# and confirms every entry (Type::Nominal carriers +
|
|
79
|
-
# core Ruby classes) is itself shareable.
|
|
69
|
+
# ADR-15 Phase 4b — the default registry MUST be `Ractor.shareable?` so worker Ractors that consult
|
|
70
|
+
# `Environment.for_project`'s default `class_registry:` don't trip `Ractor::IsolationError`. The
|
|
71
|
+
# internal `@nominals` / `@class_objects` Hashes are populated via `register`, then
|
|
72
|
+
# `Ractor.make_shareable` recursively freezes the registry, the two Hashes, and confirms every entry
|
|
73
|
+
# (Type::Nominal carriers + core Ruby classes) is itself shareable.
|
|
80
74
|
def build_default
|
|
81
75
|
registry = new
|
|
82
76
|
CORE_BUILT_INS.each { |klass| registry.register(klass) }
|
|
@@ -112,11 +106,10 @@ module Rigor
|
|
|
112
106
|
@nominals.fetch(class_object.name)
|
|
113
107
|
end
|
|
114
108
|
|
|
115
|
-
# Nil-safe lookup by class name. Accepts Symbol or String. Returns the
|
|
116
|
-
#
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
# MUST NOT raise and MUST flow through the engine's tracer.
|
|
109
|
+
# Nil-safe lookup by class name. Accepts Symbol or String. Returns the registered Rigor::Type::Nominal,
|
|
110
|
+
# or nil when the name is unknown. Used by ExpressionTyper to resolve Prism::ConstantReadNode and
|
|
111
|
+
# Prism::ConstantPathNode under the fail-soft policy: unknown names MUST NOT raise and MUST flow
|
|
112
|
+
# through the engine's tracer.
|
|
120
113
|
def nominal_for_name(name)
|
|
121
114
|
return nil if name.nil?
|
|
122
115
|
|
|
@@ -2,32 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
module Rigor
|
|
4
4
|
class Environment
|
|
5
|
-
# Per-key memoization container for {Environment#constant_for_name}.
|
|
6
|
-
#
|
|
7
|
-
# constant-resolution results on first access — the sibling of
|
|
5
|
+
# Per-key memoization container for {Environment#constant_for_name}. Held by {Environment} so the
|
|
6
|
+
# otherwise-frozen instance can cache constant-resolution results on first access — the sibling of
|
|
8
7
|
# {HktRegistryHolder}, generalised from one slot to a name-keyed map.
|
|
9
8
|
#
|
|
10
|
-
# Why this exists: `constant_for_name` is a pure function of its
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
# same qualified names recur thousands of times across files — and
|
|
17
|
-
# each miss runs a `const_get` walk that raises + rescues `NameError`
|
|
18
|
-
# for every project-defined constant (the common case). Caching the
|
|
9
|
+
# Why this exists: `constant_for_name` is a pure function of its name for a given Environment (both the
|
|
10
|
+
# predefined-constant refinement table and the RBS loader's constant table are fixed for the
|
|
11
|
+
# Environment's lifetime), yet it is the hottest non-dispatch path on a large Rails app.
|
|
12
|
+
# {ExpressionTyper#resolve_constant_name} peels a lexical-candidate ladder per constant reference, so the
|
|
13
|
+
# same qualified names recur thousands of times across files — and each miss runs a `const_get` walk
|
|
14
|
+
# that raises + rescues `NameError` for every project-defined constant (the common case). Caching the
|
|
19
15
|
# result collapses the repeats to a hash lookup.
|
|
20
16
|
#
|
|
21
|
-
# Caches `nil` results too (a name that resolves to no refined /
|
|
22
|
-
#
|
|
23
|
-
# const_get-and-rescue is paid at most once per distinct name.
|
|
17
|
+
# Caches `nil` results too (a name that resolves to no refined / RBS constant type stays unresolved for
|
|
18
|
+
# the run), so the const_get-and-rescue is paid at most once per distinct name.
|
|
24
19
|
#
|
|
25
|
-
# Concurrency: single-threaded use only, the same discipline as
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
# multi-threaded reader against one Environment, the synchronisation
|
|
30
|
-
# belongs at that caller's seam, not here.
|
|
20
|
+
# Concurrency: single-threaded use only, the same discipline as {HktRegistryHolder} — the Ractor pool
|
|
21
|
+
# builds a per-worker Environment and the LSP single-publish invariant serialises the shared-Environment
|
|
22
|
+
# reader path. If a future caller introduces a multi-threaded reader against one Environment, the
|
|
23
|
+
# synchronisation belongs at that caller's seam, not here.
|
|
31
24
|
class ConstantTypeCacheHolder
|
|
32
25
|
def initialize
|
|
33
26
|
@cache = {}
|
|
@@ -2,19 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
module Rigor
|
|
4
4
|
class Environment
|
|
5
|
-
# ADR-20 slice 2e — mutable single-slot memoization
|
|
6
|
-
#
|
|
7
|
-
# {Environment} so the otherwise-frozen instance can
|
|
8
|
-
# still cache a computed value on first access.
|
|
5
|
+
# ADR-20 slice 2e — mutable single-slot memoization container for the per-Environment HKT registry. Held
|
|
6
|
+
# by {Environment} so the otherwise-frozen instance can still cache a computed value on first access.
|
|
9
7
|
#
|
|
10
|
-
# Concurrent {#fetch} calls from multiple threads against
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# cross-thread races. If a future caller introduces a
|
|
15
|
-
# multi-threaded reader path against a shared
|
|
16
|
-
# Environment, the synchronisation belongs at that
|
|
17
|
-
# caller's seam, not here.
|
|
8
|
+
# Concurrent {#fetch} calls from multiple threads against one Environment are NOT serialised here — the
|
|
9
|
+
# LSP single-publish-at-a-time discipline and the Ractor pool's per-worker Environment shape already
|
|
10
|
+
# prevent cross-thread races. If a future caller introduces a multi-threaded reader path against a
|
|
11
|
+
# shared Environment, the synchronisation belongs at that caller's seam, not here.
|
|
18
12
|
class HktRegistryHolder
|
|
19
13
|
def initialize
|
|
20
14
|
@loaded = false
|
|
@@ -4,32 +4,25 @@ module Rigor
|
|
|
4
4
|
class Environment
|
|
5
5
|
# Gemfile.lock parser for target-project bundler awareness (O4 Layer 3, implemented).
|
|
6
6
|
#
|
|
7
|
-
# Parses a target project's `Gemfile.lock` via Bundler's
|
|
8
|
-
# `
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# bundler install root are limited to gems the project
|
|
13
|
-
# actually declares (and at the version it declared them).
|
|
7
|
+
# Parses a target project's `Gemfile.lock` via Bundler's `LockfileParser` and exposes the locked gem set
|
|
8
|
+
# as a frozen `Hash[String, LockfileResolver::LockedGem]` keyed by gem name. Used by
|
|
9
|
+
# {Rigor::Environment::BundleSigDiscovery} as a filter so the discovered `sig/` directories under the
|
|
10
|
+
# bundler install root are limited to gems the project actually declares (and at the version it declared
|
|
11
|
+
# them).
|
|
14
12
|
#
|
|
15
|
-
# The resolver is intentionally read-only. It does NOT load
|
|
16
|
-
# the
|
|
17
|
-
#
|
|
18
|
-
# target project's Bundler context. It only reads bytes from
|
|
19
|
-
# the lockfile.
|
|
13
|
+
# The resolver is intentionally read-only. It does NOT load the project's `Gemfile`, does NOT resolve
|
|
14
|
+
# dependencies, does NOT touch the network, and does NOT require the target project's Bundler context. It
|
|
15
|
+
# only reads bytes from the lockfile.
|
|
20
16
|
#
|
|
21
|
-
# Failure modes are deliberately quiet: a missing or
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
# failures should pass an explicit `bundler.lockfile:` and
|
|
25
|
-
# check the result via the stats banner.
|
|
17
|
+
# Failure modes are deliberately quiet: a missing or malformed lockfile returns an empty map. The
|
|
18
|
+
# auto-detect path is the configuration default; users who want hard failures should pass an explicit
|
|
19
|
+
# `bundler.lockfile:` and check the result via the stats banner.
|
|
26
20
|
module LockfileResolver
|
|
27
21
|
# Frozen value object for one locked gem entry.
|
|
28
22
|
#
|
|
29
|
-
# `version` is the resolved version string (e.g. "8.0.1");
|
|
30
|
-
# `
|
|
31
|
-
#
|
|
32
|
-
# raw String otherwise (e.g. "aarch64-linux-gnu").
|
|
23
|
+
# `version` is the resolved version string (e.g. "8.0.1"); `platform` is the lockfile's platform tag,
|
|
24
|
+
# normalised to `"ruby"` when the lockfile records `ruby` and to the raw String otherwise (e.g.
|
|
25
|
+
# "aarch64-linux-gnu").
|
|
33
26
|
LockedGem = Data.define(:name, :version, :platform) do
|
|
34
27
|
def initialize(name:, version:, platform:)
|
|
35
28
|
super(
|
|
@@ -40,18 +33,15 @@ module Rigor
|
|
|
40
33
|
end
|
|
41
34
|
end
|
|
42
35
|
|
|
43
|
-
# @param lockfile_path [String, Pathname, nil] explicit path
|
|
44
|
-
# to
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
# @param auto_detect [Boolean] when true and
|
|
49
|
-
# `lockfile_path:` is nil, look for
|
|
36
|
+
# @param lockfile_path [String, Pathname, nil] explicit path to the Gemfile.lock. When `nil`, falls
|
|
37
|
+
# back to `auto_detect` if `auto_detect:` is true.
|
|
38
|
+
# @param project_root [String] resolution base for a relative `lockfile_path:` and the auto-detect
|
|
39
|
+
# search.
|
|
40
|
+
# @param auto_detect [Boolean] when true and `lockfile_path:` is nil, look for
|
|
50
41
|
# `<project_root>/Gemfile.lock`.
|
|
51
|
-
# @return [Hash{String => LockedGem}] frozen map of gem
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
# unreadable, or when Bundler refuses to parse it.
|
|
42
|
+
# @return [Hash{String => LockedGem}] frozen map of gem name → locked entry. Returns the empty frozen
|
|
43
|
+
# hash when no lockfile is resolvable, when the file is unreadable, or when Bundler refuses to parse
|
|
44
|
+
# it.
|
|
55
45
|
def self.locked_gems(lockfile_path:, project_root: Dir.pwd, auto_detect: true)
|
|
56
46
|
resolved = resolve_lockfile_path(
|
|
57
47
|
lockfile_path: lockfile_path,
|
|
@@ -63,9 +53,8 @@ module Rigor
|
|
|
63
53
|
parse(resolved)
|
|
64
54
|
end
|
|
65
55
|
|
|
66
|
-
# Returns the resolved lockfile path (`Pathname`) or `nil`
|
|
67
|
-
#
|
|
68
|
-
# Public so the stats banner can show what rigor picked up.
|
|
56
|
+
# Returns the resolved lockfile path (`Pathname`) or `nil` when neither explicit nor auto-detect
|
|
57
|
+
# produces one. Public so the stats banner can show what rigor picked up.
|
|
69
58
|
def self.resolve_lockfile_path(lockfile_path:, project_root: Dir.pwd, auto_detect: true)
|
|
70
59
|
if lockfile_path
|
|
71
60
|
path = Pathname.new(File.expand_path(lockfile_path.to_s, project_root))
|
|
@@ -83,12 +72,9 @@ module Rigor
|
|
|
83
72
|
EMPTY = {}.freeze
|
|
84
73
|
private_constant :EMPTY
|
|
85
74
|
|
|
86
|
-
# Parses a Gemfile.lock at the given path. Bundler load
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
# lockfile is malformed. A single warning is emitted to
|
|
90
|
-
# `$stderr` so the user can see why their lockfile was
|
|
91
|
-
# ignored.
|
|
75
|
+
# Parses a Gemfile.lock at the given path. Bundler load errors and malformed lockfile bytes both
|
|
76
|
+
# surface as the empty frozen hash; analysis must not crash because a lockfile is malformed. A single
|
|
77
|
+
# warning is emitted to `$stderr` so the user can see why their lockfile was ignored.
|
|
92
78
|
def self.parse(path)
|
|
93
79
|
require "bundler"
|
|
94
80
|
rescue LoadError => e
|
|
@@ -103,12 +89,10 @@ module Rigor
|
|
|
103
89
|
body = File.read(path.to_s)
|
|
104
90
|
parser = Bundler::LockfileParser.new(body)
|
|
105
91
|
locked = parser.specs.each_with_object({}) do |spec, h|
|
|
106
|
-
# `Bundler::LazySpecification` carries name, version,
|
|
107
|
-
#
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
# does NOT declare `LazySpecification#platform` so the
|
|
111
|
-
# call site needs a suppression marker.
|
|
92
|
+
# `Bundler::LazySpecification` carries name, version, platform. Platform is `Gem::Platform` or the
|
|
93
|
+
# symbol `:ruby`; both stringify cleanly. The upstream bundler RBS shim
|
|
94
|
+
# (references/rbs/sig/shims/bundler.rbs) does NOT declare `LazySpecification#platform` so the call
|
|
95
|
+
# site needs a suppression marker.
|
|
112
96
|
platform = spec.platform.to_s # rigor:disable undefined-method
|
|
113
97
|
h[spec.name.to_s] = LockedGem.new(
|
|
114
98
|
name: spec.name, version: spec.version.to_s, platform: platform
|