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
|
@@ -4,28 +4,24 @@ require_relative "box"
|
|
|
4
4
|
|
|
5
5
|
module Rigor
|
|
6
6
|
module Plugin
|
|
7
|
-
# ADR-39 slice 5 — the selectable isolation strategy for target-library
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# is a **configurable strategy** (`RIGOR_PLUGIN_ISOLATION` env; the
|
|
12
|
-
# `exe/rigor` launcher maps `.rigor.yml`'s `plugins_isolation:` onto it
|
|
7
|
+
# ADR-39 slice 5 — the selectable isolation strategy for target-library invocation. A plugin invokes a
|
|
8
|
+
# pure method on a trusted target library (e.g. `ActiveSupport::Inflector.pluralize("post")`) through
|
|
9
|
+
# {.call}; how much the invocation is isolated from Rigor's own process is a **configurable strategy**
|
|
10
|
+
# (`RIGOR_PLUGIN_ISOLATION` env; the `exe/rigor` launcher maps `.rigor.yml`'s `plugins_isolation:` onto it
|
|
13
11
|
# before re-exec). Three backends behind one interface:
|
|
14
12
|
#
|
|
15
|
-
# - `none` — load into the main space and call directly.
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
# the parent survives and declines. Higher cost (fork + IPC).
|
|
13
|
+
# - `none` — load into the main space and call directly. Lowest cost; no isolation. Used as the fallback
|
|
14
|
+
# where fork is unavailable; fine because the invoked library is trusted + pure.
|
|
15
|
+
# - `ruby_box` — call inside a {Box} (`Ruby::Box`, `RUBY_BOX=1`). Isolates core-class monkey-patches +
|
|
16
|
+
# lets gem versions coexist, but a native crash in the boxed work still takes the process down
|
|
17
|
+
# (in-process).
|
|
18
|
+
# - `process` (**default**) — call in a forked worker ({Process}); returns data over a pipe. The
|
|
19
|
+
# strongest: a child crash (even `SIGSEGV`) is contained — the parent survives and declines. Higher cost
|
|
20
|
+
# (fork + IPC).
|
|
24
21
|
#
|
|
25
|
-
# All three answer with the method's return value, or raise
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
# that into silence, never a wrong fact.
|
|
22
|
+
# All three answer with the method's return value, or raise {Unavailable} (never approximate) when the
|
|
23
|
+
# target library cannot be reached in the chosen strategy — the caller's per-plugin rescue turns that into
|
|
24
|
+
# silence, never a wrong fact.
|
|
29
25
|
module Isolation
|
|
30
26
|
class Unavailable < StandardError
|
|
31
27
|
end
|
|
@@ -34,36 +30,31 @@ module Rigor
|
|
|
34
30
|
|
|
35
31
|
module_function
|
|
36
32
|
|
|
37
|
-
# The default strategy. `process` (a crash-contained forked worker)
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
# `none` where fork is unavailable (see {#backend}).
|
|
33
|
+
# The default strategy. `process` (a crash-contained forked worker) is the default: it isolates the
|
|
34
|
+
# target library's monkey-patches + crashes from Rigor with no in-process contamination, and forks a
|
|
35
|
+
# single persistent worker (not one per call). It falls back to `none` where fork is unavailable (see
|
|
36
|
+
# {#backend}).
|
|
42
37
|
DEFAULT = "process"
|
|
43
38
|
|
|
44
|
-
# The configured strategy name (`RIGOR_PLUGIN_ISOLATION`), defaulting
|
|
45
|
-
#
|
|
39
|
+
# The configured strategy name (`RIGOR_PLUGIN_ISOLATION`), defaulting to {DEFAULT} for any unset /
|
|
40
|
+
# unrecognised value.
|
|
46
41
|
def strategy_name
|
|
47
42
|
name = ENV["RIGOR_PLUGIN_ISOLATION"].to_s
|
|
48
43
|
STRATEGIES.include?(name) ? name : DEFAULT
|
|
49
44
|
end
|
|
50
45
|
|
|
51
|
-
# Invokes `receiver.method(*args)` on a target library, requiring
|
|
52
|
-
# `
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
# (Strings) — never free input. Returns the result, or raises
|
|
56
|
-
# {Unavailable}.
|
|
46
|
+
# Invokes `receiver.method(*args)` on a target library, requiring `feature` first, under the configured
|
|
47
|
+
# isolation strategy. `receiver` is a constant name (String), `method` a Symbol from the caller's
|
|
48
|
+
# allow-list, and `args` simple, Marshal-able / inspectable values (Strings) — never free input. Returns
|
|
49
|
+
# the result, or raises {Unavailable}.
|
|
57
50
|
def call(feature:, receiver:, method:, args:)
|
|
58
51
|
backend.call(feature: feature, receiver: receiver, method: method, args: args)
|
|
59
52
|
end
|
|
60
53
|
|
|
61
|
-
# The backend module for the configured strategy. `process`
|
|
62
|
-
# (
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
# main-space fallback is acceptable when no fork-based isolation can
|
|
66
|
-
# be had.
|
|
54
|
+
# The backend module for the configured strategy. `process` (including the default) falls back to
|
|
55
|
+
# `Direct` where `fork` is unavailable (Windows / JRuby) so inflection still works rather than silently
|
|
56
|
+
# degrading — the libraries are trusted + pure, so the main-space fallback is acceptable when no
|
|
57
|
+
# fork-based isolation can be had.
|
|
67
58
|
def backend
|
|
68
59
|
case strategy_name
|
|
69
60
|
when "ruby_box" then RubyBox
|
|
@@ -72,8 +63,8 @@ module Rigor
|
|
|
72
63
|
end
|
|
73
64
|
end
|
|
74
65
|
|
|
75
|
-
# `none` — load the trusted library into the main space and call it
|
|
76
|
-
#
|
|
66
|
+
# `none` — load the trusted library into the main space and call it directly. No isolation; lowest
|
|
67
|
+
# cost; the fork-unavailable fallback.
|
|
77
68
|
module Direct
|
|
78
69
|
module_function
|
|
79
70
|
|
|
@@ -85,9 +76,8 @@ module Rigor
|
|
|
85
76
|
end
|
|
86
77
|
end
|
|
87
78
|
|
|
88
|
-
# `ruby_box` — call inside the shared {Box}. The expression is built
|
|
89
|
-
#
|
|
90
|
-
# args (safe Ruby literals), so the box's `eval` carries no free
|
|
79
|
+
# `ruby_box` — call inside the shared {Box}. The expression is built from the fixed `receiver` /
|
|
80
|
+
# allow-listed `method` and `inspect`-ed args (safe Ruby literals), so the box's `eval` carries no free
|
|
91
81
|
# input.
|
|
92
82
|
module RubyBox
|
|
93
83
|
module_function
|
|
@@ -96,9 +86,8 @@ module Rigor
|
|
|
96
86
|
raise Unavailable, "ruby_box isolation requested but Ruby::Box is not active (RUBY_BOX=1)" unless Box.enabled?
|
|
97
87
|
raise Unavailable, "#{feature} could not be loaded into the Ruby::Box" unless Box.require_feature(feature)
|
|
98
88
|
|
|
99
|
-
# `receiver` is a fixed constant name and `method` an allow-listed
|
|
100
|
-
#
|
|
101
|
-
# the expression is e.g. `ActiveSupport::Inflector.pluralize("x")`
|
|
89
|
+
# `receiver` is a fixed constant name and `method` an allow-listed symbol; args are rendered via
|
|
90
|
+
# `inspect` (safe Ruby literals), so the expression is e.g. `ActiveSupport::Inflector.pluralize("x")`
|
|
102
91
|
# — no free input reaches the box's eval.
|
|
103
92
|
rendered = args.map(&:inspect).join(", ")
|
|
104
93
|
expression = "#{receiver}.#{method}(#{rendered})"
|
|
@@ -106,9 +95,8 @@ module Rigor
|
|
|
106
95
|
end
|
|
107
96
|
end
|
|
108
97
|
|
|
109
|
-
# `process` — run the call in a forked worker so a crash (even a C
|
|
110
|
-
#
|
|
111
|
-
# worker (broken pipe / EOF) and declines instead of dying. A single
|
|
98
|
+
# `process` — run the call in a forked worker so a crash (even a C extension `SIGSEGV`) is contained:
|
|
99
|
+
# the parent detects the dead worker (broken pipe / EOF) and declines instead of dying. A single
|
|
112
100
|
# persistent worker handles all calls over a Marshal pipe pair.
|
|
113
101
|
module Process
|
|
114
102
|
module_function
|
|
@@ -128,9 +116,9 @@ module Rigor
|
|
|
128
116
|
rescue Unavailable
|
|
129
117
|
raise
|
|
130
118
|
rescue StandardError => e
|
|
131
|
-
# A dead worker surfaces as EOFError (Marshal.load) or Errno::EPIPE
|
|
132
|
-
#
|
|
133
|
-
#
|
|
119
|
+
# A dead worker surfaces as EOFError (Marshal.load) or Errno::EPIPE (Marshal.dump) — both
|
|
120
|
+
# StandardError. The crash is contained: the parent resets the worker (respawn next call) and
|
|
121
|
+
# declines.
|
|
134
122
|
@worker = nil
|
|
135
123
|
raise Unavailable, "process isolation worker failed (#{e.class})"
|
|
136
124
|
end
|
|
@@ -164,9 +152,8 @@ module Rigor
|
|
|
164
152
|
end
|
|
165
153
|
end
|
|
166
154
|
|
|
167
|
-
# The child loop: read a `[feature, receiver, method, args]`
|
|
168
|
-
#
|
|
169
|
-
# `[:error, message]`. EOF (parent gone) ends the loop.
|
|
155
|
+
# The child loop: read a `[feature, receiver, method, args]` request, require + call, and write
|
|
156
|
+
# `[:ok, result]` or `[:error, message]`. EOF (parent gone) ends the loop.
|
|
170
157
|
def run_worker_loop(req_r, res_w)
|
|
171
158
|
loop do
|
|
172
159
|
feature, receiver, method, args = Marshal.load(req_r) # rubocop:disable Security/MarshalLoad -- parent input
|
|
@@ -2,28 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module Plugin
|
|
5
|
-
# Raised inside the loader (and surfaced as a diagnostic by the
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# underlying cause so the diagnostic message stays precise.
|
|
5
|
+
# Raised inside the loader (and surfaced as a diagnostic by the analyzer) when a plugin entry cannot be
|
|
6
|
+
# resolved or instantiated. Carries the failing plugin reference plus the underlying cause so the diagnostic
|
|
7
|
+
# message stays precise.
|
|
9
8
|
#
|
|
10
|
-
# ADR-2 § "Plugin Trust and I/O Policy" requires plugin failures
|
|
11
|
-
#
|
|
12
|
-
# carrier for that contract on the loading side.
|
|
9
|
+
# ADR-2 § "Plugin Trust and I/O Policy" requires plugin failures to be isolated at the analyzer boundary;
|
|
10
|
+
# this class is the carrier for that contract on the loading side.
|
|
13
11
|
class LoadError < StandardError
|
|
14
12
|
attr_reader :plugin_ref, :cause_class, :reason
|
|
15
13
|
|
|
16
|
-
# ADR-9 slice 5 introduces two new reason codes alongside the
|
|
17
|
-
#
|
|
18
|
-
# init failures:
|
|
14
|
+
# ADR-9 slice 5 introduces two new reason codes alongside the implicit "load failure" used for require /
|
|
15
|
+
# configuration / init failures:
|
|
19
16
|
#
|
|
20
|
-
# - `:missing-producer` — a non-optional `manifest(consumes:)`
|
|
21
|
-
#
|
|
22
|
-
# produces.
|
|
17
|
+
# - `:missing-producer` — a non-optional `manifest(consumes:)` entry names a `(plugin_id, name)` no
|
|
18
|
+
# loaded plugin produces.
|
|
23
19
|
# - `:dependency-cycle` — the consumes graph forms a cycle.
|
|
24
20
|
#
|
|
25
|
-
# Older callers omit `reason:` and the field defaults to nil
|
|
26
|
-
# (the legacy "load failure" envelope).
|
|
21
|
+
# Older callers omit `reason:` and the field defaults to nil (the legacy "load failure" envelope).
|
|
27
22
|
def initialize(message, plugin_ref:, cause: nil, reason: nil)
|
|
28
23
|
super(message)
|
|
29
24
|
@plugin_ref = plugin_ref
|
data/lib/rigor/plugin/loader.rb
CHANGED
|
@@ -6,34 +6,27 @@ require_relative "load_error"
|
|
|
6
6
|
|
|
7
7
|
module Rigor
|
|
8
8
|
module Plugin
|
|
9
|
-
# Resolves the project's `.rigor.yml` `plugins:` entries into
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# {Loader.load} returning a {Registry}.
|
|
9
|
+
# Resolves the project's `.rigor.yml` `plugins:` entries into instantiated plugin instances, paired with a
|
|
10
|
+
# service container. Internal slice-1 implementation; the public surface is {Loader.load} returning a
|
|
11
|
+
# {Registry}.
|
|
13
12
|
#
|
|
14
13
|
# Steps per entry (in order):
|
|
15
14
|
#
|
|
16
15
|
# 1. Normalise the entry into `{ gem:, id:, config: }`.
|
|
17
16
|
# 2. `require` the gem (failures surface as a {LoadError}).
|
|
18
|
-
# 3. Look up the registered plugin class by id (or by gem
|
|
19
|
-
#
|
|
20
|
-
# 4. Validate the user's config against the manifest's
|
|
21
|
-
# `config_schema`.
|
|
17
|
+
# 3. Look up the registered plugin class by id (or by gem name if the entry omitted an explicit id).
|
|
18
|
+
# 4. Validate the user's config against the manifest's `config_schema`.
|
|
22
19
|
# 5. Instantiate the plugin and call `init(services)`.
|
|
23
20
|
#
|
|
24
|
-
# Loading is deterministic: configuration order, with plugin
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
# collects them on the {Registry} so the runner can convert
|
|
28
|
-
# each one into a `:plugin_loader` diagnostic.
|
|
21
|
+
# Loading is deterministic: configuration order, with plugin id alphabetical as the tie-breaker for
|
|
22
|
+
# entries that resolve to the same gem. Failures do not abort the run; the loader collects them on the
|
|
23
|
+
# {Registry} so the runner can convert each one into a `:plugin_loader` diagnostic.
|
|
29
24
|
class Loader # rubocop:disable Metrics/ClassLength
|
|
30
25
|
attr_reader :services, :requirer
|
|
31
26
|
|
|
32
27
|
# @param services [Rigor::Plugin::Services]
|
|
33
|
-
# @param requirer [#call] takes a gem name and returns truthy
|
|
34
|
-
#
|
|
35
|
-
# a lambda; the spec injects a fake to avoid touching the
|
|
36
|
-
# real load path.
|
|
28
|
+
# @param requirer [#call] takes a gem name and returns truthy on successful require. Defaulted to
|
|
29
|
+
# `Kernel.require` via a lambda; the spec injects a fake to avoid touching the real load path.
|
|
37
30
|
def initialize(services:, requirer: ->(name) { require name })
|
|
38
31
|
@services = services
|
|
39
32
|
@requirer = requirer
|
|
@@ -43,8 +36,7 @@ module Rigor
|
|
|
43
36
|
new(services: services, requirer: requirer).load(configuration.plugins)
|
|
44
37
|
end
|
|
45
38
|
|
|
46
|
-
# @param entries [Array<String, Hash>] the raw `plugins:`
|
|
47
|
-
# list from the configuration.
|
|
39
|
+
# @param entries [Array<String, Hash>] the raw `plugins:` list from the configuration.
|
|
48
40
|
# @return [Registry]
|
|
49
41
|
def load(entries)
|
|
50
42
|
plugins = []
|
|
@@ -64,12 +56,10 @@ module Rigor
|
|
|
64
56
|
end
|
|
65
57
|
end
|
|
66
58
|
|
|
67
|
-
# ADR-9 slice 5 — topological sort by `manifest(consumes:)`
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
# offending plugin(s) drop from the returned plugins list
|
|
72
|
-
# and the LoadError surfaces alongside any earlier failure.
|
|
59
|
+
# ADR-9 slice 5 — topological sort by `manifest(consumes:)` so producers run before consumers, plus
|
|
60
|
+
# early `missing-producer` validation. Cycles surface as `dependency-cycle` LoadErrors. When
|
|
61
|
+
# validation fails, the offending plugin(s) drop from the returned plugins list and the LoadError
|
|
62
|
+
# surfaces alongside any earlier failure.
|
|
73
63
|
plugins, sort_errors = topo_sort_plugins(plugins)
|
|
74
64
|
load_errors.concat(sort_errors)
|
|
75
65
|
|
|
@@ -130,13 +120,11 @@ module Rigor
|
|
|
130
120
|
plugin
|
|
131
121
|
end
|
|
132
122
|
|
|
133
|
-
# ADR-25 — a plugin's manifest-declared `signature_paths:`
|
|
134
|
-
#
|
|
135
|
-
#
|
|
136
|
-
#
|
|
137
|
-
#
|
|
138
|
-
# broken. The raised LoadError is collected like any other
|
|
139
|
-
# load failure and the plugin drops from the registry.
|
|
123
|
+
# ADR-25 — a plugin's manifest-declared `signature_paths:` are resolved (by
|
|
124
|
+
# `Plugin::Base#signature_paths`) against the plugin gem root. A declared directory that does not exist
|
|
125
|
+
# is a load-time failure for that plugin — loud, not silent, because a missing `sig/` means the bundle
|
|
126
|
+
# gem is broken. The raised LoadError is collected like any other load failure and the plugin drops
|
|
127
|
+
# from the registry.
|
|
140
128
|
def validate_signature_paths!(plugin)
|
|
141
129
|
plugin.signature_paths.each do |dir|
|
|
142
130
|
next if File.directory?(dir)
|
|
@@ -221,19 +209,15 @@ module Rigor
|
|
|
221
209
|
plugin_class.to_s
|
|
222
210
|
end
|
|
223
211
|
|
|
224
|
-
# ADR-9 slice 5 — topological sort of plugins by their
|
|
225
|
-
# `
|
|
226
|
-
#
|
|
227
|
-
#
|
|
228
|
-
# manifest id. A non-optional consume of a `(plugin_id, name)`
|
|
229
|
-
# whose producer is missing emits a `:missing-producer`
|
|
230
|
-
# LoadError and drops the consumer; cycles emit a
|
|
212
|
+
# ADR-9 slice 5 — topological sort of plugins by their `manifest(consumes:)` declarations. Returns
|
|
213
|
+
# `[sorted_plugins, load_errors]`. Determinism: when no dependency relation forces an order, plugins
|
|
214
|
+
# are visited alphabetically by manifest id. A non-optional consume of a `(plugin_id, name)` whose
|
|
215
|
+
# producer is missing emits a `:missing-producer` LoadError and drops the consumer; cycles emit a
|
|
231
216
|
# `:dependency-cycle` LoadError naming the offending chain.
|
|
232
217
|
def topo_sort_plugins(plugins)
|
|
233
|
-
# If no plugin opts into the cross-plugin API the loader's
|
|
234
|
-
#
|
|
235
|
-
#
|
|
236
|
-
# run when at least one plugin declares `consumes:`.
|
|
218
|
+
# If no plugin opts into the cross-plugin API the loader's legacy configuration-order contract is
|
|
219
|
+
# preserved unchanged. Topo sort and missing-producer validation only run when at least one plugin
|
|
220
|
+
# declares `consumes:`.
|
|
237
221
|
return [plugins, []] unless plugins.any? { |p| p.manifest.consumes.any? }
|
|
238
222
|
|
|
239
223
|
index = plugins.to_h { |plugin| [plugin.manifest.id, plugin] }
|
|
@@ -267,12 +251,9 @@ module Rigor
|
|
|
267
251
|
producer.manifest.produces.include?(name)
|
|
268
252
|
end
|
|
269
253
|
|
|
270
|
-
# Kahn's algorithm with `Configuration#plugins`-order
|
|
271
|
-
#
|
|
272
|
-
#
|
|
273
|
-
# ready, the configuration-order index decides the visit
|
|
274
|
-
# order — preserves the v0.1.0 legacy contract for plugins
|
|
275
|
-
# without dependencies.
|
|
254
|
+
# Kahn's algorithm with `Configuration#plugins`-order tie-break. Edges go from producer -> consumer
|
|
255
|
+
# (producer must visit first). When two plugins are simultaneously ready, the configuration-order index
|
|
256
|
+
# decides the visit order — preserves the v0.1.0 legacy contract for plugins without dependencies.
|
|
276
257
|
def sort_in_topo_order(plugins, index, errors, config_order)
|
|
277
258
|
in_degree, forward = build_consumes_graph(plugins, index, errors)
|
|
278
259
|
ordered, cycle_errors = kahn_walk(plugins, in_degree, forward, config_order)
|
|
@@ -3,10 +3,8 @@
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module Plugin
|
|
5
5
|
module Macro
|
|
6
|
-
# ADR-16 Tier A declaration: "the block passed to a
|
|
7
|
-
#
|
|
8
|
-
# instance method on `receiver_constraint`'s subclass tree,
|
|
9
|
-
# with `self` typed accordingly."
|
|
6
|
+
# ADR-16 Tier A declaration: "the block passed to a class-level DSL call of one of `method_names` runs as
|
|
7
|
+
# an instance method on `receiver_constraint`'s subclass tree, with `self` typed accordingly."
|
|
10
8
|
#
|
|
11
9
|
# Authored on a plugin manifest:
|
|
12
10
|
#
|
|
@@ -21,41 +19,31 @@ module Rigor
|
|
|
21
19
|
# ]
|
|
22
20
|
# )
|
|
23
21
|
#
|
|
24
|
-
# Sinatra is the canonical worked target (`Sinatra::Base#generate_method`
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
# class-level methods whose block argument runs as if it were
|
|
30
|
-
# an instance method of the receiver.
|
|
22
|
+
# Sinatra is the canonical worked target (`Sinatra::Base#generate_method` at
|
|
23
|
+
# `lib/sinatra/base.rb:1788-1793` literally does `define_method(name, &block); remove_method` — the
|
|
24
|
+
# block IS the method body, byte-for-byte). The substrate adopts the same contract: declare the receiver
|
|
25
|
+
# constraint + the class-level methods whose block argument runs as if it were an instance method of the
|
|
26
|
+
# receiver.
|
|
31
27
|
#
|
|
32
|
-
# Engine wiring: `Inference::MacroBlockSelfType.narrow_self_type_for`
|
|
33
|
-
#
|
|
34
|
-
# and narrows `Scope#self_type` for matching block call sites.
|
|
28
|
+
# Engine wiring: `Inference::MacroBlockSelfType.narrow_self_type_for` (called from expression_typer.rb)
|
|
29
|
+
# consults registered entries and narrows `Scope#self_type` for matching block call sites.
|
|
35
30
|
#
|
|
36
31
|
# ## Fields
|
|
37
32
|
#
|
|
38
|
-
# - `receiver_constraint` — fully-qualified class name (String)
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
# `:
|
|
46
|
-
#
|
|
47
|
-
# - `self_type` — Symbol selecting the kind of `self`-binding
|
|
48
|
-
# the substrate applies inside the block. Slice 1a accepts
|
|
49
|
-
# only `:receiver_instance` (the block runs as an instance
|
|
50
|
-
# method of the receiver class). Other kinds (`:receiver_singleton`,
|
|
51
|
-
# `:dsl_recorder`) are reserved for later slices.
|
|
33
|
+
# - `receiver_constraint` — fully-qualified class name (String) that the call's lexical receiver MUST be
|
|
34
|
+
# (or inherit from) for the entry to fire. For Sinatra modular-style this is `"Sinatra::Base"`; the
|
|
35
|
+
# substrate's class-context match accepts every subclass.
|
|
36
|
+
# - `method_names` — Array of Symbol method names. A call shape `<receiver_subclass>.get('/path') { ... }`
|
|
37
|
+
# matches when `:get` is in this list. (Named `verbs:` before ADR-60 WD2 normalised the macro
|
|
38
|
+
# value-object vocabulary.)
|
|
39
|
+
# - `self_type` — Symbol selecting the kind of `self`-binding the substrate applies inside the block.
|
|
40
|
+
# Slice 1a accepts only `:receiver_instance` (the block runs as an instance method of the receiver
|
|
41
|
+
# class). Other kinds (`:receiver_singleton`, `:dsl_recorder`) are reserved for later slices.
|
|
52
42
|
#
|
|
53
43
|
# ## Ractor-shareability
|
|
54
44
|
#
|
|
55
|
-
# All fields are frozen at construction (ADR-15 Phase 1).
|
|
56
|
-
#
|
|
57
|
-
# does not leak into the value. `Ractor.shareable?` returns
|
|
58
|
-
# true after `#initialize`.
|
|
45
|
+
# All fields are frozen at construction (ADR-15 Phase 1). `method_names` is dup-frozen so the caller's
|
|
46
|
+
# mutable array does not leak into the value. `Ractor.shareable?` returns true after `#initialize`.
|
|
59
47
|
class BlockAsMethod
|
|
60
48
|
SELF_TYPE_RECEIVER_INSTANCE = :receiver_instance
|
|
61
49
|
VALID_SELF_TYPES = [SELF_TYPE_RECEIVER_INSTANCE].freeze
|
|
@@ -3,17 +3,13 @@
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module Plugin
|
|
5
5
|
module Macro
|
|
6
|
-
# ADR-16 Tier C declaration: "the class-level DSL call
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# interpolating the source-visible literal argument at
|
|
10
|
-
# `symbol_arg_position`."
|
|
6
|
+
# ADR-16 Tier C declaration: "the class-level DSL call `<receiver_constraint>.<method_name>(name_arg,
|
|
7
|
+
# ...)` emits synthetic methods on the calling class, with names interpolating the source-visible
|
|
8
|
+
# literal argument at `symbol_arg_position`."
|
|
11
9
|
#
|
|
12
|
-
# Textbook target — dry-struct's `attribute :name, T` and
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
# argument; the framework `class_eval`s a heredoc
|
|
16
|
-
# interpolating that Symbol; the emit table is fixed.
|
|
10
|
+
# Textbook target — dry-struct's `attribute :name, T` and ActiveStorage's `has_one_attached :avatar`
|
|
11
|
+
# both have this shape: a class-level call enumerates a literal Symbol argument; the framework
|
|
12
|
+
# `class_eval`s a heredoc interpolating that Symbol; the emit table is fixed.
|
|
17
13
|
#
|
|
18
14
|
# ## Authoring shape
|
|
19
15
|
#
|
|
@@ -39,38 +35,27 @@ module Rigor
|
|
|
39
35
|
#
|
|
40
36
|
# ## Fields
|
|
41
37
|
#
|
|
42
|
-
# - `receiver_constraint` — fully-qualified class name (String).
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
# - `
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
# template. Slice 2a accepts non-negative integers only.
|
|
51
|
-
# - `emit` — Array of `Emit` (or coerced Hash) — instance
|
|
52
|
-
# methods to synthesise on the calling class.
|
|
53
|
-
# - `class_level_emit` — same shape, but the synthesised
|
|
54
|
-
# methods are singleton (class-level) methods.
|
|
38
|
+
# - `receiver_constraint` — fully-qualified class name (String). Synthesis fires when the call's
|
|
39
|
+
# lexical receiver class equals or inherits from this constraint.
|
|
40
|
+
# - `method_name` — Symbol naming the DSL method (e.g. `:has_one_attached`, `:attribute`).
|
|
41
|
+
# - `symbol_arg_position` — Integer (default 0) — the argument index whose literal Symbol value becomes
|
|
42
|
+
# the `name` interpolated into each emit row's `name:` template. Slice 2a accepts non-negative
|
|
43
|
+
# integers only.
|
|
44
|
+
# - `emit` — Array of `Emit` (or coerced Hash) — instance methods to synthesise on the calling class.
|
|
45
|
+
# - `class_level_emit` — same shape, but the synthesised methods are singleton (class-level) methods.
|
|
55
46
|
#
|
|
56
47
|
# ## Floor / ceiling per ADR-16 WD13
|
|
57
48
|
#
|
|
58
|
-
# Slice 2 ships at the **floor**: each emit row's `name:`
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
# `macro.tier_c.unresolved-return` provenance marker.
|
|
66
|
-
# Precise return-type resolution via ADR-13's
|
|
67
|
-
# `Plugin::TypeNodeResolver` is the **ceiling**, deferred
|
|
68
|
-
# to a later slice — the `returns:` declarations cost
|
|
69
|
-
# nothing to write today and unlock precision then.
|
|
49
|
+
# Slice 2 ships at the **floor**: each emit row's `name:` is the source of truth for the synthetic
|
|
50
|
+
# method's name (a single `"\#{name}"` placeholder gets interpolated with the literal symbol argument at
|
|
51
|
+
# `symbol_arg_position`). The `returns:` strings are **recorded in the manifest but not resolved**; the
|
|
52
|
+
# engine emits synthetic methods with `Dynamic[T]` returns plus a `macro.tier_c.unresolved-return`
|
|
53
|
+
# provenance marker. Precise return-type resolution via ADR-13's `Plugin::TypeNodeResolver` is the
|
|
54
|
+
# **ceiling**, deferred to a later slice — the `returns:` declarations cost nothing to write today and
|
|
55
|
+
# unlock precision then.
|
|
70
56
|
#
|
|
71
|
-
# Engine wiring: `Inference::SyntheticMethodScanner` (slice 2b,
|
|
72
|
-
# `
|
|
73
|
-
# Worked consumers: `plugins/rigor-dry-struct/` and
|
|
57
|
+
# Engine wiring: `Inference::SyntheticMethodScanner` (slice 2b, `synthetic_method_scanner.rb`) consumes
|
|
58
|
+
# `manifest.heredoc_templates`. Worked consumers: `plugins/rigor-dry-struct/` and
|
|
74
59
|
# `plugins/rigor-dry-types/` (slice 2c).
|
|
75
60
|
class HeredocTemplate
|
|
76
61
|
NAME_PLACEHOLDER = "\#{name}"
|
|
@@ -109,15 +94,12 @@ module Rigor
|
|
|
109
94
|
to_h.hash
|
|
110
95
|
end
|
|
111
96
|
|
|
112
|
-
# One row of an emit table: the synthetic method's
|
|
113
|
-
# name
|
|
114
|
-
#
|
|
115
|
-
#
|
|
116
|
-
#
|
|
117
|
-
#
|
|
118
|
-
# [ADR-18](../../../../../docs/adr/18-substrate-per-call-site-return-type.md).
|
|
119
|
-
# When both are nil, the synthesised method's return type
|
|
120
|
-
# falls back to `Dynamic[Top]`.
|
|
97
|
+
# One row of an emit table: the synthetic method's name-template (the analyzer interpolates
|
|
98
|
+
# `\#{name}` with the call-site literal symbol) and its declared return type. The return type can be
|
|
99
|
+
# a static String (resolved via `Environment#nominal_for_name` per ADR-16 slice 6b) or a
|
|
100
|
+
# per-call-site lookup ({ReturnsFromArg}) — see
|
|
101
|
+
# [ADR-18](../../../../../docs/adr/18-substrate-per-call-site-return-type.md). When both are nil, the
|
|
102
|
+
# synthesised method's return type falls back to `Dynamic[Top]`.
|
|
121
103
|
class Emit
|
|
122
104
|
attr_reader :name, :returns, :returns_from_arg
|
|
123
105
|
|
|
@@ -155,10 +137,8 @@ module Rigor
|
|
|
155
137
|
end
|
|
156
138
|
end
|
|
157
139
|
|
|
158
|
-
# ADR-18 — per-call-site return-type DSL. Declares which
|
|
159
|
-
#
|
|
160
|
-
# in a cross-plugin fact channel for the synthesised
|
|
161
|
-
# method's return type.
|
|
140
|
+
# ADR-18 — per-call-site return-type DSL. Declares which call-site argument's source representation
|
|
141
|
+
# to look up in a cross-plugin fact channel for the synthesised method's return type.
|
|
162
142
|
#
|
|
163
143
|
# Authoring shape:
|
|
164
144
|
#
|
|
@@ -167,16 +147,13 @@ module Rigor
|
|
|
167
147
|
# lookup_via: { plugin_id: "dry-types", fact: :dry_type_aliases }
|
|
168
148
|
# }
|
|
169
149
|
#
|
|
170
|
-
# Slice 1 (this file) ships the value class + validation
|
|
171
|
-
#
|
|
172
|
-
# fact-store lookup land in slice 2 / 3.
|
|
150
|
+
# Slice 1 (this file) ships the value class + validation only. The scanner-side arg-position
|
|
151
|
+
# extraction + fact-store lookup land in slice 2 / 3.
|
|
173
152
|
class ReturnsFromArg
|
|
174
153
|
attr_reader :position, :plugin_id, :fact
|
|
175
154
|
|
|
176
|
-
# @return [ReturnsFromArg, nil] coerced value class
|
|
177
|
-
#
|
|
178
|
-
# any other shape so manifest authoring failures
|
|
179
|
-
# surface at construction time.
|
|
155
|
+
# @return [ReturnsFromArg, nil] coerced value class for a Hash / nil / ReturnsFromArg input. Raises
|
|
156
|
+
# on any other shape so manifest authoring failures surface at construction time.
|
|
180
157
|
def self.coerce(value)
|
|
181
158
|
return nil if value.nil?
|
|
182
159
|
return value if value.is_a?(ReturnsFromArg)
|