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
|
@@ -2,26 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module Analysis
|
|
5
|
-
# ADR-46 slice 1 — records, per analyzed file, which OTHER source
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# resolved to nothing (negative edges — adding that symbol later must
|
|
9
|
-
# re-check the consumer).
|
|
5
|
+
# ADR-46 slice 1 — records, per analyzed file, which OTHER source files its analysis read declarations /
|
|
6
|
+
# method bodies from (the cross-file dependency edges), plus the cross-file lookups that resolved to
|
|
7
|
+
# nothing (negative edges — adding that symbol later must re-check the consumer).
|
|
10
8
|
#
|
|
11
|
-
# Thread-local and activated per `analyze_file` only when the runner
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# accessors pay nothing. Recording is purely observational — it never
|
|
15
|
-
# changes a diagnostic.
|
|
9
|
+
# Thread-local and activated per `analyze_file` only when the runner opts in (`record_dependencies: true`);
|
|
10
|
+
# a normal run never activates it, so {active?} is a single nil-check and the instrumented `Scope`
|
|
11
|
+
# accessors pay nothing. Recording is purely observational — it never changes a diagnostic.
|
|
16
12
|
#
|
|
17
|
-
# Modelled on {Inference::BudgetTrace}: process-thread-local state, a
|
|
18
|
-
#
|
|
13
|
+
# Modelled on {Inference::BudgetTrace}: process-thread-local state, a cheap disabled fast path, and a
|
|
14
|
+
# frozen snapshot for consumers.
|
|
19
15
|
module DependencyRecorder
|
|
20
16
|
KEY = :__rigor_dependency_recorder__
|
|
21
17
|
private_constant :KEY
|
|
22
18
|
|
|
23
|
-
# Mutable per-consumer accumulator. Frozen into a {Record} snapshot
|
|
24
|
-
# when `record_for` returns.
|
|
19
|
+
# Mutable per-consumer accumulator. Frozen into a {Record} snapshot when `record_for` returns.
|
|
25
20
|
class Accumulator
|
|
26
21
|
attr_reader :consumer, :sources, :missing, :symbol_sources, :ancestry_sources
|
|
27
22
|
|
|
@@ -31,8 +26,8 @@ module Rigor
|
|
|
31
26
|
@missing = Set.new
|
|
32
27
|
# ADR-46 slice 4 — symbol-granularity tracking.
|
|
33
28
|
# `symbol_sources`: source_path → Set<"ClassName#method"> for method-call deps.
|
|
34
|
-
# `ancestry_sources`: Set<source_path> for class-ancestry (superclass / include)
|
|
35
|
-
#
|
|
29
|
+
# `ancestry_sources`: Set<source_path> for class-ancestry (superclass / include) deps —
|
|
30
|
+
# file-granularity by nature (a superclass edge touches the whole class).
|
|
36
31
|
@symbol_sources = Hash.new { |h, k| h[k] = Set.new }
|
|
37
32
|
@ancestry_sources = Set.new
|
|
38
33
|
end
|
|
@@ -54,22 +49,19 @@ module Rigor
|
|
|
54
49
|
# `ancestry_sources`: frozen Set<source_path> (class-ancestry edges, file-granularity).
|
|
55
50
|
Record = Data.define(:consumer, :sources, :missing, :symbol_sources, :ancestry_sources)
|
|
56
51
|
|
|
57
|
-
# Module-level activation count so the disabled fast path
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
# little as possible. The per-thread accumulator still isolates the
|
|
62
|
-
# actual recording, so a non-recording thread seeing `active?` true
|
|
52
|
+
# Module-level activation count so the disabled fast path ({active?}) is a plain integer read rather
|
|
53
|
+
# than a `Thread.current` hash lookup — `user_def_for` (the instrumented accessor) is on the
|
|
54
|
+
# per-dispatch hot path, so a normal (non-recording) run must pay as little as possible. The per-thread
|
|
55
|
+
# accumulator still isolates the actual recording, so a non-recording thread seeing `active?` true
|
|
63
56
|
# (another thread is recording) just performs an extra nil-check.
|
|
64
57
|
@active_count = 0
|
|
65
58
|
@mutex = Mutex.new
|
|
66
59
|
|
|
67
60
|
module_function
|
|
68
61
|
|
|
69
|
-
# Activates recording for `consumer` (the path being analyzed) for
|
|
70
|
-
# the
|
|
71
|
-
#
|
|
72
|
-
# restores the previous recorder on exit.
|
|
62
|
+
# Activates recording for `consumer` (the path being analyzed) for the duration of the block and returns
|
|
63
|
+
# the frozen {Record}. Nests safely (the inner consumer's reads do not leak to the outer one); restores
|
|
64
|
+
# the previous recorder on exit.
|
|
73
65
|
def record_for(consumer)
|
|
74
66
|
previous = Thread.current[KEY]
|
|
75
67
|
accumulator = Accumulator.new(consumer.to_s)
|
|
@@ -82,20 +74,16 @@ module Rigor
|
|
|
82
74
|
@mutex.synchronize { @active_count -= 1 }
|
|
83
75
|
end
|
|
84
76
|
|
|
85
|
-
# Plain integer read (GVL-atomic) — no `Thread.current` lookup on the
|
|
86
|
-
# disabled fast path.
|
|
77
|
+
# Plain integer read (GVL-atomic) — no `Thread.current` lookup on the disabled fast path.
|
|
87
78
|
def active?
|
|
88
79
|
@active_count.positive?
|
|
89
80
|
end
|
|
90
81
|
|
|
91
|
-
# Records that the current consumer read a declaration / body whose
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
# a
|
|
95
|
-
# `
|
|
96
|
-
# Without `symbol` the read is a class-ancestry edge (file-granularity)
|
|
97
|
-
# and is added to `ancestry_sources` only.
|
|
98
|
-
# Self-reads and nil sites are ignored in all cases.
|
|
82
|
+
# Records that the current consumer read a declaration / body whose definition site is `path_line` (a
|
|
83
|
+
# `"path:line"` String, or nil). When `symbol` is given (a `"ClassName#method"` String), the read is a
|
|
84
|
+
# method-call edge and is recorded at symbol granularity in `symbol_sources` in addition to the coarse
|
|
85
|
+
# `sources` set. Without `symbol` the read is a class-ancestry edge (file-granularity) and is added to
|
|
86
|
+
# `ancestry_sources` only. Self-reads and nil sites are ignored in all cases.
|
|
99
87
|
def read_site(path_line, symbol = nil)
|
|
100
88
|
accumulator = Thread.current[KEY]
|
|
101
89
|
return if accumulator.nil? || path_line.nil?
|
|
@@ -111,8 +99,8 @@ module Rigor
|
|
|
111
99
|
end
|
|
112
100
|
end
|
|
113
101
|
|
|
114
|
-
# Records a cross-file lookup of `name` (kind `:method` / `:class` /
|
|
115
|
-
#
|
|
102
|
+
# Records a cross-file lookup of `name` (kind `:method` / `:class` / `:const` / …) that resolved to
|
|
103
|
+
# nothing — a negative dependency.
|
|
116
104
|
def read_missing(kind, name)
|
|
117
105
|
accumulator = Thread.current[KEY]
|
|
118
106
|
accumulator&.missing&.add("#{kind}:#{name}")
|
|
@@ -3,26 +3,19 @@
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module Analysis
|
|
5
5
|
module DependencySourceInference
|
|
6
|
-
# ADR-10 slice 5c — per-run accumulator for the
|
|
7
|
-
# `dynamic.dependency-source.boundary-cross` `:info`
|
|
6
|
+
# ADR-10 slice 5c — per-run accumulator for the `dynamic.dependency-source.boundary-cross` `:info`
|
|
8
7
|
# diagnostic.
|
|
9
8
|
#
|
|
10
|
-
# The diagnostic fires when both an authoritative source
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# source's answer (per ADR-10's tier order), but records
|
|
15
|
-
# the boundary crossing so the user can audit whether RBS
|
|
16
|
-
# and the gem source have drifted.
|
|
9
|
+
# The diagnostic fires when both an authoritative source (RBS today; plugins later) AND a `mode: :full`
|
|
10
|
+
# opt-in gem's source catalog resolve the same `(class_name, method_name)`. The dispatcher takes the
|
|
11
|
+
# authoritative source's answer (per ADR-10's tier order), but records the boundary crossing so the user
|
|
12
|
+
# can audit whether RBS and the gem source have drifted.
|
|
17
13
|
#
|
|
18
|
-
# The accumulator deduplicates per `(class_name,
|
|
19
|
-
# method_name, gem_name)` so a method called from many
|
|
14
|
+
# The accumulator deduplicates per `(class_name, method_name, gem_name)` so a method called from many
|
|
20
15
|
# files yields one diagnostic.
|
|
21
16
|
#
|
|
22
|
-
# Used in the same pattern as
|
|
23
|
-
# {Rigor::
|
|
24
|
-
# events into the per-run instance; {Rigor::Analysis::Runner}
|
|
25
|
-
# drains it at end-of-run into a flat
|
|
17
|
+
# Used in the same pattern as {Rigor::RbsExtended::Reporter}: the dispatcher writes events into the
|
|
18
|
+
# per-run instance; {Rigor::Analysis::Runner} drains it at end-of-run into a flat
|
|
26
19
|
# `Rigor::Analysis::Diagnostic` list.
|
|
27
20
|
class BoundaryCrossReporter
|
|
28
21
|
Entry = Data.define(:class_name, :method_name, :gem_name, :rbs_display)
|
|
@@ -32,12 +25,9 @@ module Rigor
|
|
|
32
25
|
@mutex = Mutex.new
|
|
33
26
|
end
|
|
34
27
|
|
|
35
|
-
# @return [Array<Entry>] frozen snapshot of the recorded
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
# `gem_name` (String), and `rbs_display` (String —
|
|
39
|
-
# the authoritative-side type's human-facing form,
|
|
40
|
-
# embedded into the diagnostic message).
|
|
28
|
+
# @return [Array<Entry>] frozen snapshot of the recorded boundary-cross events. Each entry is a Data
|
|
29
|
+
# with `class_name` (String), `method_name` (Symbol), `gem_name` (String), and `rbs_display` (String
|
|
30
|
+
# — the authoritative-side type's human-facing form, embedded into the diagnostic message).
|
|
41
31
|
def entries
|
|
42
32
|
@mutex.synchronize { @entries.dup.freeze }
|
|
43
33
|
end
|
|
@@ -46,10 +36,8 @@ module Rigor
|
|
|
46
36
|
@mutex.synchronize { @entries.empty? }
|
|
47
37
|
end
|
|
48
38
|
|
|
49
|
-
# Records one boundary-cross event. Deduplicates on the
|
|
50
|
-
#
|
|
51
|
-
# diagnostic per-receiver-per-method-per-owning-gem is
|
|
52
|
-
# the actionable unit.
|
|
39
|
+
# Records one boundary-cross event. Deduplicates on the `(class_name, method_name, gem_name)` triple —
|
|
40
|
+
# the diagnostic per-receiver-per-method-per-owning-gem is the actionable unit.
|
|
53
41
|
def record(class_name:, method_name:, gem_name:, rbs_display:)
|
|
54
42
|
entry = Entry.new(
|
|
55
43
|
class_name: class_name, method_name: method_name,
|
|
@@ -7,17 +7,13 @@ require_relative "walker"
|
|
|
7
7
|
module Rigor
|
|
8
8
|
module Analysis
|
|
9
9
|
module DependencySourceInference
|
|
10
|
-
# Folds a `Configuration::Dependencies` value into a
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# and aggregates the per-gem method catalogs into the
|
|
15
|
-
# Index's flat `(class_name, method_name) → kind` table.
|
|
10
|
+
# Folds a `Configuration::Dependencies` value into a frozen {Index}. Resolves each non-disabled entry
|
|
11
|
+
# through {GemResolver}, walks each resolved gem's `roots:` via {Walker.walk} under the configured
|
|
12
|
+
# `budget_per_gem` cap, and aggregates the per-gem method catalogs into the Index's flat
|
|
13
|
+
# `(class_name, method_name) → kind` table.
|
|
16
14
|
#
|
|
17
|
-
# Entries with `mode: :disabled` are skipped without
|
|
18
|
-
#
|
|
19
|
-
# gem in configuration without provoking a missing-gem
|
|
20
|
-
# diagnostic.
|
|
15
|
+
# Entries with `mode: :disabled` are skipped without resolution attempts so users can "list and disable"
|
|
16
|
+
# a gem in configuration without provoking a missing-gem diagnostic.
|
|
21
17
|
module Builder
|
|
22
18
|
module_function
|
|
23
19
|
|
|
@@ -36,12 +32,9 @@ module Rigor
|
|
|
36
32
|
state.to_index(dependencies)
|
|
37
33
|
end
|
|
38
34
|
|
|
39
|
-
# Per-build mutable accumulator. The original inline
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
# pushed the method past the AbcSize budget; the
|
|
43
|
-
# struct collects the same fields, narrows
|
|
44
|
-
# `absorb`'s branching, and yields one
|
|
35
|
+
# Per-build mutable accumulator. The original inline variables (`resolved` / `unresolvable` /
|
|
36
|
+
# `catalog` / `class_to_gem` / `budget_exceeded` / `gem_modes`) pushed the method past the AbcSize
|
|
37
|
+
# budget; the struct collects the same fields, narrows `absorb`'s branching, and yields one
|
|
45
38
|
# `Index.new(...)` call from `to_index`.
|
|
46
39
|
class BuildState
|
|
47
40
|
def initialize
|
|
@@ -82,26 +75,20 @@ module Rigor
|
|
|
82
75
|
end
|
|
83
76
|
end
|
|
84
77
|
|
|
85
|
-
# ADR-10 5b — per-class reverse-lookup table (β budget
|
|
86
|
-
#
|
|
87
|
-
# class
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
# The dispatcher only consults this map when the
|
|
91
|
-
# `budget_overrun_strategy` is `:dependency_silence`,
|
|
92
|
-
# so the storage cost is never paid back unless the
|
|
93
|
-
# user opts in.
|
|
78
|
+
# ADR-10 5b — per-class reverse-lookup table (β budget semantics). Records `class_name → gem_name` for
|
|
79
|
+
# every class observed in the gem's catalog. First-write-wins: if two opt-in gems re-open the same
|
|
80
|
+
# class, the first gem to harvest the class owns it in the reverse index. The dispatcher only consults
|
|
81
|
+
# this map when the `budget_overrun_strategy` is `:dependency_silence`, so the storage cost is never
|
|
82
|
+
# paid back unless the user opts in.
|
|
94
83
|
def record_class_to_gem(catalog, gem_name, class_to_gem)
|
|
95
84
|
catalog.each_key do |(class_name, _method_name)|
|
|
96
85
|
class_to_gem[class_name] ||= gem_name
|
|
97
86
|
end
|
|
98
87
|
end
|
|
99
88
|
|
|
100
|
-
# Per-resolved-gem walk. Isolated so a single gem's
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
# and a top-level raise here degrades the gem to "no
|
|
104
|
-
# contributions" without touching the rest of the run.
|
|
89
|
+
# Per-resolved-gem walk. Isolated so a single gem's filesystem error / parse failure cannot abort the
|
|
90
|
+
# build; the walker swallows its own per-file errors, and a top-level raise here degrades the gem to
|
|
91
|
+
# "no contributions" without touching the rest of the run.
|
|
105
92
|
def walker_outcome_for(resolved, budget)
|
|
106
93
|
Walker.walk(gem_dir: resolved.gem_dir, roots: resolved.roots, budget: budget)
|
|
107
94
|
rescue StandardError
|
|
@@ -3,34 +3,26 @@
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module Analysis
|
|
5
5
|
module DependencySourceInference
|
|
6
|
-
# Maps a `Configuration::Dependencies::Entry` to the gem's
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# frozen {Resolved} value object or an {Unresolvable} value
|
|
11
|
-
# describing why the gem cannot participate in this run.
|
|
6
|
+
# Maps a `Configuration::Dependencies::Entry` to the gem's on-disk installation directory by consulting
|
|
7
|
+
# RubyGems (`Gem.loaded_specs` first, falling back to `Gem::Specification.find_by_name`). Returns either
|
|
8
|
+
# a frozen {Resolved} value object or an {Unresolvable} value describing why the gem cannot participate
|
|
9
|
+
# in this run.
|
|
12
10
|
#
|
|
13
|
-
# Resolution failures are surfaced as
|
|
14
|
-
# `
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# cleanly to "no contributions from that gem" — every other
|
|
18
|
-
# gem and the project source remain unaffected.
|
|
11
|
+
# Resolution failures are surfaced as `dynamic.dependency-source.gem-not-found` diagnostics by
|
|
12
|
+
# {Analysis::Runner} rather than crashing the run, so a missing gem in `dependencies.source_inference`
|
|
13
|
+
# degrades cleanly to "no contributions from that gem" — every other gem and the project source remain
|
|
14
|
+
# unaffected.
|
|
19
15
|
module GemResolver
|
|
20
|
-
# Successful resolution. `version` is the spec version as
|
|
21
|
-
#
|
|
22
|
-
# (slice 3) without leaking a `Gem::Version` instance
|
|
23
|
-
# through public surfaces.
|
|
16
|
+
# Successful resolution. `version` is the spec version as a String so it round-trips into cache
|
|
17
|
+
# descriptors (slice 3) without leaking a `Gem::Version` instance through public surfaces.
|
|
24
18
|
class Resolved < Data.define(:gem_name, :version, :gem_dir, :mode, :roots)
|
|
25
19
|
def descriptor_key
|
|
26
20
|
[gem_name, version, mode].freeze
|
|
27
21
|
end
|
|
28
22
|
end
|
|
29
23
|
|
|
30
|
-
# Unresolvable reasons. `:not_in_bundle` covers both the
|
|
31
|
-
#
|
|
32
|
-
# `LoadError`-style raise from `find_by_name`. Future
|
|
33
|
-
# reasons (`:c_extension_only`, `:no_lib_root`) are
|
|
24
|
+
# Unresolvable reasons. `:not_in_bundle` covers both the "RubyGems doesn't know this gem" case and the
|
|
25
|
+
# `LoadError`-style raise from `find_by_name`. Future reasons (`:c_extension_only`, `:no_lib_root`) are
|
|
34
26
|
# introduced as the walker discovers them in slice 2b.
|
|
35
27
|
Unresolvable = Data.define(:gem_name, :reason)
|
|
36
28
|
|
|
@@ -53,12 +45,9 @@ module Rigor
|
|
|
53
45
|
)
|
|
54
46
|
end
|
|
55
47
|
|
|
56
|
-
# Locator. `Gem.loaded_specs` reflects the bundle (cheap
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
# not yet `require`'d. `Gem::MissingSpecError` is a
|
|
60
|
-
# `LoadError` subclass, so the rescue covers both
|
|
61
|
-
# missing-spec and load-error signals.
|
|
48
|
+
# Locator. `Gem.loaded_specs` reflects the bundle (cheap lookup, no filesystem walk); `find_by_name` is
|
|
49
|
+
# the broader fallback for gems present on the gem path but not yet `require`'d. `Gem::MissingSpecError`
|
|
50
|
+
# is a `LoadError` subclass, so the rescue covers both missing-spec and load-error signals.
|
|
62
51
|
def locate_gem_spec(name)
|
|
63
52
|
Gem.loaded_specs[name] || begin
|
|
64
53
|
Gem::Specification.find_by_name(name)
|
|
@@ -5,45 +5,31 @@ require_relative "../../cache/descriptor"
|
|
|
5
5
|
module Rigor
|
|
6
6
|
module Analysis
|
|
7
7
|
module DependencySourceInference
|
|
8
|
-
# Per-run collection of gem-source-inference state. Holds
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# The method table is fully populated by {Walker} at build
|
|
13
|
-
# time; {#contribution_for} returns live entries.
|
|
8
|
+
# Per-run collection of gem-source-inference state. Holds the resolved gems the walker visits plus the
|
|
9
|
+
# unresolvable entries the runner surfaces as `dynamic.dependency-source.gem-not-found` diagnostics.
|
|
10
|
+
# The method table is fully populated by {Walker} at build time; {#contribution_for} returns live
|
|
11
|
+
# entries.
|
|
14
12
|
class Index
|
|
15
13
|
attr_reader :resolved_gems, :unresolvable, :method_catalog, :budget_exceeded,
|
|
16
14
|
:class_to_gem, :budget_overrun_strategy, :gem_modes
|
|
17
15
|
|
|
18
|
-
# @param method_catalog [Hash{[String, Symbol] => Symbol}]
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
# `
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
# budget-exceeded gem's classes degrade to
|
|
36
|
-
# `Dynamic[top]` instead of falling through to the
|
|
37
|
-
# user-class fallback.
|
|
38
|
-
# @param gem_modes [Hash<String, Symbol>] per-gem mode
|
|
39
|
-
# table (`gem_name → :disabled | :when_missing |
|
|
40
|
-
# :full`). ADR-10 slice 5c consults this through
|
|
41
|
-
# {#mode_for} to identify call sites where gem-source
|
|
42
|
-
# and RBS both contribute under `mode: :full`. The map
|
|
43
|
-
# is keyed on `gem_name` (not class) because re-opened
|
|
44
|
-
# classes belong to the first gem they appeared in
|
|
45
|
-
# per `class_to_gem`; `mode_for(class_name)` chains
|
|
46
|
-
# the two lookups.
|
|
16
|
+
# @param method_catalog [Hash{[String, Symbol] => Symbol}] the flat
|
|
17
|
+
# `(class_name, method_name) → :instance | :singleton` table produced by {Walker.walk}, aggregated
|
|
18
|
+
# across every resolved gem in the run. The Index itself stays gem-agnostic — the per-gem
|
|
19
|
+
# attribution that slice 3's cache descriptor needs lives on `Resolved`, not here.
|
|
20
|
+
# @param budget_exceeded [Array<String>] gem names whose {Walker} run hit the per-gem catalog cap
|
|
21
|
+
# (slice 4). The Runner consumes this list to emit one `dynamic.dependency-source.budget-exceeded`
|
|
22
|
+
# warning per gem.
|
|
23
|
+
# @param class_to_gem [Hash<String, String>] reverse lookup `class_name → gem_name` (slice 5b). Built
|
|
24
|
+
# first-write-wins: when two opt-in gems re-open the same class, the first gem owns it. The
|
|
25
|
+
# dispatcher consults this map under the `:dependency_silence` budget overrun strategy so call
|
|
26
|
+
# sites on a budget-exceeded gem's classes degrade to `Dynamic[top]` instead of falling through to
|
|
27
|
+
# the user-class fallback.
|
|
28
|
+
# @param gem_modes [Hash<String, Symbol>] per-gem mode table (`gem_name → :disabled | :when_missing |
|
|
29
|
+
# :full`). ADR-10 slice 5c consults this through {#mode_for} to identify call sites where
|
|
30
|
+
# gem-source and RBS both contribute under `mode: :full`. The map is keyed on `gem_name` (not
|
|
31
|
+
# class) because re-opened classes belong to the first gem they appeared in per `class_to_gem`;
|
|
32
|
+
# `mode_for(class_name)` chains the two lookups.
|
|
47
33
|
def initialize(
|
|
48
34
|
resolved_gems: [], unresolvable: [], method_catalog: {},
|
|
49
35
|
budget_exceeded: [], class_to_gem: {},
|
|
@@ -59,12 +45,10 @@ module Rigor
|
|
|
59
45
|
freeze
|
|
60
46
|
end
|
|
61
47
|
|
|
62
|
-
# Accepts both the post-heuristic `CatalogEntry` shape
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
# `CatalogEntry(kind: value, return_type: nil)` so the
|
|
67
|
-
# downstream dispatcher always sees a single shape.
|
|
48
|
+
# Accepts both the post-heuristic `CatalogEntry` shape (the Walker's current output) and the legacy
|
|
49
|
+
# bare-Symbol `:instance` / `:singleton` shape (older tests + earlier in-tree callers). Symbol values
|
|
50
|
+
# are wrapped into `CatalogEntry(kind: value, return_type: nil)` so the downstream dispatcher always
|
|
51
|
+
# sees a single shape.
|
|
68
52
|
def normalize_catalog(catalog)
|
|
69
53
|
catalog.transform_values do |value|
|
|
70
54
|
value.is_a?(Symbol) ? Walker::CatalogEntry.new(kind: value) : value
|
|
@@ -72,16 +56,14 @@ module Rigor
|
|
|
72
56
|
end
|
|
73
57
|
private :normalize_catalog
|
|
74
58
|
|
|
75
|
-
# @return [String, nil] the gem that owns `class_name`
|
|
76
|
-
#
|
|
77
|
-
# any opt-in gem's catalog.
|
|
59
|
+
# @return [String, nil] the gem that owns `class_name` (first-write-wins); `nil` when the class isn't
|
|
60
|
+
# in any opt-in gem's catalog.
|
|
78
61
|
def gem_for(class_name)
|
|
79
62
|
@class_to_gem[class_name]
|
|
80
63
|
end
|
|
81
64
|
|
|
82
|
-
# ADR-10 slice 5c — per-class mode lookup. Chains
|
|
83
|
-
#
|
|
84
|
-
# class isn't owned by any opt-in gem in this run.
|
|
65
|
+
# ADR-10 slice 5c — per-class mode lookup. Chains `class_to_gem` + `gem_modes`; returns `nil` when
|
|
66
|
+
# the class isn't owned by any opt-in gem in this run.
|
|
85
67
|
def mode_for(class_name)
|
|
86
68
|
gem_name = @class_to_gem[class_name]
|
|
87
69
|
return nil if gem_name.nil?
|
|
@@ -89,22 +71,17 @@ module Rigor
|
|
|
89
71
|
@gem_modes[gem_name]
|
|
90
72
|
end
|
|
91
73
|
|
|
92
|
-
# ADR-10 slice 5c — true when the receiver class belongs
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
# (RBS / plugin) dispatch resolves so it can record the
|
|
96
|
-
# boundary-crossing for audit.
|
|
74
|
+
# ADR-10 slice 5c — true when the receiver class belongs to a gem the user opted into `mode: :full`
|
|
75
|
+
# for. The dispatcher consults this AFTER an authoritative-source (RBS / plugin) dispatch resolves
|
|
76
|
+
# so it can record the boundary-crossing for audit.
|
|
97
77
|
def full_mode?(class_name)
|
|
98
78
|
mode_for(class_name) == :full
|
|
99
79
|
end
|
|
100
80
|
|
|
101
|
-
# Looks up the recorded method kind for a
|
|
102
|
-
# `
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
# otherwise. Slice 2b-ii enriches this with the inferred
|
|
106
|
-
# return type so the dispatcher tier can build a
|
|
107
|
-
# `Type::Dynamic` directly from the lookup result.
|
|
81
|
+
# Looks up the recorded method kind for a `(class_name, method_name)` pair. Returns `:instance` /
|
|
82
|
+
# `:singleton` when the walker observed a definition under one of the resolved gems' `roots:`, or
|
|
83
|
+
# `nil` otherwise. Slice 2b-ii enriches this with the inferred return type so the dispatcher tier can
|
|
84
|
+
# build a `Type::Dynamic` directly from the lookup result.
|
|
108
85
|
def contribution_for(class_name:, method_name:)
|
|
109
86
|
@method_catalog[[class_name, method_name]]
|
|
110
87
|
end
|
|
@@ -113,20 +90,14 @@ module Rigor
|
|
|
113
90
|
@resolved_gems.empty?
|
|
114
91
|
end
|
|
115
92
|
|
|
116
|
-
# Builds a frozen `Cache::Descriptor` carrying one
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
# file-digest) descriptors so a `bundle update` on a
|
|
121
|
-
# listed gem invalidates exactly that gem's slice while
|
|
122
|
-
# leaving the rest of the cache hot.
|
|
93
|
+
# Builds a frozen `Cache::Descriptor` carrying one `DependencyEntry` row per resolved gem in this
|
|
94
|
+
# run. Cache producers that observe ADR-10 inference outputs compose this descriptor with their own
|
|
95
|
+
# (RBS, plugin, file-digest) descriptors so a `bundle update` on a listed gem invalidates exactly
|
|
96
|
+
# that gem's slice while leaving the rest of the cache hot.
|
|
123
97
|
#
|
|
124
|
-
# Unresolvable entries contribute nothing — there is no
|
|
125
|
-
#
|
|
126
|
-
#
|
|
127
|
-
# diagnostics. Resolved-but-disabled entries are also
|
|
128
|
-
# absent: the {Builder} skips them before resolution, so
|
|
129
|
-
# they never reach the index.
|
|
98
|
+
# Unresolvable entries contribute nothing — there is no version to key on, and the runner already
|
|
99
|
+
# surfaces them as `dynamic.dependency-source.gem-not-found` diagnostics. Resolved-but-disabled
|
|
100
|
+
# entries are also absent: the {Builder} skips them before resolution, so they never reach the index.
|
|
130
101
|
def cache_descriptor
|
|
131
102
|
dependencies = @resolved_gems.map do |resolved|
|
|
132
103
|
Cache::Descriptor::DependencyEntry.new(
|
|
@@ -139,10 +110,8 @@ module Rigor
|
|
|
139
110
|
end
|
|
140
111
|
end
|
|
141
112
|
|
|
142
|
-
# Frozen empty index — the runner uses this when
|
|
143
|
-
#
|
|
144
|
-
# so the dispatcher tier holds a stable, non-nil
|
|
145
|
-
# reference even on default configurations.
|
|
113
|
+
# Frozen empty index — the runner uses this when `Configuration#dependencies.source_inference` is
|
|
114
|
+
# empty so the dispatcher tier holds a stable, non-nil reference even on default configurations.
|
|
146
115
|
Index::EMPTY = Index.new.freeze
|
|
147
116
|
end
|
|
148
117
|
end
|
|
@@ -7,42 +7,30 @@ require_relative "../../type"
|
|
|
7
7
|
module Rigor
|
|
8
8
|
module Analysis
|
|
9
9
|
module DependencySourceInference
|
|
10
|
-
# Walker enhancement (ADR-10 § "Open questions"): pulls a
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
# the
|
|
14
|
-
# else returns `nil`, which the dispatcher consumes as
|
|
15
|
-
# "fall back to `Dynamic[top]`" (the pre-enhancement
|
|
16
|
-
# behaviour).
|
|
10
|
+
# Walker enhancement (ADR-10 § "Open questions"): pulls a **heuristic** return type out of a method
|
|
11
|
+
# body's tail expression. The heuristic is intentionally narrow — only the trivially-decidable shapes
|
|
12
|
+
# contribute. Everything else returns `nil`, which the dispatcher consumes as "fall back to
|
|
13
|
+
# `Dynamic[top]`" (the pre-enhancement behaviour).
|
|
17
14
|
#
|
|
18
15
|
# The contract is a strict floor:
|
|
19
16
|
#
|
|
20
|
-
# - Last statement is a literal scalar (Integer / Float /
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
# `Nominal[Array]` / `Nominal[Hash]` (element-type
|
|
28
|
-
# inference stays deferred — too expensive for the
|
|
29
|
-
# heuristic tier).
|
|
30
|
-
# - Last statement is `self` → `nil` (the caller's receiver
|
|
31
|
-
# nominal would be more accurate but the walker doesn't
|
|
32
|
-
# carry the receiver context; deferred).
|
|
17
|
+
# - Last statement is a literal scalar (Integer / Float / Symbol / true / false / nil) → `Constant<value>`.
|
|
18
|
+
# - Last statement is a String literal → `Nominal[String]` (NOT `Constant<"x">`; a String literal is
|
|
19
|
+
# mutable under `# frozen_string_literal: false`, so the analyzer cannot claim object identity).
|
|
20
|
+
# - Last statement is an Array / Hash literal → `Nominal[Array]` / `Nominal[Hash]` (element-type
|
|
21
|
+
# inference stays deferred — too expensive for the heuristic tier).
|
|
22
|
+
# - Last statement is `self` → `nil` (the caller's receiver nominal would be more accurate but the
|
|
23
|
+
# walker doesn't carry the receiver context; deferred).
|
|
33
24
|
# - Anything else → `nil`.
|
|
34
25
|
#
|
|
35
|
-
# The dispatcher wraps the returned type in `Dynamic[T]`
|
|
36
|
-
#
|
|
37
|
-
# contract. The wrapping is the dispatcher's responsibility,
|
|
38
|
-
# not the heuristic's.
|
|
26
|
+
# The dispatcher wraps the returned type in `Dynamic[T]` before returning to the user per ADR-10's
|
|
27
|
+
# `Dynamic`-origin contract. The wrapping is the dispatcher's responsibility, not the heuristic's.
|
|
39
28
|
module ReturnTypeHeuristic
|
|
40
29
|
module_function
|
|
41
30
|
|
|
42
31
|
# @param def_node [Prism::DefNode]
|
|
43
|
-
# @return [Rigor::Type, nil] heuristic return type, or
|
|
44
|
-
#
|
|
45
|
-
# any of the recognised shapes.
|
|
32
|
+
# @return [Rigor::Type, nil] heuristic return type, or `nil` when the body's tail expression doesn't
|
|
33
|
+
# match any of the recognised shapes.
|
|
46
34
|
def extract(def_node)
|
|
47
35
|
body = def_node.body
|
|
48
36
|
return nil if body.nil?
|
|
@@ -51,11 +39,9 @@ module Rigor
|
|
|
51
39
|
literal_return_type(tail)
|
|
52
40
|
end
|
|
53
41
|
|
|
54
|
-
# Extracts the last evaluated expression of the method
|
|
55
|
-
#
|
|
56
|
-
# `
|
|
57
|
-
# expression node directly. We dig past `BeginNode` /
|
|
58
|
-
# rescue wrappers to the protected body's tail.
|
|
42
|
+
# Extracts the last evaluated expression of the method body. A `Prism::DefNode`'s body is either a
|
|
43
|
+
# `StatementsNode` (multi-statement body) or a single expression node directly. We dig past
|
|
44
|
+
# `BeginNode` / rescue wrappers to the protected body's tail.
|
|
59
45
|
def tail_expression(node)
|
|
60
46
|
case node
|
|
61
47
|
when Prism::StatementsNode
|
|
@@ -70,11 +56,9 @@ module Rigor
|
|
|
70
56
|
end
|
|
71
57
|
private_class_method :tail_expression
|
|
72
58
|
|
|
73
|
-
# The per-shape heuristic. Per the module docstring,
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
# to the appropriate Nominal; everything else returns
|
|
77
|
-
# nil.
|
|
59
|
+
# The per-shape heuristic. Per the module docstring, immutable scalar literals fold to `Constant<value>`;
|
|
60
|
+
# mutable container literals (String, Array, Hash) fold to the appropriate Nominal; everything else
|
|
61
|
+
# returns nil.
|
|
78
62
|
def literal_return_type(node)
|
|
79
63
|
case node
|
|
80
64
|
when Prism::IntegerNode, Prism::FloatNode then Type::Combinator.constant_of(node.value)
|
|
@@ -89,9 +73,8 @@ module Rigor
|
|
|
89
73
|
end
|
|
90
74
|
private_class_method :literal_return_type
|
|
91
75
|
|
|
92
|
-
# `Prism::SymbolNode#value` returns the Symbol's name as
|
|
93
|
-
#
|
|
94
|
-
# `:foo` is `Constant<:foo>`, not `Constant<"foo">`.
|
|
76
|
+
# `Prism::SymbolNode#value` returns the Symbol's name as a String. We `.to_sym` it for the Constant
|
|
77
|
+
# carrier so `:foo` is `Constant<:foo>`, not `Constant<"foo">`.
|
|
95
78
|
def symbol_constant(node)
|
|
96
79
|
value = node.value
|
|
97
80
|
return nil if value.nil?
|