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,58 +6,49 @@ require_relative "../source/constant_path"
|
|
|
6
6
|
|
|
7
7
|
module Rigor
|
|
8
8
|
module Inference
|
|
9
|
-
# ADR-48 Struct follow-up, slice 3 — the fold-safe-local scan. Determines,
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
# off a *stored* binding (relaxing the slice-2 fresh-receiver gate).
|
|
9
|
+
# ADR-48 Struct follow-up, slice 3 — the fold-safe-local scan. Determines, for a single local-variable scope (a
|
|
10
|
+
# method body or the program top-level), which `Struct`-materialised locals are **provably never mutated, aliased,
|
|
11
|
+
# or escaped** and so may have their member reads folded off a *stored* binding (relaxing the slice-2 fresh-receiver
|
|
12
|
+
# gate).
|
|
14
13
|
#
|
|
15
|
-
# The analysis is a conservative ALLOW-LIST, not a deny-list: a local is
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
# internally) — disqualifies the local. A missed case therefore makes the
|
|
21
|
-
# scan over-conservative (no fold), **never unsound** (folding a mutated
|
|
22
|
-
# value) — the false-positive-safe direction.
|
|
14
|
+
# The analysis is a conservative ALLOW-LIST, not a deny-list: a local is fold-safe only when *every* read of it is
|
|
15
|
+
# the receiver of a known-pure read call. Anything the scan does not recognise as a pure read — a setter, an `[]=` /
|
|
16
|
+
# operator-write, an argument / alias / container store / return (escape), or an unknown method call (which could
|
|
17
|
+
# mutate `self` internally) — disqualifies the local. A missed case therefore makes the scan over-conservative (no
|
|
18
|
+
# fold), **never unsound** (folding a mutated value) — the false-positive-safe direction.
|
|
23
19
|
#
|
|
24
|
-
# Soundness rests on a counting identity: a local `n` is fold-safe iff
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
# under a pure call), a call argument, an assignment RHS (alias), a
|
|
30
|
-
# container element, a bare value (return/escape) — leaves a read that is
|
|
31
|
-
# not a pure-receiver read, so the counts diverge.
|
|
20
|
+
# Soundness rests on a counting identity: a local `n` is fold-safe iff *every* `LocalVariableReadNode(n)` is the
|
|
21
|
+
# receiver of a pure-read call. Equivalently `total_reads(n) == pure_receiver_reads(n)`. Any other occurrence — `n`
|
|
22
|
+
# as a setter receiver (`n.x = v` is a `:x=` call, not a pure read), an `[]=`/operator-write receiver (the receiver
|
|
23
|
+
# read is not under a pure call), a call argument, an assignment RHS (alias), a container element, a bare value
|
|
24
|
+
# (return/escape) — leaves a read that is not a pure-receiver read, so the counts diverge.
|
|
32
25
|
#
|
|
33
|
-
# See docs/notes/20260615-struct-folding-slice3-design.md and
|
|
34
|
-
#
|
|
26
|
+
# See docs/notes/20260615-struct-folding-slice3-design.md and docs/adr/48-data-struct-value-folding.md § "Struct
|
|
27
|
+
# follow-up".
|
|
35
28
|
module StructFoldSafety
|
|
36
29
|
module_function
|
|
37
30
|
|
|
38
31
|
EMPTY = Set.new.freeze
|
|
39
32
|
|
|
40
|
-
# The fixed `Struct` read methods that never mutate. A member-reader name
|
|
41
|
-
# (`:x
|
|
42
|
-
# (`:x=`), `:[]=`, `store`, `push`, etc. are deliberately absent.
|
|
33
|
+
# The fixed `Struct` read methods that never mutate. A member-reader name (`:x`) is added per-local from the
|
|
34
|
+
# local's recorded layout. A setter (`:x=`), `:[]=`, `store`, `push`, etc. are deliberately absent.
|
|
43
35
|
FIXED_READS = %i[
|
|
44
36
|
[] dig to_h to_hash to_a values members deconstruct deconstruct_keys
|
|
45
37
|
== != eql? equal? hash inspect to_s size length frozen? each each_pair
|
|
46
38
|
values_at with
|
|
47
39
|
].to_set.freeze
|
|
48
40
|
|
|
49
|
-
# Nested `def` / `class` / `module` bodies open a *new* local-variable
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
# (closures), so the scan does descend into them.
|
|
41
|
+
# Nested `def` / `class` / `module` bodies open a *new* local-variable scope, so the scan does not descend into
|
|
42
|
+
# them — a local of the same name there is a different binding. Blocks share the enclosing locals (closures), so
|
|
43
|
+
# the scan does descend into them.
|
|
53
44
|
def scope_boundary?(node)
|
|
54
45
|
node.is_a?(Prism::DefNode) || node.is_a?(Prism::ClassNode) ||
|
|
55
46
|
node.is_a?(Prism::ModuleNode) || node.is_a?(Prism::SingletonClassNode)
|
|
56
47
|
end
|
|
57
48
|
|
|
58
49
|
# @param root [Prism::Node, nil] the local-variable scope to scan.
|
|
59
|
-
# @param layout_lookup [#call] a `String -> Array[Symbol] | nil` resolver
|
|
60
|
-
#
|
|
50
|
+
# @param layout_lookup [#call] a `String -> Array[Symbol] | nil` resolver mapping a constant receiver name to its
|
|
51
|
+
# struct member list.
|
|
61
52
|
# @return [Set<Symbol>] the fold-safe local names.
|
|
62
53
|
def fold_safe_locals(root, layout_lookup)
|
|
63
54
|
return EMPTY if root.nil?
|
|
@@ -77,9 +68,8 @@ module Rigor
|
|
|
77
68
|
safe.empty? ? EMPTY : safe.to_set
|
|
78
69
|
end
|
|
79
70
|
|
|
80
|
-
# Pass 1 — record each local's single struct materialisation (its member
|
|
81
|
-
#
|
|
82
|
-
# later excluded (the static fold-safe set cannot track a rebinding).
|
|
71
|
+
# Pass 1 — record each local's single struct materialisation (its member set) and count its assignments. A local
|
|
72
|
+
# assigned more than once is later excluded (the static fold-safe set cannot track a rebinding).
|
|
83
73
|
def collect_struct_locals(node, layout_lookup, members, writes)
|
|
84
74
|
return if node.nil?
|
|
85
75
|
|
|
@@ -94,8 +84,7 @@ module Rigor
|
|
|
94
84
|
end
|
|
95
85
|
end
|
|
96
86
|
|
|
97
|
-
# Pass 2 — count, per recorded struct local, total reads vs. reads that
|
|
98
|
-
# are the receiver of a pure-read call.
|
|
87
|
+
# Pass 2 — count, per recorded struct local, total reads vs. reads that are the receiver of a pure-read call.
|
|
99
88
|
def count_uses(node, members, total, pure)
|
|
100
89
|
return if node.nil?
|
|
101
90
|
|
|
@@ -114,17 +103,15 @@ module Rigor
|
|
|
114
103
|
end
|
|
115
104
|
end
|
|
116
105
|
|
|
117
|
-
# A call is a pure read of the receiver when its name is a fixed Struct
|
|
118
|
-
#
|
|
119
|
-
# and any unknown method are excluded.
|
|
106
|
+
# A call is a pure read of the receiver when its name is a fixed Struct read or one of the receiver's member
|
|
107
|
+
# readers. Setters (`:x=`), `:[]=`, and any unknown method are excluded.
|
|
120
108
|
def pure_read_call?(call_node, member_set)
|
|
121
109
|
name = call_node.name
|
|
122
110
|
FIXED_READS.include?(name) || member_set.include?(name)
|
|
123
111
|
end
|
|
124
112
|
|
|
125
|
-
# The member set of a `<Struct chain>.new(...)` / `.[]` materialisation,
|
|
126
|
-
#
|
|
127
|
-
# `Const.new(...)` form (resolved through the layout side-table).
|
|
113
|
+
# The member set of a `<Struct chain>.new(...)` / `.[]` materialisation, or nil. Handles the inline
|
|
114
|
+
# `Struct.new(:a, :b).new(...)` form and the `Const.new(...)` form (resolved through the layout side-table).
|
|
128
115
|
def struct_materialization_members(value_node, layout_lookup)
|
|
129
116
|
return nil unless value_node.is_a?(Prism::CallNode)
|
|
130
117
|
return nil unless %i[new []].include?(value_node.name)
|
|
@@ -140,9 +127,8 @@ module Rigor
|
|
|
140
127
|
end
|
|
141
128
|
end
|
|
142
129
|
|
|
143
|
-
# The Symbol member set of a literal `Struct.new(:a, :b [, keyword_init:])`
|
|
144
|
-
#
|
|
145
|
-
# ignored — only the literal-Symbol positionals contribute.)
|
|
130
|
+
# The Symbol member set of a literal `Struct.new(:a, :b [, keyword_init:])` call, or nil. (A leading String name
|
|
131
|
+
# and the trailing options hash are ignored — only the literal-Symbol positionals contribute.)
|
|
146
132
|
def struct_new_member_set(call_node)
|
|
147
133
|
return nil unless call_node.is_a?(Prism::CallNode) && call_node.name == :new
|
|
148
134
|
return nil unless meta_constant?(call_node.receiver, :Struct)
|
|
@@ -167,8 +153,8 @@ module Rigor
|
|
|
167
153
|
end
|
|
168
154
|
end
|
|
169
155
|
|
|
170
|
-
# Yields each child to recurse into, skipping the subtree of a nested
|
|
171
|
-
#
|
|
156
|
+
# Yields each child to recurse into, skipping the subtree of a nested local-variable-scope boundary (a `def` /
|
|
157
|
+
# `class` / `module`).
|
|
172
158
|
def each_local_scope_child(node)
|
|
173
159
|
node.compact_child_nodes.each do |child|
|
|
174
160
|
next if scope_boundary?(child)
|
|
@@ -2,24 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module Inference
|
|
5
|
-
# ADR-16 Tier C output — one synthetic method declared by a
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# the template name. Stored in {SyntheticMethodIndex} and
|
|
9
|
-
# consulted by {MethodDispatcher} below the RBS dispatch tier.
|
|
5
|
+
# ADR-16 Tier C output — one synthetic method declared by a plugin's `Plugin::Macro::HeredocTemplate` entry, after
|
|
6
|
+
# the pre-pass has interpolated the call-site literal symbol into the template name. Stored in
|
|
7
|
+
# {SyntheticMethodIndex} and consulted by {MethodDispatcher} below the RBS dispatch tier.
|
|
10
8
|
#
|
|
11
|
-
# Per ADR-16 § WD13 (cost-bounded best-effort): method names
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
# `return_type` string is preserved so promotion can resolve
|
|
16
|
-
# it without re-walking; `"untyped"` / `"void"` placeholders
|
|
17
|
-
# fall back to `Dynamic[Top]`.
|
|
9
|
+
# Per ADR-16 § WD13 (cost-bounded best-effort): method names emit with return types promoted by the dispatcher's
|
|
10
|
+
# Slice 6 tiers — Slice 6a promotes via `origin_module` (TierB) and Slice 6b via `return_type` nominal lookup
|
|
11
|
+
# (TierC). The `return_type` string is preserved so promotion can resolve it without re-walking; `"untyped"` /
|
|
12
|
+
# `"void"` placeholders fall back to `Dynamic[Top]`.
|
|
18
13
|
#
|
|
19
|
-
# The `provenance` Hash carries debug / `--explain` metadata:
|
|
20
|
-
#
|
|
21
|
-
# location of the originating DSL call. Surfaced through the
|
|
22
|
-
# dispatcher's `macro.tier_c.*` provenance markers.
|
|
14
|
+
# The `provenance` Hash carries debug / `--explain` metadata: plugin id, the template's call shape, and the source
|
|
15
|
+
# location of the originating DSL call. Surfaced through the dispatcher's `macro.tier_c.*` provenance markers.
|
|
23
16
|
class SyntheticMethod
|
|
24
17
|
INSTANCE = :instance
|
|
25
18
|
SINGLETON = :singleton
|
|
@@ -4,43 +4,30 @@ require_relative "synthetic_method"
|
|
|
4
4
|
|
|
5
5
|
module Rigor
|
|
6
6
|
module Inference
|
|
7
|
-
# Frozen, Ractor-shareable lookup table for the synthetic
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# scanner (see {SyntheticMethodScanner}) and consulted by
|
|
11
|
-
# {MethodDispatcher} below `RbsDispatch.try_dispatch` (per WD13:
|
|
12
|
-
# user-authored RBS overrides substrate synthesis).
|
|
7
|
+
# Frozen, Ractor-shareable lookup table for the synthetic methods emitted by ADR-16 Tier C declarations during a
|
|
8
|
+
# single `Analysis::Runner#run`. Constructed by the pre-pass scanner (see {SyntheticMethodScanner}) and consulted by
|
|
9
|
+
# {MethodDispatcher} below `RbsDispatch.try_dispatch` (per WD13: user-authored RBS overrides substrate synthesis).
|
|
13
10
|
#
|
|
14
|
-
# The index is keyed by `(class_name, method_name, kind)`. A
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
# the same attribute). Per ADR-16 WD11 / the WD-discussion in
|
|
19
|
-
# `## Open questions` the dispatcher uses first-wins by
|
|
20
|
-
# registration order; this index preserves that order in
|
|
21
|
-
# `lookup`'s return.
|
|
11
|
+
# The index is keyed by `(class_name, method_name, kind)`. A single key may resolve to multiple {SyntheticMethod}
|
|
12
|
+
# records if two plugins emit the same name (e.g. `rigor-dry-struct` and a hypothetical `rigor-dry-struct-extras`
|
|
13
|
+
# both registering the same attribute). Per ADR-16 WD11 / the WD-discussion in `## Open questions` the dispatcher
|
|
14
|
+
# uses first-wins by registration order; this index preserves that order in `lookup`'s return.
|
|
22
15
|
#
|
|
23
16
|
# ## Slice 2b — return-type precision posture
|
|
24
17
|
#
|
|
25
|
-
# The recorded `SyntheticMethod#return_type` is a String
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
# floor — the recorded string is the input to a later slice's
|
|
30
|
-
# precision promotion via ADR-13's `Plugin::TypeNodeResolver`.
|
|
18
|
+
# The recorded `SyntheticMethod#return_type` is a String (e.g. `"ActiveStorage::Attached::One"`), preserved verbatim
|
|
19
|
+
# from the manifest's emit table. Slice 2b's engine wiring treats every match as returning `Dynamic[T]` per WD13's
|
|
20
|
+
# floor — the recorded string is the input to a later slice's precision promotion via ADR-13's
|
|
21
|
+
# `Plugin::TypeNodeResolver`.
|
|
31
22
|
class SyntheticMethodIndex
|
|
32
23
|
attr_reader :entries, :class_names
|
|
33
24
|
|
|
34
25
|
# @param entries [Array<SyntheticMethod>]
|
|
35
|
-
# @param class_names [Array<String>, Set<String>] names of
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
# (their constant reference + `.new` dispatch) even though
|
|
41
|
-
# nothing else in the type universe declares them. Tier B/C
|
|
42
|
-
# method emissions leave this empty (their receiver classes
|
|
43
|
-
# are already real).
|
|
26
|
+
# @param class_names [Array<String>, Set<String>] names of classes the substrate synthesises wholesale (ADR-36
|
|
27
|
+
# nested-class emission — the variant subclasses that have no RBS/source declaration of their own). Recorded so
|
|
28
|
+
# `Environment#class_known?` can resolve them as classes (their constant reference + `.new` dispatch) even
|
|
29
|
+
# though nothing else in the type universe declares them. Tier B/C method emissions leave this empty (their
|
|
30
|
+
# receiver classes are already real).
|
|
44
31
|
def initialize(entries: [], class_names: [])
|
|
45
32
|
unless entries.is_a?(Array) && entries.all?(SyntheticMethod)
|
|
46
33
|
raise ArgumentError,
|
|
@@ -59,16 +46,14 @@ module Rigor
|
|
|
59
46
|
entries.empty? && class_names.empty?
|
|
60
47
|
end
|
|
61
48
|
|
|
62
|
-
# True when `name` is a substrate-synthesised class (an
|
|
63
|
-
#
|
|
64
|
-
# so the constant resolves and `.new` dispatches.
|
|
49
|
+
# True when `name` is a substrate-synthesised class (an ADR-36 variant subclass). Used by
|
|
50
|
+
# `Environment#class_known?` so the constant resolves and `.new` dispatches.
|
|
65
51
|
def knows_class?(name)
|
|
66
52
|
@class_name_set.include?(name.to_s)
|
|
67
53
|
end
|
|
68
54
|
|
|
69
|
-
# Returns an Array of matching {SyntheticMethod} records in
|
|
70
|
-
#
|
|
71
|
-
# declared a Tier C entry that interpolates to this name.
|
|
55
|
+
# Returns an Array of matching {SyntheticMethod} records in plugin-registration order. Empty Array when no plugin
|
|
56
|
+
# has declared a Tier C entry that interpolates to this name.
|
|
72
57
|
def lookup_instance(class_name, method_name)
|
|
73
58
|
@by_instance.fetch([class_name, method_name.to_sym], EMPTY_ROW)
|
|
74
59
|
end
|
|
@@ -10,13 +10,10 @@ require_relative "synthetic_method_index"
|
|
|
10
10
|
|
|
11
11
|
module Rigor
|
|
12
12
|
module Inference
|
|
13
|
-
# ADR-16 slice 2b pre-pass — scans the project's source paths
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# {SyntheticMethodIndex} the dispatcher consults below the RBS
|
|
18
|
-
# tier (per WD13 — user-authored RBS overrides substrate
|
|
19
|
-
# synthesis).
|
|
13
|
+
# ADR-16 slice 2b pre-pass — scans the project's source paths for class-level DSL calls that match any registered
|
|
14
|
+
# plugin's `Plugin::Macro::HeredocTemplate` entry, instantiates the corresponding {SyntheticMethod} records, and
|
|
15
|
+
# returns a frozen {SyntheticMethodIndex} the dispatcher consults below the RBS tier (per WD13 — user-authored RBS
|
|
16
|
+
# overrides substrate synthesis).
|
|
20
17
|
#
|
|
21
18
|
# Two-phase walk:
|
|
22
19
|
#
|
|
@@ -32,36 +29,31 @@ module Rigor
|
|
|
32
29
|
# class must equal or inherit (lexically OR through the
|
|
33
30
|
# RBS env) from the template's `receiver_constraint`.
|
|
34
31
|
#
|
|
35
|
-
# Per WD4 the pre-pass mechanism is "scan all files once at
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
# to lazy emit (per WD4 alternatives) if the warm-cache profile
|
|
39
|
-
# justifies it.
|
|
32
|
+
# Per WD4 the pre-pass mechanism is "scan all files once at startup, populate the index before per-file inference."
|
|
33
|
+
# Slice 2b ships this strategy; future iterations may revisit to lazy emit (per WD4 alternatives) if the warm-cache
|
|
34
|
+
# profile justifies it.
|
|
40
35
|
#
|
|
41
|
-
# Per WD13 floor — `return_type` is recorded but not resolved.
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
# dispatcher's slice-2b tier translates every match to
|
|
45
|
-
# `Dynamic[T]`. Precise resolution via the ADR-13 resolver
|
|
46
|
-
# chain is the ceiling, deferred.
|
|
36
|
+
# Per WD13 floor — `return_type` is recorded but not resolved. `Macro::HeredocTemplate::Emit#returns` strings
|
|
37
|
+
# round-trip through {SyntheticMethod#return_type} verbatim; the dispatcher's slice-2b tier translates every match
|
|
38
|
+
# to `Dynamic[T]`. Precise resolution via the ADR-13 resolver chain is the ceiling, deferred.
|
|
47
39
|
module SyntheticMethodScanner # rubocop:disable Metrics/ModuleLength
|
|
48
40
|
module_function
|
|
49
41
|
|
|
50
42
|
# @param plugin_registry [Rigor::Plugin::Registry]
|
|
51
|
-
# @param paths
|
|
43
|
+
# @param paths [Array<String>] absolute paths to the project
|
|
52
44
|
# source files to scan.
|
|
53
|
-
# @param environment
|
|
45
|
+
# @param environment [Rigor::Environment, nil] used for
|
|
54
46
|
# inheritance resolution against RBS-known classes
|
|
55
47
|
# (ActiveRecord::Base, Dry::Struct, etc.) that aren't
|
|
56
48
|
# declared in project source.
|
|
57
|
-
# @param fact_store
|
|
49
|
+
# @param fact_store [Rigor::Plugin::FactStore, nil]
|
|
58
50
|
# the per-run cross-plugin fact store. ADR-18 lookups
|
|
59
51
|
# (`Plugin::Macro::HeredocTemplate::Emit#returns_from_arg`)
|
|
60
52
|
# consult this at scan time to resolve per-call-site
|
|
61
53
|
# return types from published facts; without it, those
|
|
62
54
|
# emit rows fall back to their static `returns:` (or
|
|
63
55
|
# `"untyped"` → `Dynamic[Top]`).
|
|
64
|
-
# @param buffer
|
|
56
|
+
# @param buffer [Rigor::Analysis::BufferBinding, nil]
|
|
65
57
|
# editor-mode buffer binding. When set, reads for the
|
|
66
58
|
# logical path resolve to the buffer's physical path so
|
|
67
59
|
# the pre-pass sees the in-flight bytes instead of the
|
|
@@ -94,8 +86,7 @@ module Rigor
|
|
|
94
86
|
SyntheticMethodIndex.new(entries: entries, class_names: class_names)
|
|
95
87
|
end
|
|
96
88
|
|
|
97
|
-
# Aggregates `(plugin_id, template)` pairs across every
|
|
98
|
-
# plugin's `manifest.heredoc_templates` in registration
|
|
89
|
+
# Aggregates `(plugin_id, template)` pairs across every plugin's `manifest.heredoc_templates` in registration
|
|
99
90
|
# order. Empty when no plugin contributes Tier C entries.
|
|
100
91
|
def collect_templates(plugin_registry)
|
|
101
92
|
return [] if plugin_registry.nil? || plugin_registry.empty?
|
|
@@ -108,10 +99,8 @@ module Rigor
|
|
|
108
99
|
end
|
|
109
100
|
end
|
|
110
101
|
|
|
111
|
-
# ADR-16 Tier B (slice 3b). Aggregates `(plugin_id, registry)`
|
|
112
|
-
#
|
|
113
|
-
# registration order. Empty when no plugin contributes Tier B
|
|
114
|
-
# entries.
|
|
102
|
+
# ADR-16 Tier B (slice 3b). Aggregates `(plugin_id, registry)` pairs across every plugin's
|
|
103
|
+
# `manifest.trait_registries` in registration order. Empty when no plugin contributes Tier B entries.
|
|
115
104
|
def collect_trait_registries(plugin_registry)
|
|
116
105
|
return [] if plugin_registry.nil? || plugin_registry.empty?
|
|
117
106
|
|
|
@@ -123,9 +112,8 @@ module Rigor
|
|
|
123
112
|
end
|
|
124
113
|
end
|
|
125
114
|
|
|
126
|
-
# ADR-36 — aggregates `(plugin_id, template)` pairs across
|
|
127
|
-
#
|
|
128
|
-
# no plugin contributes the nested-class emission tier.
|
|
115
|
+
# ADR-36 — aggregates `(plugin_id, template)` pairs across every plugin's `manifest.nested_class_templates`. Empty
|
|
116
|
+
# when no plugin contributes the nested-class emission tier.
|
|
129
117
|
def collect_nested_class_templates(plugin_registry)
|
|
130
118
|
return [] if plugin_registry.nil? || plugin_registry.empty?
|
|
131
119
|
|
|
@@ -137,10 +125,8 @@ module Rigor
|
|
|
137
125
|
end
|
|
138
126
|
end
|
|
139
127
|
|
|
140
|
-
# ADR-36 nested-class emission. For each class that `extend`s a
|
|
141
|
-
#
|
|
142
|
-
# `<block_method> do ... end` block, mint one synthetic
|
|
143
|
-
# subclass per `<variant_method> <Const>, <Type>` row:
|
|
128
|
+
# ADR-36 nested-class emission. For each class that `extend`s a template's `receiver_constraint` and carries a
|
|
129
|
+
# `<block_method> do ... end` block, mint one synthetic subclass per `<variant_method> <Const>, <Type>` row:
|
|
144
130
|
#
|
|
145
131
|
# class Shape
|
|
146
132
|
# extend Mangrove::Enum
|
|
@@ -149,13 +135,10 @@ module Rigor
|
|
|
149
135
|
# end
|
|
150
136
|
# end
|
|
151
137
|
#
|
|
152
|
-
# yields synthetic class `Shape::Circle` + instance method
|
|
153
|
-
# `
|
|
154
|
-
#
|
|
155
|
-
#
|
|
156
|
-
# `meta_new`); `#inner`'s return type is the literal constant
|
|
157
|
-
# type argument (non-constant inner shapes degrade to
|
|
158
|
-
# `Dynamic[Top]` per the slice-A floor).
|
|
138
|
+
# yields synthetic class `Shape::Circle` + instance method `Shape::Circle#inner -> Float`. The variant subclass
|
|
139
|
+
# name is recorded in `class_names` so `Environment#class_known?` resolves the constant (and `.new` dispatches
|
|
140
|
+
# through `meta_new`); `#inner`'s return type is the literal constant type argument (non-constant inner shapes
|
|
141
|
+
# degrade to `Dynamic[Top]` per the slice-A floor).
|
|
159
142
|
def collect_nested_class_entries(entries, class_names, nested_templates, ast, path)
|
|
160
143
|
return if ast.nil?
|
|
161
144
|
|
|
@@ -173,9 +156,8 @@ module Rigor
|
|
|
173
156
|
end
|
|
174
157
|
end
|
|
175
158
|
|
|
176
|
-
# Walks every class declaration, yielding its fully-qualified
|
|
177
|
-
#
|
|
178
|
-
# scope-stack bookkeeping but hands back the class node itself.
|
|
159
|
+
# Walks every class declaration, yielding its fully-qualified name and the `Prism::ClassNode`. Mirrors
|
|
160
|
+
# `walk_class_bodies`' scope-stack bookkeeping but hands back the class node itself.
|
|
179
161
|
def walk_classes(node, scope_stack = [], &)
|
|
180
162
|
return unless node.respond_to?(:compact_child_nodes)
|
|
181
163
|
|
|
@@ -198,9 +180,8 @@ module Rigor
|
|
|
198
180
|
body.respond_to?(:body) ? body.body.compact : []
|
|
199
181
|
end
|
|
200
182
|
|
|
201
|
-
# True when the class body carries `extend <constraint>`
|
|
202
|
-
#
|
|
203
|
-
# its first argument).
|
|
183
|
+
# True when the class body carries `extend <constraint>` (receiverless `extend` call with the constraint constant
|
|
184
|
+
# as its first argument).
|
|
204
185
|
def body_extends?(body, constraint)
|
|
205
186
|
body.any? do |stmt|
|
|
206
187
|
stmt.is_a?(Prism::CallNode) && stmt.receiver.nil? && stmt.name == :extend &&
|
|
@@ -208,9 +189,8 @@ module Rigor
|
|
|
208
189
|
end
|
|
209
190
|
end
|
|
210
191
|
|
|
211
|
-
# Yields `(variant_const_name, inner_type_node)` for every
|
|
212
|
-
# `<
|
|
213
|
-
# `<block_method> do ... end` block(s).
|
|
192
|
+
# Yields `(variant_const_name, inner_type_node)` for every `<variant_method> <Const>, <Type>` call inside the
|
|
193
|
+
# template's `<block_method> do ... end` block(s).
|
|
214
194
|
def each_variant_call(body, template, &)
|
|
215
195
|
body.each do |stmt|
|
|
216
196
|
next unless variants_block_call?(stmt, template)
|
|
@@ -275,8 +255,7 @@ module Rigor
|
|
|
275
255
|
|
|
276
256
|
# ADR-16 slice 4 — Concern re-targeting index.
|
|
277
257
|
#
|
|
278
|
-
# Walks every top-level / nested `module M` decl looking for
|
|
279
|
-
# the ActiveSupport::Concern shape:
|
|
258
|
+
# Walks every top-level / nested `module M` decl looking for the ActiveSupport::Concern shape:
|
|
280
259
|
#
|
|
281
260
|
# module M
|
|
282
261
|
# extend ActiveSupport::Concern
|
|
@@ -287,9 +266,8 @@ module Rigor
|
|
|
287
266
|
# end
|
|
288
267
|
# end
|
|
289
268
|
#
|
|
290
|
-
# The returned Hash maps `module_name => [deferred_call_node, ...]`.
|
|
291
|
-
#
|
|
292
|
-
# replays each deferred call against the including class.
|
|
269
|
+
# The returned Hash maps `module_name => [deferred_call_node, ...]`. When a class body later contains `include M`,
|
|
270
|
+
# the substrate replays each deferred call against the including class.
|
|
293
271
|
#
|
|
294
272
|
# Slice 4 scope (floor):
|
|
295
273
|
# - constant-path `include M` only (not `include some_var`).
|
|
@@ -334,8 +312,8 @@ module Rigor
|
|
|
334
312
|
end
|
|
335
313
|
end
|
|
336
314
|
|
|
337
|
-
# Recognises a module body that begins with (or contains at
|
|
338
|
-
#
|
|
315
|
+
# Recognises a module body that begins with (or contains at top level) an `extend ActiveSupport::Concern`
|
|
316
|
+
# statement.
|
|
339
317
|
def concern_module_body?(body)
|
|
340
318
|
return false unless body.respond_to?(:body)
|
|
341
319
|
|
|
@@ -358,13 +336,10 @@ module Rigor
|
|
|
358
336
|
end
|
|
359
337
|
end
|
|
360
338
|
|
|
361
|
-
# Slice 4 hook. When the current class body contains
|
|
362
|
-
#
|
|
363
|
-
#
|
|
364
|
-
#
|
|
365
|
-
# needed; downstream `collect_entries` /
|
|
366
|
-
# `collect_trait_entries` fire just as if the calls had been
|
|
367
|
-
# written directly in X's body.
|
|
339
|
+
# Slice 4 hook. When the current class body contains `include M` and M is a Concern with deferred DSL calls,
|
|
340
|
+
# replay each deferred call against the including class. Acts as a re-targeting walker — no new manifest entries
|
|
341
|
+
# needed; downstream `collect_entries` / `collect_trait_entries` fire just as if the calls had been written
|
|
342
|
+
# directly in X's body.
|
|
368
343
|
def collect_concern_re_targeted_entries(entries, call_node, class_name, concern_index, # rubocop:disable Metrics/ParameterLists
|
|
369
344
|
templates, registries, hierarchy, environment, path, fact_store = nil)
|
|
370
345
|
return unless call_node.name == :include && call_node.receiver.nil?
|
|
@@ -383,9 +358,8 @@ module Rigor
|
|
|
383
358
|
end
|
|
384
359
|
end
|
|
385
360
|
|
|
386
|
-
# Builds a lexical inheritance map `class_name => parent_class_name`
|
|
387
|
-
#
|
|
388
|
-
# across the AST set.
|
|
361
|
+
# Builds a lexical inheritance map `class_name => parent_class_name` by walking every top-level / nested `class X
|
|
362
|
+
# < Y` decl across the AST set.
|
|
389
363
|
def build_hierarchy(asts)
|
|
390
364
|
hierarchy = {}
|
|
391
365
|
asts.each_value do |ast|
|
|
@@ -415,11 +389,9 @@ module Rigor
|
|
|
415
389
|
end
|
|
416
390
|
end
|
|
417
391
|
|
|
418
|
-
# Yields `(class_name, call_node)` for every Prism::CallNode
|
|
419
|
-
#
|
|
420
|
-
#
|
|
421
|
-
# Tier C call shapes the substrate targets all live at the
|
|
422
|
-
# class body's top level.
|
|
392
|
+
# Yields `(class_name, call_node)` for every Prism::CallNode at class-body top level (singleton-context calls).
|
|
393
|
+
# Nested method bodies, blocks, and conditionals are skipped — the Tier C call shapes the substrate targets all
|
|
394
|
+
# live at the class body's top level.
|
|
423
395
|
def walk_class_bodies(node, scope_stack = [], &) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
|
424
396
|
return unless node.respond_to?(:compact_child_nodes)
|
|
425
397
|
|
|
@@ -487,18 +459,14 @@ module Rigor
|
|
|
487
459
|
end
|
|
488
460
|
end
|
|
489
461
|
|
|
490
|
-
# ADR-16 Tier B (slice 3b). For each matching call like
|
|
491
|
-
#
|
|
492
|
-
#
|
|
493
|
-
#
|
|
494
|
-
# unknown traits per design decision (2)) plus the
|
|
495
|
-
# always_included modules, then per-method-explode each
|
|
496
|
-
# module's RBS instance methods into the index.
|
|
462
|
+
# ADR-16 Tier B (slice 3b). For each matching call like `<X>.<method_name>(:trait_a, :trait_b)` where X inherits
|
|
463
|
+
# from the registry's receiver_constraint: collect every registered trait symbol's module (silently skipping
|
|
464
|
+
# unknown traits per design decision (2)) plus the always_included modules, then per-method-explode each module's
|
|
465
|
+
# RBS instance methods into the index.
|
|
497
466
|
#
|
|
498
|
-
# Per slice 3 floor (per user agreement): the synthesised
|
|
499
|
-
#
|
|
500
|
-
#
|
|
501
|
-
# actual RBS return type — is reserved for the ceiling slice.
|
|
467
|
+
# Per slice 3 floor (per user agreement): the synthesised methods adopt `return_type: "untyped"` (Dynamic[T] at
|
|
468
|
+
# dispatch). Precision promotion — looking up the module's actual RBS return type — is reserved for the ceiling
|
|
469
|
+
# slice.
|
|
502
470
|
def collect_trait_entries(entries, registries, class_name, call_node, hierarchy, environment, path)
|
|
503
471
|
registries.each do |(plugin_id, registry)|
|
|
504
472
|
next unless call_node.name == registry.method_name
|
|
@@ -511,16 +479,14 @@ module Rigor
|
|
|
511
479
|
end
|
|
512
480
|
end
|
|
513
481
|
|
|
514
|
-
# Resolves the set of modules to include from a Tier B
|
|
515
|
-
# call site:
|
|
482
|
+
# Resolves the set of modules to include from a Tier B call site:
|
|
516
483
|
#
|
|
517
484
|
# - `always_included` modules (unconditional);
|
|
518
485
|
# - one module per literal Symbol argument the call carries
|
|
519
486
|
# (resolved through `registry.modules_by_symbol`; unknown
|
|
520
487
|
# symbols silently skipped per design decision (2)).
|
|
521
488
|
#
|
|
522
|
-
# Returns an Array<String> of module names in
|
|
523
|
-
# `always_included` order followed by argument order.
|
|
489
|
+
# Returns an Array<String> of module names in `always_included` order followed by argument order.
|
|
524
490
|
def resolve_trait_modules(registry, call_node)
|
|
525
491
|
modules = registry.always_included.dup
|
|
526
492
|
positional_symbols(call_node, registry).each do |symbol|
|
|
@@ -558,10 +524,9 @@ module Rigor
|
|
|
558
524
|
end
|
|
559
525
|
end
|
|
560
526
|
|
|
561
|
-
# Returns the Symbol method-name list defined on `module_name`'s
|
|
562
|
-
# RBS
|
|
563
|
-
#
|
|
564
|
-
# nothing rather than fabricating method names).
|
|
527
|
+
# Returns the Symbol method-name list defined on `module_name`'s RBS instance definition. Empty Array when the
|
|
528
|
+
# module is not in the RBS env (silent skip — the synthetic emit produces nothing rather than fabricating method
|
|
529
|
+
# names).
|
|
565
530
|
def module_instance_method_names(module_name, environment)
|
|
566
531
|
return [] if environment.nil?
|
|
567
532
|
|
|
@@ -630,8 +595,7 @@ module Rigor
|
|
|
630
595
|
)
|
|
631
596
|
end
|
|
632
597
|
|
|
633
|
-
# ADR-18 three-tier fallback for the synthetic method's
|
|
634
|
-
# `return_type` string:
|
|
598
|
+
# ADR-18 three-tier fallback for the synthetic method's `return_type` string:
|
|
635
599
|
#
|
|
636
600
|
# 1. When `row.returns_from_arg` is present AND the
|
|
637
601
|
# call-site argument at the declared position is a
|
|
@@ -663,13 +627,10 @@ module Rigor
|
|
|
663
627
|
fact[source_rep]
|
|
664
628
|
end
|
|
665
629
|
|
|
666
|
-
# Extracts the source-text qualified-constant representation
|
|
667
|
-
#
|
|
668
|
-
#
|
|
669
|
-
#
|
|
670
|
-
# intentionally accepts only ConstantReadNode /
|
|
671
|
-
# ConstantPathNode per ADR-18; chained-call argument
|
|
672
|
-
# resolution stays deferred.
|
|
630
|
+
# Extracts the source-text qualified-constant representation of the call's positional argument (e.g.,
|
|
631
|
+
# `"Types::String"`). Returns nil for non-constant shapes (literals, method chains, blocks, …). The floor
|
|
632
|
+
# intentionally accepts only ConstantReadNode / ConstantPathNode per ADR-18; chained-call argument resolution
|
|
633
|
+
# stays deferred.
|
|
673
634
|
def argument_source_representation(call_node, position)
|
|
674
635
|
args = call_node.arguments&.arguments
|
|
675
636
|
return nil if args.nil? || position >= args.size
|
|
@@ -709,8 +670,7 @@ module Rigor
|
|
|
709
670
|
current = parent
|
|
710
671
|
end
|
|
711
672
|
|
|
712
|
-
# Fall back to the env's RBS-aware ordering for the case
|
|
713
|
-
# where the chain terminates at an RBS-known class
|
|
673
|
+
# Fall back to the env's RBS-aware ordering for the case where the chain terminates at an RBS-known class
|
|
714
674
|
# (ActiveRecord::Base, Dry::Struct, Sinatra::Base, …).
|
|
715
675
|
return false if environment.nil?
|
|
716
676
|
|
|
@@ -6,18 +6,15 @@ module Rigor
|
|
|
6
6
|
module LanguageServer
|
|
7
7
|
# Shared buffer lookup for the LSP providers.
|
|
8
8
|
#
|
|
9
|
-
# Every provider's `provide` opens with the same two steps: translate
|
|
10
|
-
# the
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
# (strict vs error-tolerant, whole-buffer vs cursor-recovery), so
|
|
14
|
-
# only this resolution is shared here.
|
|
9
|
+
# Every provider's `provide` opens with the same two steps: translate the document URI to an on-disk path,
|
|
10
|
+
# then fetch the open buffer entry from the buffer table — returning nil to the editor when either is
|
|
11
|
+
# missing. The parse step that follows differs per provider (strict vs error-tolerant, whole-buffer vs
|
|
12
|
+
# cursor-recovery), so only this resolution is shared here.
|
|
15
13
|
module BufferResolution
|
|
16
14
|
private
|
|
17
15
|
|
|
18
|
-
# Resolves `[path, entry]` for the document `uri`, or nil when the
|
|
19
|
-
#
|
|
20
|
-
# `path, entry = buffer_for(uri)` can guard on `entry.nil?` to cover
|
|
16
|
+
# Resolves `[path, entry]` for the document `uri`, or nil when the uri has no file path or no open
|
|
17
|
+
# buffer. A caller that destructures `path, entry = buffer_for(uri)` can guard on `entry.nil?` to cover
|
|
21
18
|
# both misses (a nil return leaves both locals nil).
|
|
22
19
|
def buffer_for(uri)
|
|
23
20
|
path = Uri.to_path(uri)
|