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
|
@@ -10,7 +10,8 @@ module Rigor
|
|
|
10
10
|
module Plugin
|
|
11
11
|
# rigor-dry-schema — Tier A per
|
|
12
12
|
# [ADR-12](../../../../../docs/adr/12-dry-rb-packaging.md) and the
|
|
13
|
-
# slicing plan in
|
|
13
|
+
# slicing plan in
|
|
14
|
+
# [docs/design/20260517-dry-validation-slicing.md](../../../../../docs/design/20260517-dry-validation-slicing.md).
|
|
14
15
|
#
|
|
15
16
|
# Recognises the canonical dry-schema declaration shapes:
|
|
16
17
|
#
|
|
@@ -28,51 +29,42 @@ module Rigor
|
|
|
28
29
|
# required(:foo).value(:string)
|
|
29
30
|
# end
|
|
30
31
|
#
|
|
31
|
-
# and publishes the resulting
|
|
32
|
-
# `
|
|
33
|
-
#
|
|
34
|
-
# Downstream `rigor-dry-validation` consumes the fact for
|
|
35
|
-
# per-Contract typed-payload synthesis.
|
|
32
|
+
# and publishes the resulting `{schema_const_fqn => {required: {key => underlying_class}, optional:
|
|
33
|
+
# {…}}}` table as the `:dry_schema_table` cross-plugin fact (ADR-9). Downstream `rigor-dry-validation`
|
|
34
|
+
# consumes the fact for per-Contract typed-payload synthesis.
|
|
36
35
|
#
|
|
37
36
|
# ## Predicate type recognition
|
|
38
37
|
#
|
|
39
|
-
# Each `required(:key).<predicate>(<arg>)` row maps the predicate
|
|
40
|
-
#
|
|
41
|
-
# canonical-type vocabulary:
|
|
38
|
+
# Each `required(:key).<predicate>(<arg>)` row maps the predicate argument to an underlying Ruby
|
|
39
|
+
# class via the dry-schema canonical-type vocabulary:
|
|
42
40
|
#
|
|
43
|
-
# - `:string` / `:integer` / `:float` / `:decimal` / `:symbol` /
|
|
44
|
-
# `:
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
# `filled(Types::String)`) resolve through the
|
|
52
|
-
# `:dry_type_aliases` ADR-9 fact published by `rigor-dry-types`
|
|
53
|
-
# when that plugin is loaded; without it the row degrades to
|
|
54
|
-
# "no type contribution from this key".
|
|
41
|
+
# - `:string` / `:integer` / `:float` / `:decimal` / `:symbol` / `:bool` / `:nil` / `:date` /
|
|
42
|
+
# `:date_time` / `:time` / `:hash` / `:array` map to their underlying class.
|
|
43
|
+
# - The four predicate verbs `filled` / `value` / `maybe` / `each` are accepted on the same row; their
|
|
44
|
+
# semantic difference (whether the value is nullable or coerced) does not change the underlying
|
|
45
|
+
# class for Rigor's purposes.
|
|
46
|
+
# - References to dry-types aliases (`value(Types::Email)`, `filled(Types::String)`) resolve through
|
|
47
|
+
# the `:dry_type_aliases` ADR-9 fact published by `rigor-dry-types` when that plugin is loaded;
|
|
48
|
+
# without it the row degrades to "no type contribution from this key".
|
|
55
49
|
#
|
|
56
50
|
# ## Floor / ceiling (slice 1)
|
|
57
51
|
#
|
|
58
52
|
# Slice 1 ships the **floor**:
|
|
59
53
|
#
|
|
60
|
-
# - Top-level `Foo = Dry::Schema.{Params,JSON,define} { ... }`
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
# for the canonical-type vocabulary above.
|
|
54
|
+
# - Top-level `Foo = Dry::Schema.{Params,JSON,define} { ... }` assignments. Class-level constants
|
|
55
|
+
# (`class Bar; SCHEMA = Dry::Schema.Params { ... }; end`) work too — the walker prefixes the
|
|
56
|
+
# enclosing constant chain.
|
|
57
|
+
# - `required(:key).<predicate>(:type_symbol_or_constant)` rows for the canonical-type vocabulary
|
|
58
|
+
# above.
|
|
66
59
|
# - Publishes the table; no user-facing diagnostics yet.
|
|
67
60
|
#
|
|
68
61
|
# The **ceiling** (slice 2+):
|
|
69
62
|
#
|
|
70
|
-
# - Synthesise typed `result.to_h` returns from each schema
|
|
71
|
-
#
|
|
63
|
+
# - Synthesise typed `result.to_h` returns from each schema via ADR-16 Tier C heredoc-template
|
|
64
|
+
# substrate.
|
|
72
65
|
# - Nested schemas (`schema(do ... end)` inside another row).
|
|
73
66
|
# - `predicates(:size?)` / `each { ... }` recursion.
|
|
74
|
-
# - Per-row `dry-schema.unknown-predicate` /
|
|
75
|
-
# `dry-schema.unknown-type` `:info` diagnostics when a
|
|
67
|
+
# - Per-row `dry-schema.unknown-predicate` / `dry-schema.unknown-type` `:info` diagnostics when a
|
|
76
68
|
# row's predicate or type symbol isn't recognised.
|
|
77
69
|
class DrySchema < Rigor::Plugin::Base
|
|
78
70
|
manifest(
|
|
@@ -84,10 +76,9 @@ module Rigor
|
|
|
84
76
|
consumes: [{ plugin_id: "dry-types", name: :dry_type_aliases, optional: true }]
|
|
85
77
|
)
|
|
86
78
|
|
|
87
|
-
# Walks every project file once during `prepare(services)` to
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
# walk is bounded by `paths:`, parse errors degrade silently.
|
|
79
|
+
# Walks every project file once during `prepare(services)` to build the schema table, then publishes
|
|
80
|
+
# via the ADR-9 fact store. Mirrors the rigor-dry-types `#prepare` shape — the walk is bounded by
|
|
81
|
+
# `paths:`, parse errors degrade silently.
|
|
91
82
|
def prepare(services)
|
|
92
83
|
type_aliases = services.fact_store.read(plugin_id: "dry-types", name: :dry_type_aliases) || {}
|
|
93
84
|
table = SchemaScanner.scan(paths: scannable_paths(services), type_aliases: type_aliases)
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Gem entry point. Required by Rigor's plugin loader when
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
# `Rigor::Plugin.register`, which the body of
|
|
7
|
-
# `lib/rigor/plugin/dry_schema.rb` performs at load time.
|
|
3
|
+
# Gem entry point. Required by Rigor's plugin loader when `.rigor.yml` lists `rigor-dry-schema` under
|
|
4
|
+
# `plugins:`. The loader expects this `require` to side-effect a call to `Rigor::Plugin.register`,
|
|
5
|
+
# which the body of `lib/rigor/plugin/dry_schema.rb` performs at load time.
|
|
8
6
|
require_relative "rigor/plugin/dry_schema"
|
|
@@ -4,16 +4,13 @@ require "rigor/plugin"
|
|
|
4
4
|
|
|
5
5
|
module Rigor
|
|
6
6
|
module Plugin
|
|
7
|
-
# ADR-16 Tier C worked plugin: recognises dry-struct's class-
|
|
8
|
-
#
|
|
9
|
-
# the enclosing `Dry::Struct` subclass.
|
|
7
|
+
# ADR-16 Tier C worked plugin: recognises dry-struct's class-level `attribute :name, T` DSL and
|
|
8
|
+
# synthesises a reader on the enclosing `Dry::Struct` subclass.
|
|
10
9
|
#
|
|
11
|
-
# dry-struct's `Dry::Struct::ClassInterface#attribute` (per the
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
# `class_interface.rb:452-464` `class_eval`s a heredoc
|
|
16
|
-
# interpolating that Symbol into a getter:
|
|
10
|
+
# dry-struct's `Dry::Struct::ClassInterface#attribute` (per the per-library survey,
|
|
11
|
+
# `lib/dry/struct/class_interface.rb:86-88` in the upstream gem) is the textbook Tier C target — a
|
|
12
|
+
# class-level DSL call enumerates a literal Symbol argument, and `class_interface.rb:452-464`
|
|
13
|
+
# `class_eval`s a heredoc interpolating that Symbol into a getter:
|
|
17
14
|
#
|
|
18
15
|
# class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
|
19
16
|
# def #{key} # def city
|
|
@@ -21,44 +18,36 @@ module Rigor
|
|
|
21
18
|
# end
|
|
22
19
|
# RUBY
|
|
23
20
|
#
|
|
24
|
-
# The substrate replays the same contract statically. For
|
|
25
|
-
# source like:
|
|
21
|
+
# The substrate replays the same contract statically. For source like:
|
|
26
22
|
#
|
|
27
23
|
# class Address < Dry::Struct
|
|
28
24
|
# attribute :city, Types::String
|
|
29
25
|
# attribute :country, Types::String
|
|
30
26
|
# end
|
|
31
27
|
#
|
|
32
|
-
# the pre-pass scans the file, sees `attribute :city, ...`, and
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
# than falling through to `call.undefined-method`.
|
|
28
|
+
# the pre-pass scans the file, sees `attribute :city, ...`, and synthesises `Address#city` as a
|
|
29
|
+
# SyntheticMethod the dispatcher surfaces below RBS dispatch. Bare `address.city` calls in other
|
|
30
|
+
# files then dispatch through the synthetic record rather than falling through to
|
|
31
|
+
# `call.undefined-method`.
|
|
37
32
|
#
|
|
38
33
|
# ## Precision model (ADR-16 WD13 + ADR-18)
|
|
39
34
|
#
|
|
40
|
-
# The synthetic reader's return type is resolved via ADR-18's
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
# for common cases. When the lookup misses (e.g. inline method-chain
|
|
45
|
-
# argument whose chain-head isn't currently extracted), the row
|
|
35
|
+
# The synthetic reader's return type is resolved via ADR-18's `returns_from_arg:` fact lookup: the
|
|
36
|
+
# call-site's second argument (`Types::String` etc.) is looked up through the `:dry_type_aliases`
|
|
37
|
+
# fact published by `rigor-dry-types`, yielding `Nominal[String]` for common cases. When the lookup
|
|
38
|
+
# misses (e.g. inline method-chain argument whose chain-head isn't currently extracted), the row
|
|
46
39
|
# falls back to `Dynamic[Top]` silently.
|
|
47
40
|
#
|
|
48
41
|
# ## Scope (slice 2c minimum)
|
|
49
42
|
#
|
|
50
43
|
# - Recognises `attribute :name, T` at class body top level.
|
|
51
|
-
# - Recognises `attribute? :name, T` via a separate template
|
|
52
|
-
#
|
|
53
|
-
# - Synthesises **only the reader** — the other survey-listed
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
# - Nested-block form (`attribute :details do ... end` minting
|
|
59
|
-
# `Address::Details`) is out of scope for slice 2c; that
|
|
60
|
-
# pattern needs Tier A + Tier C composition + const_set
|
|
61
|
-
# emission. Deferred.
|
|
44
|
+
# - Recognises `attribute? :name, T` via a separate template entry (omittable attribute; same reader
|
|
45
|
+
# name, `?` stripped).
|
|
46
|
+
# - Synthesises **only the reader** — the other survey-listed emit rows (`schema` key, `to_h` row,
|
|
47
|
+
# `[:key]` access, `.new` kwarg) are not yet wired by the substrate (they require either RBS-level
|
|
48
|
+
# shape synthesis or additional substrate primitives). Slice 2c stops at the reader.
|
|
49
|
+
# - Nested-block form (`attribute :details do ... end` minting `Address::Details`) is out of scope
|
|
50
|
+
# for slice 2c; that pattern needs Tier A + Tier C composition + const_set emission. Deferred.
|
|
62
51
|
class DryStruct < Rigor::Plugin::Base
|
|
63
52
|
manifest(
|
|
64
53
|
id: "dry-struct",
|
|
@@ -66,26 +55,20 @@ module Rigor
|
|
|
66
55
|
description: "Recognises dry-struct `attribute :name, T` DSL via ADR-16 Tier C; " \
|
|
67
56
|
"promotes the reader's return type through ADR-18's `returns_from_arg:` " \
|
|
68
57
|
"by consuming `rigor-dry-types`'s `:dry_type_aliases` fact.",
|
|
69
|
-
# ADR-9 consumption — the precision-promotion path
|
|
70
|
-
#
|
|
71
|
-
# `
|
|
72
|
-
#
|
|
73
|
-
# `returns_from_arg:` lookup misses and the synthetic
|
|
74
|
-
# readers fall back to `Dynamic[Top]` (the pre-ADR-18
|
|
75
|
-
# floor).
|
|
58
|
+
# ADR-9 consumption — the precision-promotion path below uses `:dry_type_aliases` published by
|
|
59
|
+
# `rigor-dry-types`. The fact is optional: when the `rigor-dry-types` plugin isn't loaded, the
|
|
60
|
+
# `returns_from_arg:` lookup misses and the synthetic readers fall back to `Dynamic[Top]` (the
|
|
61
|
+
# pre-ADR-18 floor).
|
|
76
62
|
consumes: [{ plugin_id: "dry-types", name: :dry_type_aliases, optional: true }],
|
|
77
63
|
heredoc_templates: [
|
|
78
64
|
Rigor::Plugin::Macro::HeredocTemplate.new(
|
|
79
65
|
receiver_constraint: "Dry::Struct",
|
|
80
66
|
method_name: :attribute,
|
|
81
67
|
symbol_arg_position: 0,
|
|
82
|
-
# ADR-18 — the synthetic reader's return type comes
|
|
83
|
-
#
|
|
84
|
-
# (`Types::String
|
|
85
|
-
#
|
|
86
|
-
# (e.g. inline `attribute :tag, Types::String.constrained(...)`,
|
|
87
|
-
# whose receiver chain head isn't currently
|
|
88
|
-
# extracted), the row falls back to Dynamic[Top].
|
|
68
|
+
# ADR-18 — the synthetic reader's return type comes from the call site's second argument
|
|
69
|
+
# (`Types::String` etc.), resolved through the `:dry_type_aliases` fact. When the lookup
|
|
70
|
+
# misses (e.g. inline `attribute :tag, Types::String.constrained(...)`, whose receiver chain
|
|
71
|
+
# head isn't currently extracted), the row falls back to Dynamic[Top].
|
|
89
72
|
emit: [{
|
|
90
73
|
name: "\#{name}",
|
|
91
74
|
returns_from_arg: {
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Gem entry point. Required by Rigor's plugin loader when
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
# `Rigor::Plugin.register`, which the body of
|
|
7
|
-
# `lib/rigor/plugin/dry_struct.rb` performs at load time.
|
|
3
|
+
# Gem entry point. Required by Rigor's plugin loader when `.rigor.yml` lists `rigor-dry-struct` under
|
|
4
|
+
# `plugins:`. The loader expects this `require` to side-effect a call to `Rigor::Plugin.register`,
|
|
5
|
+
# which the body of `lib/rigor/plugin/dry_struct.rb` performs at load time.
|
|
8
6
|
require_relative "rigor/plugin/dry_struct"
|
|
@@ -5,15 +5,12 @@ require "prism"
|
|
|
5
5
|
module Rigor
|
|
6
6
|
module Plugin
|
|
7
7
|
class DryTypes < Rigor::Plugin::Base
|
|
8
|
-
# Walks project source for `module X; include Dry.Types(); end`
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# See {DryTypes} module-docstring for the floor / ceiling
|
|
12
|
-
# scoping.
|
|
8
|
+
# Walks project source for `module X; include Dry.Types(); end` shapes and emits a
|
|
9
|
+
# `{ "<X>::<Alias>" => "<UnderlyingClass>" }` alias table covering the dry-types canonical-shortcut
|
|
10
|
+
# names. See {DryTypes} module-docstring for the floor / ceiling scoping.
|
|
13
11
|
module AliasScanner
|
|
14
|
-
# The canonical-shortcut names dry-types exposes through
|
|
15
|
-
# `
|
|
16
|
-
# from the upstream gem.
|
|
12
|
+
# The canonical-shortcut names dry-types exposes through `include Dry.Types()`. Mirrors
|
|
13
|
+
# `Dry::Types.type_keys` from the upstream gem.
|
|
17
14
|
CANONICAL_ALIASES = {
|
|
18
15
|
"String" => "String",
|
|
19
16
|
"Integer" => "Integer",
|
|
@@ -32,29 +29,21 @@ module Rigor
|
|
|
32
29
|
"Any" => "Object"
|
|
33
30
|
}.freeze
|
|
34
31
|
|
|
35
|
-
# Slice 2 — nested-category aliases. dry-types installs
|
|
36
|
-
#
|
|
37
|
-
# (
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
# coercion
|
|
42
|
-
#
|
|
43
|
-
# that are category-specific (`Params::Nil`,
|
|
44
|
-
# `JSON::Symbol`). For Rigor's purposes the underlying
|
|
45
|
-
# class is the same regardless of category — coercion
|
|
46
|
-
# semantics are a runtime concern. We register every
|
|
47
|
-
# `<module>::<Category>::<Name>` mapping the upstream gem
|
|
48
|
-
# publishes so call-site references work uniformly.
|
|
32
|
+
# Slice 2 — nested-category aliases. dry-types installs four parallel coercion categories:
|
|
33
|
+
# `Coercible::*` (everything-to-target coercion), `Strict::*` (no coercion; raise if mismatch),
|
|
34
|
+
# `Params::*` (HTTP / query-string-style coercion, used by Hanami / Roda / dry-web in request
|
|
35
|
+
# handling), `JSON::*` (JSON-shape coercion). Each category exposes the same set of names as the
|
|
36
|
+
# canonical shortcuts above, plus a few additions that are category-specific (`Params::Nil`,
|
|
37
|
+
# `JSON::Symbol`). For Rigor's purposes the underlying class is the same regardless of category —
|
|
38
|
+
# coercion semantics are a runtime concern. We register every `<module>::<Category>::<Name>`
|
|
39
|
+
# mapping the upstream gem publishes so call-site references work uniformly.
|
|
49
40
|
NESTED_CATEGORIES = %w[Coercible Strict Params JSON].freeze
|
|
50
41
|
private_constant :NESTED_CATEGORIES
|
|
51
42
|
|
|
52
43
|
module_function
|
|
53
44
|
|
|
54
|
-
# @param paths [Array<String>] absolute paths to `.rb`
|
|
55
|
-
#
|
|
56
|
-
# @return [Hash{String => String}] frozen
|
|
57
|
-
# `{aliased_name => underlying_class_name}` map. Empty
|
|
45
|
+
# @param paths [Array<String>] absolute paths to `.rb` files the project's `paths:` resolves to.
|
|
46
|
+
# @return [Hash{String => String}] frozen `{aliased_name => underlying_class_name}` map. Empty
|
|
58
47
|
# when no `include Dry.Types()` declaration is found.
|
|
59
48
|
def scan(paths:)
|
|
60
49
|
results = paths.flat_map { |path| scan_file(path) }
|
|
@@ -64,16 +53,16 @@ module Rigor
|
|
|
64
53
|
base = canonical_table(modules)
|
|
65
54
|
results.each do |result|
|
|
66
55
|
result[:compositions].each do |const_name, underlying|
|
|
67
|
-
# Each result's compositions are scoped under that
|
|
68
|
-
#
|
|
56
|
+
# Each result's compositions are scoped under that result's enclosing module
|
|
57
|
+
# (`Types::Email`, etc.).
|
|
69
58
|
base["#{result[:module_name]}::#{const_name}"] ||= underlying
|
|
70
59
|
end
|
|
71
60
|
end
|
|
72
61
|
base.freeze
|
|
73
62
|
end
|
|
74
63
|
|
|
75
|
-
# Populates the canonical-shortcut + nested-category
|
|
76
|
-
#
|
|
64
|
+
# Populates the canonical-shortcut + nested-category table (15 + 15 × 4 = 75 entries per alias
|
|
65
|
+
# module).
|
|
77
66
|
def canonical_table(modules)
|
|
78
67
|
modules.each_with_object({}) do |module_name, acc|
|
|
79
68
|
CANONICAL_ALIASES.each do |alias_name, underlying|
|
|
@@ -96,22 +85,18 @@ module Rigor
|
|
|
96
85
|
{ module_name: module_info[:module_name], compositions: compositions }
|
|
97
86
|
end
|
|
98
87
|
rescue StandardError
|
|
99
|
-
# Missing-file / parse failures degrade to "no
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
# dropping unparseable files keeps the fact stable.
|
|
88
|
+
# Missing-file / parse failures degrade to "no contribution from this file"; the plugin's
|
|
89
|
+
# user-visible surface is the published fact, and dropping unparseable files keeps the fact
|
|
90
|
+
# stable.
|
|
103
91
|
[]
|
|
104
92
|
end
|
|
105
93
|
private_class_method :scan_file
|
|
106
94
|
|
|
107
|
-
# Walks a Prism AST collecting alias-module info:
|
|
108
|
-
#
|
|
109
|
-
# Dry.Types();
|
|
110
|
-
#
|
|
111
|
-
#
|
|
112
|
-
# alias scope. The `body:` field is the
|
|
113
|
-
# `Prism::StatementsNode` (or nil) we re-walk later for
|
|
114
|
-
# user-authored compositions (slice 3).
|
|
95
|
+
# Walks a Prism AST collecting alias-module info: `{module_name:, body:}` for every `module X;
|
|
96
|
+
# include Dry.Types(); …end` shape. Tracks the enclosing module chain so a nested `module App;
|
|
97
|
+
# module Types; include Dry.Types(); end; end` publishes `"App::Types"` as the alias scope. The
|
|
98
|
+
# `body:` field is the `Prism::StatementsNode` (or nil) we re-walk later for user-authored
|
|
99
|
+
# compositions (slice 3).
|
|
115
100
|
def collect_alias_modules(node, qualified_prefix)
|
|
116
101
|
return [] unless node.is_a?(Prism::Node)
|
|
117
102
|
|
|
@@ -127,9 +112,8 @@ module Rigor
|
|
|
127
112
|
end
|
|
128
113
|
current + children
|
|
129
114
|
when Prism::ClassNode
|
|
130
|
-
# Module-level declarations win; we don't recurse into
|
|
131
|
-
#
|
|
132
|
-
# canonical pattern is module-level.
|
|
115
|
+
# Module-level declarations win; we don't recurse into class bodies for `include
|
|
116
|
+
# Dry.Types()` because the canonical pattern is module-level.
|
|
133
117
|
[]
|
|
134
118
|
else
|
|
135
119
|
node.compact_child_nodes.flat_map { |c| collect_alias_modules(c, qualified_prefix) }
|
|
@@ -137,31 +121,20 @@ module Rigor
|
|
|
137
121
|
end
|
|
138
122
|
private_class_method :collect_alias_modules
|
|
139
123
|
|
|
140
|
-
# Slice 3 — user-authored composition recognition.
|
|
141
|
-
#
|
|
142
|
-
#
|
|
143
|
-
# `
|
|
144
|
-
#
|
|
145
|
-
# `Integer`, …) — or on a nested-category form
|
|
146
|
-
# (`Strict::String` etc.) — registers the LHS under
|
|
147
|
-
# the canonical head's underlying class. Unions
|
|
148
|
-
# (`String | Integer`) and intersections are skipped
|
|
149
|
-
# (no single underlying class).
|
|
124
|
+
# Slice 3 — user-authored composition recognition. Walks the alias-module body for `Email =
|
|
125
|
+
# String.constrained(...)` shapes. Each `ConstantWriteNode` whose RHS is a method chain rooted on
|
|
126
|
+
# a canonical-shortcut name (`String`, `Integer`, …) — or on a nested-category form
|
|
127
|
+
# (`Strict::String` etc.) — registers the LHS under the canonical head's underlying class. Unions
|
|
128
|
+
# (`String | Integer`) and intersections are skipped (no single underlying class).
|
|
150
129
|
#
|
|
151
|
-
# Slice 4 — transitive composition resolution. After
|
|
152
|
-
#
|
|
153
|
-
#
|
|
154
|
-
#
|
|
155
|
-
#
|
|
156
|
-
#
|
|
157
|
-
# `ManagerEmail =
|
|
158
|
-
#
|
|
159
|
-
# detection: `A = B; B = A` resolves neither (each
|
|
160
|
-
# LHS's resolution walk sees itself in the visited
|
|
161
|
-
# set and bails). Unknown references (`ManagerEmail =
|
|
162
|
-
# NotAComposition`) silently drop — the user is free
|
|
163
|
-
# to assign any constant to any other; the plugin only
|
|
164
|
-
# publishes facts when the underlying class is known.
|
|
130
|
+
# Slice 4 — transitive composition resolution. After the direct (slice-3) pass collects
|
|
131
|
+
# compositions whose RHS root is canonical, a second pass walks the remaining
|
|
132
|
+
# `ConstantWriteNode`s for RHS shapes that resolve THROUGH an already-published composition —
|
|
133
|
+
# e.g. `ManagerEmail = Email` (bare reference) or `ManagerEmail = Email.constrained(min_size: 3)`
|
|
134
|
+
# (method chain rooted on a composition LHS). Cycle detection: `A = B; B = A` resolves neither
|
|
135
|
+
# (each LHS's resolution walk sees itself in the visited set and bails). Unknown references
|
|
136
|
+
# (`ManagerEmail = NotAComposition`) silently drop — the user is free to assign any constant to
|
|
137
|
+
# any other; the plugin only publishes facts when the underlying class is known.
|
|
165
138
|
def collect_compositions(body)
|
|
166
139
|
return {} if body.nil?
|
|
167
140
|
|
|
@@ -188,14 +161,10 @@ module Rigor
|
|
|
188
161
|
end
|
|
189
162
|
private_class_method :collect_compositions
|
|
190
163
|
|
|
191
|
-
# Slice-4 helper. Returns the un-resolved RHS root
|
|
192
|
-
#
|
|
193
|
-
#
|
|
194
|
-
#
|
|
195
|
-
# `ConstantReadNode` / `ConstantPathNode` tail rather
|
|
196
|
-
# than canonical-only. Declines on union / intersection
|
|
197
|
-
# operators for the same single-underlying-class
|
|
198
|
-
# reason.
|
|
164
|
+
# Slice-4 helper. Returns the un-resolved RHS root constant name (not necessarily a
|
|
165
|
+
# CANONICAL_ALIASES entry) for transitive resolution. Mirrors {composition_head_canonical} but
|
|
166
|
+
# accepts any `ConstantReadNode` / `ConstantPathNode` tail rather than canonical-only. Declines on
|
|
167
|
+
# union / intersection operators for the same single-underlying-class reason.
|
|
199
168
|
def transitive_reference_name(node)
|
|
200
169
|
case node
|
|
201
170
|
when Prism::ConstantReadNode
|
|
@@ -211,14 +180,10 @@ module Rigor
|
|
|
211
180
|
end
|
|
212
181
|
private_class_method :transitive_reference_name
|
|
213
182
|
|
|
214
|
-
# Slice-4 helper. Resolves `lhs`'s RHS through the
|
|
215
|
-
# `
|
|
216
|
-
#
|
|
217
|
-
#
|
|
218
|
-
# `"String"`) or nil when no chain ends at a direct
|
|
219
|
-
# composition. Cycles silently return nil — every step
|
|
220
|
-
# adds the current lhs to `visited` and bails on
|
|
221
|
-
# re-entry.
|
|
183
|
+
# Slice-4 helper. Resolves `lhs`'s RHS through the `direct` (slice-3) compositions table, chaining
|
|
184
|
+
# through `ref_edges` for transitive references. Returns the canonical underlying-class name
|
|
185
|
+
# (e.g. `"String"`) or nil when no chain ends at a direct composition. Cycles silently return nil
|
|
186
|
+
# — every step adds the current lhs to `visited` and bails on re-entry.
|
|
222
187
|
def resolve_transitive_ref(lhs, direct, ref_edges, visited:)
|
|
223
188
|
return nil if visited.include?(lhs)
|
|
224
189
|
|
|
@@ -231,25 +196,19 @@ module Rigor
|
|
|
231
196
|
end
|
|
232
197
|
private_class_method :resolve_transitive_ref
|
|
233
198
|
|
|
234
|
-
# Walks an RHS expression looking for the canonical
|
|
235
|
-
#
|
|
236
|
-
# the canonical name (`"String"` etc.) or nil.
|
|
199
|
+
# Walks an RHS expression looking for the canonical shortcut name at the root of a method chain.
|
|
200
|
+
# Returns the canonical name (`"String"` etc.) or nil.
|
|
237
201
|
#
|
|
238
202
|
# Recognised shapes (recursively on `node.receiver`):
|
|
239
203
|
#
|
|
240
|
-
# - Bare `String` / `Integer` — `Prism::ConstantReadNode`
|
|
241
|
-
#
|
|
242
|
-
# - `Strict::String` / `Coercible::Integer` / etc. —
|
|
243
|
-
# `Prism::ConstantPathNode` whose tail is in
|
|
204
|
+
# - Bare `String` / `Integer` — `Prism::ConstantReadNode` whose name is in `CANONICAL_ALIASES`.
|
|
205
|
+
# - `Strict::String` / `Coercible::Integer` / etc. — `Prism::ConstantPathNode` whose tail is in
|
|
244
206
|
# `CANONICAL_ALIASES`.
|
|
245
|
-
# - `String.constrained(...)` / `.optional` /
|
|
246
|
-
# `.default(...)` / arbitrary single-arg method —
|
|
207
|
+
# - `String.constrained(...)` / `.optional` / `.default(...)` / arbitrary single-arg method —
|
|
247
208
|
# recurse on the receiver.
|
|
248
209
|
#
|
|
249
|
-
# Declines on `String | Integer` (union, `:|`) and
|
|
250
|
-
#
|
|
251
|
-
# table doesn't claim a single underlying class for
|
|
252
|
-
# a multi-class composition.
|
|
210
|
+
# Declines on `String | Integer` (union, `:|`) and `String & Foo` (intersection, `:&`) so the
|
|
211
|
+
# alias table doesn't claim a single underlying class for a multi-class composition.
|
|
253
212
|
def composition_head_canonical(node)
|
|
254
213
|
case node
|
|
255
214
|
when Prism::ConstantReadNode
|
|
@@ -266,12 +225,10 @@ module Rigor
|
|
|
266
225
|
end
|
|
267
226
|
private_class_method :composition_head_canonical
|
|
268
227
|
|
|
269
|
-
# `include Dry.Types()` at the top of the module body is the
|
|
270
|
-
#
|
|
271
|
-
#
|
|
272
|
-
#
|
|
273
|
-
# `default: :nominal` style accepted by upstream; we treat
|
|
274
|
-
# both as "alias-installing").
|
|
228
|
+
# `include Dry.Types()` at the top of the module body is the canonical alias declaration. We
|
|
229
|
+
# accept the call anywhere in the body (some projects guard it with a `if defined?` check). The
|
|
230
|
+
# argument list must be empty (or a kwargs-only `default: :nominal` style accepted by upstream;
|
|
231
|
+
# we treat both as "alias-installing").
|
|
275
232
|
def contains_dry_types_include?(body)
|
|
276
233
|
return false if body.nil?
|
|
277
234
|
|
|
@@ -295,10 +252,9 @@ module Rigor
|
|
|
295
252
|
end
|
|
296
253
|
private_class_method :tree_walk
|
|
297
254
|
|
|
298
|
-
# Matches `include Dry.Types()` (with or without kwargs).
|
|
299
|
-
#
|
|
300
|
-
#
|
|
301
|
-
# method call on the `Dry` constant naming `Types`.
|
|
255
|
+
# Matches `include Dry.Types()` (with or without kwargs). The receiver of the include call MUST
|
|
256
|
+
# be implicit (i.e., called on `self`), and the argument MUST be a method call on the `Dry`
|
|
257
|
+
# constant naming `Types`.
|
|
302
258
|
def include_call_targeting_dry_types?(node)
|
|
303
259
|
return false unless node.is_a?(Prism::CallNode)
|
|
304
260
|
return false unless node.name == :include && node.receiver.nil?
|
|
@@ -319,11 +275,9 @@ module Rigor
|
|
|
319
275
|
end
|
|
320
276
|
private_class_method :dry_types_call?
|
|
321
277
|
|
|
322
|
-
# Resolves a `Prism::ConstantPathNode` /
|
|
323
|
-
# `
|
|
324
|
-
#
|
|
325
|
-
# dynamic-prefix shape so the scanner treats those as
|
|
326
|
-
# opaque rather than guessing.
|
|
278
|
+
# Resolves a `Prism::ConstantPathNode` / `Prism::ConstantReadNode` chain to its dot-separated
|
|
279
|
+
# name (e.g. `"App::Types"`). Returns nil for the dynamic-prefix shape so the scanner treats
|
|
280
|
+
# those as opaque rather than guessing.
|
|
327
281
|
def qualified_name_for(node)
|
|
328
282
|
case node
|
|
329
283
|
when Prism::ConstantReadNode then node.name.to_s
|