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
|
@@ -5,75 +5,54 @@ require "rigor/plugin"
|
|
|
5
5
|
|
|
6
6
|
module Rigor
|
|
7
7
|
module Plugin
|
|
8
|
-
# rigor-mangrove — precision plugin for the
|
|
9
|
-
# [Mangrove](https://github.com/kazzix14/mangrove) functional
|
|
8
|
+
# rigor-mangrove — precision plugin for the [Mangrove](https://github.com/kazzix14/mangrove) functional
|
|
10
9
|
# toolkit (Result / Option carriers).
|
|
11
10
|
#
|
|
12
11
|
# ## Why this plugin exists (and what it deliberately is NOT)
|
|
13
12
|
#
|
|
14
|
-
# Mangrove is a Sorbet-first library: every carrier method is
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
# covered by `rigor-sorbet` (sig ingestion + RBI walking) —
|
|
19
|
-
# this plugin is NOT a parallel type source. See
|
|
20
|
-
# `docs/notes/20260530-mangrove-library-survey.md`.
|
|
13
|
+
# Mangrove is a Sorbet-first library: every carrier method is annotated with an inline `sig { ... }`, and
|
|
14
|
+
# the Enum DSL's dynamic variants are materialised by a bundled Tapioca DSL compiler. So the *type source*
|
|
15
|
+
# for Mangrove is already covered by `rigor-sorbet` (sig ingestion + RBI walking) — this plugin is NOT a
|
|
16
|
+
# parallel type source. See `docs/notes/20260530-mangrove-library-survey.md`.
|
|
21
17
|
#
|
|
22
|
-
# What sig-ingestion (Sorbet-level precision) structurally
|
|
23
|
-
#
|
|
24
|
-
# type-member at the unwrap call site**. Mangrove's `Result`
|
|
25
|
-
# interface declares:
|
|
18
|
+
# What sig-ingestion (Sorbet-level precision) structurally cannot do is **instantiate the carrier's generic
|
|
19
|
+
# type-member at the unwrap call site**. Mangrove's `Result` interface declares:
|
|
26
20
|
#
|
|
27
21
|
# sig { abstract.returns(OkType) }
|
|
28
22
|
# def unwrap!; end
|
|
29
23
|
#
|
|
30
|
-
# `OkType` is a `type_member(:out)`. When the receiver's
|
|
31
|
-
#
|
|
32
|
-
# `
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
# contributes the cataloged sig's return type verbatim) nor a
|
|
36
|
-
# bare-`def unwrap!: () -> untyped` RBS stub performs. The
|
|
37
|
-
# call degrades to `Dynamic[top]`.
|
|
24
|
+
# `OkType` is a `type_member(:out)`. When the receiver's static type is
|
|
25
|
+
# `Mangrove::Result[String, StandardError]`, `unwrap!` *should* yield `String` — but resolving the abstract
|
|
26
|
+
# `OkType` to the receiver's first type argument is generic instantiation, which neither `rigor-sorbet` (it
|
|
27
|
+
# contributes the cataloged sig's return type verbatim) nor a bare-`def unwrap!: () -> untyped` RBS stub
|
|
28
|
+
# performs. The call degrades to `Dynamic[top]`.
|
|
38
29
|
#
|
|
39
|
-
# This plugin closes exactly that gap. At every recognised
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
# this only ever *sharpens* an existing `Dynamic[top]` into a
|
|
45
|
-
# concrete type — it never invents a receiver type and never
|
|
46
|
-
# emits a diagnostic of its own, so it cannot frighten working
|
|
47
|
-
# code (Rigor's false-positive discipline).
|
|
30
|
+
# This plugin closes exactly that gap. At every recognised unwrap-family call on a known Mangrove carrier,
|
|
31
|
+
# it reads the receiver's `Rigor::Type::Nominal#type_args` and contributes the first argument (the
|
|
32
|
+
# `OkType` / `InnerType`) as the call's return type. The receiver type is already known, so this only ever
|
|
33
|
+
# *sharpens* an existing `Dynamic[top]` into a concrete type — it never invents a receiver type and never
|
|
34
|
+
# emits a diagnostic of its own, so it cannot frighten working code (Rigor's false-positive discipline).
|
|
48
35
|
#
|
|
49
36
|
# ## Scope (slice 1)
|
|
50
37
|
#
|
|
51
|
-
# - Result unwrap family → `OkType` (`type_args[0]`):
|
|
52
|
-
# `
|
|
53
|
-
#
|
|
54
|
-
# `unwrap_or_raise_inner!`.
|
|
55
|
-
# - Option unwrap family → `InnerType` (`type_args[0]`):
|
|
56
|
-
# `unwrap`, `unwrap!`, `unwrap_or`, `expect!`,
|
|
38
|
+
# - Result unwrap family → `OkType` (`type_args[0]`): `unwrap!`, `unwrap_in`, `expect!`, `expect_with!`,
|
|
39
|
+
# `unwrap_or_raise!`, `unwrap_or_raise_with!`, `unwrap_or_raise_inner!`.
|
|
40
|
+
# - Option unwrap family → `InnerType` (`type_args[0]`): `unwrap`, `unwrap!`, `unwrap_or`, `expect!`,
|
|
57
41
|
# `expect_with!`.
|
|
58
42
|
#
|
|
59
|
-
# The contribution fires only when the receiver resolves to a
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
# no `type_args` (the engine does not infer generics from
|
|
66
|
-
# constructor arguments), so the plugin no-ops there rather
|
|
67
|
-
# than guessing — a conservative floor, not a regression.
|
|
43
|
+
# The contribution fires only when the receiver resolves to a carrier Nominal carrying a non-empty
|
|
44
|
+
# `type_args` — i.e. when the Result/Option came from a method whose declared return type is an applied
|
|
45
|
+
# generic (`-> Result[String, E]`), the realistic Mangrove shape. The bare-constructor shape
|
|
46
|
+
# (`Result::Ok.new("x")`) currently yields a raw Nominal with no `type_args` (the engine does not infer
|
|
47
|
+
# generics from constructor arguments), so the plugin no-ops there rather than guessing — a conservative
|
|
48
|
+
# floor, not a regression.
|
|
68
49
|
#
|
|
69
50
|
# ## Out of scope (tracked elsewhere)
|
|
70
51
|
#
|
|
71
|
-
# - `is_a?(Result::Ok)` / `Some` / `None` exhaustive
|
|
72
|
-
# narrowing — core control-flow analysis over a sealed
|
|
52
|
+
# - `is_a?(Result::Ok)` / `Some` / `None` exhaustive narrowing — core control-flow analysis over a sealed
|
|
73
53
|
# hierarchy, not a plugin surface.
|
|
74
|
-
# - The `variants do variant Const, Type end` Enum DSL is handled
|
|
75
|
-
#
|
|
76
|
-
# (Slice A — see `nested_class_templates:` block below).
|
|
54
|
+
# - The `variants do variant Const, Type end` Enum DSL is handled via ADR-36 `nested_class_templates:` in
|
|
55
|
+
# this plugin's manifest (Slice A — see `nested_class_templates:` block below).
|
|
77
56
|
class Mangrove < Rigor::Plugin::Base
|
|
78
57
|
manifest(
|
|
79
58
|
id: "mangrove",
|
|
@@ -90,10 +69,9 @@ module Rigor
|
|
|
90
69
|
# end
|
|
91
70
|
# end
|
|
92
71
|
#
|
|
93
|
-
# mints `Shape::Circle < Shape` with `#inner : Float`. The
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
# types as `Float` without running Mangrove's `const_missing`.
|
|
72
|
+
# mints `Shape::Circle < Shape` with `#inner : Float`. The substrate resolves the variant constant +
|
|
73
|
+
# its `#inner` reader statically so `Shape::Circle.new(1.0).inner` types as `Float` without running
|
|
74
|
+
# Mangrove's `const_missing`.
|
|
97
75
|
nested_class_templates: [
|
|
98
76
|
Rigor::Plugin::Macro::NestedClassTemplate.new(
|
|
99
77
|
receiver_constraint: "Mangrove::Enum",
|
|
@@ -106,11 +84,9 @@ module Rigor
|
|
|
106
84
|
]
|
|
107
85
|
)
|
|
108
86
|
|
|
109
|
-
# Carrier class names whose FIRST type argument is the
|
|
110
|
-
#
|
|
111
|
-
#
|
|
112
|
-
# leading `::` because `scope.type_of` reports the lexical
|
|
113
|
-
# form while user code may root the constant.
|
|
87
|
+
# Carrier class names whose FIRST type argument is the value the unwrap family yields (`OkType` for
|
|
88
|
+
# Result, `InnerType` for Option). Matched with and without a leading `::` because `scope.type_of`
|
|
89
|
+
# reports the lexical form while user code may root the constant.
|
|
114
90
|
RESULT_CARRIERS = [
|
|
115
91
|
"Mangrove::Result",
|
|
116
92
|
"Mangrove::Result::Ok",
|
|
@@ -123,23 +99,19 @@ module Rigor
|
|
|
123
99
|
"Mangrove::Option::None"
|
|
124
100
|
].freeze
|
|
125
101
|
|
|
126
|
-
# Methods on the Result carriers that return `OkType`
|
|
127
|
-
#
|
|
128
|
-
# the success value, raising / short-circuiting on `Err`.
|
|
102
|
+
# Methods on the Result carriers that return `OkType` (`type_args[0]`). All of these are documented as
|
|
103
|
+
# yielding the success value, raising / short-circuiting on `Err`.
|
|
129
104
|
RESULT_UNWRAP_METHODS = %i[
|
|
130
105
|
unwrap! unwrap_in expect! expect_with!
|
|
131
106
|
unwrap_or_raise! unwrap_or_raise_with! unwrap_or_raise_inner!
|
|
132
107
|
].freeze
|
|
133
108
|
|
|
134
|
-
# Methods on the Option carriers that return `InnerType`
|
|
135
|
-
# (`type_args[0]`).
|
|
109
|
+
# Methods on the Option carriers that return `InnerType` (`type_args[0]`).
|
|
136
110
|
OPTION_UNWRAP_METHODS = %i[unwrap unwrap! unwrap_or expect! expect_with!].freeze
|
|
137
111
|
|
|
138
|
-
# ADR-37 slice 2 — a pure return-type contributor. The engine
|
|
139
|
-
#
|
|
140
|
-
#
|
|
141
|
-
# the call is an unwrap method on an instantiated carrier and
|
|
142
|
-
# yields the carried `type_args[0]`.
|
|
112
|
+
# ADR-37 slice 2 — a pure return-type contributor. The engine gates on the receiver being a known
|
|
113
|
+
# carrier class (so the block only runs on Result/Option receivers); the block then confirms the call
|
|
114
|
+
# is an unwrap method on an instantiated carrier and yields the carried `type_args[0]`.
|
|
143
115
|
dynamic_return receivers: (RESULT_CARRIERS + OPTION_CARRIERS) do |call_node, scope|
|
|
144
116
|
receiver = call_node.receiver
|
|
145
117
|
next nil if receiver.nil?
|
|
@@ -152,18 +124,16 @@ module Rigor
|
|
|
152
124
|
|
|
153
125
|
private
|
|
154
126
|
|
|
155
|
-
# @return [Rigor::Type, nil] the receiver's inferred type, or
|
|
156
|
-
#
|
|
157
|
-
# node (mirrors rigor-sorbet's defensive degrade).
|
|
127
|
+
# @return [Rigor::Type, nil] the receiver's inferred type, or nil when the engine raises on a synthetic
|
|
128
|
+
# / unrecognised node (mirrors rigor-sorbet's defensive degrade).
|
|
158
129
|
def receiver_type_of(receiver, scope)
|
|
159
130
|
scope.type_of(receiver)
|
|
160
131
|
rescue StandardError
|
|
161
132
|
nil
|
|
162
133
|
end
|
|
163
134
|
|
|
164
|
-
# The value the unwrap family yields for this receiver, or
|
|
165
|
-
#
|
|
166
|
-
# the carrier is raw (no `type_args` to instantiate).
|
|
135
|
+
# The value the unwrap family yields for this receiver, or nil when the call is not an unwrap on a
|
|
136
|
+
# known carrier, or the carrier is raw (no `type_args` to instantiate).
|
|
167
137
|
def carried_type(receiver_type, method_name)
|
|
168
138
|
class_name = normalize(receiver_type.class_name)
|
|
169
139
|
type_args = receiver_type.type_args
|
|
@@ -8,9 +8,8 @@ require "rigor/flow_contribution/fact"
|
|
|
8
8
|
module Rigor
|
|
9
9
|
module Plugin
|
|
10
10
|
class Minitest < Rigor::Plugin::Base
|
|
11
|
-
# Recognises the four shapes of Minitest / Test::Unit
|
|
12
|
-
#
|
|
13
|
-
# the named local on the post-call edge.
|
|
11
|
+
# Recognises the four shapes of Minitest / Test::Unit assertions and emits a `post_return_fact` that
|
|
12
|
+
# narrows the named local on the post-call edge.
|
|
14
13
|
#
|
|
15
14
|
# ## Recognised call shapes
|
|
16
15
|
#
|
|
@@ -29,9 +28,8 @@ module Rigor
|
|
|
29
28
|
# refute_nil(x) → narrow x AWAY from nil
|
|
30
29
|
# refute_equal(literal, x) → narrow x AWAY from Constant<literal>
|
|
31
30
|
#
|
|
32
|
-
# Test::Unit's `assert_not_nil(x)` / `assert_not_equal(...)` /
|
|
33
|
-
# `
|
|
34
|
-
# share the recognizer with `refute_*` (they're aliases).
|
|
31
|
+
# Test::Unit's `assert_not_nil(x)` / `assert_not_equal(...)` / `assert_not_kind_of(...)` /
|
|
32
|
+
# `assert_not_instance_of(...)` share the recognizer with `refute_*` (they're aliases).
|
|
35
33
|
#
|
|
36
34
|
# ### (3) Minitest/spec `_(x).must_*` (positive) ###
|
|
37
35
|
#
|
|
@@ -41,12 +39,10 @@ module Rigor
|
|
|
41
39
|
# _(x).must_equal(literal) → narrow x to Constant<literal>
|
|
42
40
|
# _(x).must_match(regex) → narrow x to String
|
|
43
41
|
#
|
|
44
|
-
# The legacy bare `x.must_be_kind_of(T)` form (Minitest <
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
# to narrow against. Users who still rely on the legacy
|
|
49
|
-
# form should migrate to `_(x).must_*`.
|
|
42
|
+
# The legacy bare `x.must_be_kind_of(T)` form (Minitest < 6.0 monkey-patched onto Object) is
|
|
43
|
+
# intentionally NOT recognised — the receiver is the value itself rather than a wrapping `_(value)`,
|
|
44
|
+
# so the analyzer has nothing to narrow against. Users who still rely on the legacy form should
|
|
45
|
+
# migrate to `_(x).must_*`.
|
|
50
46
|
#
|
|
51
47
|
# ### (4) Minitest/spec `_(x).wont_*` (negative) ###
|
|
52
48
|
#
|
|
@@ -56,14 +52,10 @@ module Rigor
|
|
|
56
52
|
#
|
|
57
53
|
# ## Not yet recognised
|
|
58
54
|
#
|
|
59
|
-
# `assert_predicate(x, :foo?)` (custom predicate) /
|
|
60
|
-
# `
|
|
61
|
-
#
|
|
62
|
-
# `
|
|
63
|
-
# carrier Rigor doesn't model today (predicate-state,
|
|
64
|
-
# respond-to-set) or a multi-edge fact that the
|
|
65
|
-
# `post_return_facts` slot can't express. Queued for
|
|
66
|
-
# follow-up slices.
|
|
55
|
+
# `assert_predicate(x, :foo?)` (custom predicate) / `assert_respond_to(x, :method)` / `assert_includes`
|
|
56
|
+
# / `assert_operator` / `assert_throws` / `assert_raises(T) { ... }` etc. — each either needs a
|
|
57
|
+
# carrier Rigor doesn't model today (predicate-state, respond-to-set) or a multi-edge fact that the
|
|
58
|
+
# `post_return_facts` slot can't express. Queued for follow-up slices.
|
|
67
59
|
module AssertionAnalyzer
|
|
68
60
|
module_function
|
|
69
61
|
|
|
@@ -83,16 +75,12 @@ module Rigor
|
|
|
83
75
|
|
|
84
76
|
# --- assert_* / refute_* / assert_not_* form ---
|
|
85
77
|
|
|
86
|
-
# Maps each recognised assertion name to a tuple
|
|
87
|
-
# `[shape, negative]`. `shape` is one of:
|
|
78
|
+
# Maps each recognised assertion name to a tuple `[shape, negative]`. `shape` is one of:
|
|
88
79
|
#
|
|
89
|
-
# - :class_then_local — `assert_kind_of(T, x)`, T at
|
|
90
|
-
# args[0], local at args[1].
|
|
80
|
+
# - :class_then_local — `assert_kind_of(T, x)`, T at args[0], local at args[1].
|
|
91
81
|
# - :nil_local — `assert_nil(x)`, local at args[0].
|
|
92
|
-
# - :literal_then_local — `assert_equal(literal, x)`,
|
|
93
|
-
#
|
|
94
|
-
# - :regex_then_local — `assert_match(regex, x)`,
|
|
95
|
-
# regex at args[0], local at args[1].
|
|
82
|
+
# - :literal_then_local — `assert_equal(literal, x)`, literal at args[0], local at args[1].
|
|
83
|
+
# - :regex_then_local — `assert_match(regex, x)`, regex at args[0], local at args[1].
|
|
96
84
|
ASSERT_FORM = {
|
|
97
85
|
assert_kind_of: [:class_then_local, false],
|
|
98
86
|
assert_instance_of: [:class_then_local, false],
|
|
@@ -123,8 +111,7 @@ module Rigor
|
|
|
123
111
|
|
|
124
112
|
# --- _(x).must_* / .wont_* form ---
|
|
125
113
|
|
|
126
|
-
# Maps the spec-style matcher names to `[shape,
|
|
127
|
-
# negative]`. `shape`:
|
|
114
|
+
# Maps the spec-style matcher names to `[shape, negative]`. `shape`:
|
|
128
115
|
# - :class_arg — `_(x).must_be_kind_of(T)`, T at args[0].
|
|
129
116
|
# - :no_arg_nil — `_(x).must_be_nil`, no args.
|
|
130
117
|
# - :literal_arg — `_(x).must_equal(literal)`, literal at args[0].
|
|
@@ -144,8 +131,8 @@ module Rigor
|
|
|
144
131
|
}.freeze
|
|
145
132
|
private_constant :SPEC_MATCHER_FORM
|
|
146
133
|
|
|
147
|
-
# ADR-37 slice 2 — the method names this analyzer narrows on,
|
|
148
|
-
#
|
|
134
|
+
# ADR-37 slice 2 — the method names this analyzer narrows on, for the plugin's
|
|
135
|
+
# `narrowing_facts methods:` gate.
|
|
149
136
|
SUPPORTED_METHODS = (ASSERT_FORM.keys + SPEC_MATCHER_FORM.keys).freeze
|
|
150
137
|
|
|
151
138
|
def spec_form_fact(call_node, environment:)
|
|
@@ -160,10 +147,9 @@ module Rigor
|
|
|
160
147
|
fact_for_spec_shape(shape, target_local, args, negative: negative, environment: environment)
|
|
161
148
|
end
|
|
162
149
|
|
|
163
|
-
# `_(x)` returns a `Minitest::Expectation` wrapping x.
|
|
164
|
-
#
|
|
165
|
-
#
|
|
166
|
-
# local-variable arg in any of those receiver-call shapes.
|
|
150
|
+
# `_(x)` returns a `Minitest::Expectation` wrapping x. Some specs use `value(x)` or `expect(x)`
|
|
151
|
+
# interchangeably (Minitest provides all three as aliases). Recognises the local-variable arg in
|
|
152
|
+
# any of those receiver-call shapes.
|
|
167
153
|
SPEC_WRAPPER_NAMES = %i[_ value expect].freeze
|
|
168
154
|
private_constant :SPEC_WRAPPER_NAMES
|
|
169
155
|
|
|
@@ -6,61 +6,44 @@ require_relative "minitest/assertion_analyzer"
|
|
|
6
6
|
|
|
7
7
|
module Rigor
|
|
8
8
|
module Plugin
|
|
9
|
-
# rigor-minitest — narrows locals through Minitest /
|
|
10
|
-
# Test::Unit assertions.
|
|
9
|
+
# rigor-minitest — narrows locals through Minitest / Test::Unit assertions.
|
|
11
10
|
#
|
|
12
|
-
# Recognises every supported `assert_*` / `refute_*` /
|
|
13
|
-
# `
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# `post_return_fact` whose `:local`-kind target narrows the
|
|
18
|
-
# named local on the post-call edge so downstream calls in
|
|
19
|
-
# the same `it` / `test_` / `def test_*` body resolve
|
|
20
|
-
# against the narrowed type.
|
|
11
|
+
# Recognises every supported `assert_*` / `refute_*` / `assert_not_*` shape (Minitest core + Test::Unit
|
|
12
|
+
# aliases), plus the Minitest/spec `_(x).must_*` / `.wont_*` matchers (`expect(x)` / `value(x)` wrappers
|
|
13
|
+
# covered too). For each recognised assertion the plugin emits a Rigor `post_return_fact` whose
|
|
14
|
+
# `:local`-kind target narrows the named local on the post-call edge so downstream calls in the same
|
|
15
|
+
# `it` / `test_` / `def test_*` body resolve against the narrowed type.
|
|
21
16
|
#
|
|
22
17
|
# ## Why a single plugin covers two frameworks
|
|
23
18
|
#
|
|
24
|
-
# Minitest and Test::Unit share the `assert_*` / `refute_*`
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
# gem layers RSpec-style matcher composition on top of
|
|
28
|
-
# Minitest's `must` API and is covered by the spec-style
|
|
29
|
-
# recogniser without additional wiring.
|
|
19
|
+
# Minitest and Test::Unit share the `assert_*` / `refute_*` surface (Test::Unit's `assert_not_*` aliases
|
|
20
|
+
# are recognised by the same rule table). The matchers_vaccine gem layers RSpec-style matcher composition
|
|
21
|
+
# on top of Minitest's `must` API and is covered by the spec-style recogniser without additional wiring.
|
|
30
22
|
#
|
|
31
23
|
# ## Configuration
|
|
32
24
|
#
|
|
33
|
-
# No configuration knobs. Activate via
|
|
34
|
-
# `plugins: ["rigor-minitest"]` in `.rigor.yml`.
|
|
25
|
+
# No configuration knobs. Activate via `plugins: ["rigor-minitest"]` in `.rigor.yml`.
|
|
35
26
|
#
|
|
36
27
|
# ## Limitations
|
|
37
28
|
#
|
|
38
|
-
# - **No `assert_raises(T) { ... }`** — that's a block-shape
|
|
39
|
-
# matcher and Rigor's narrowing model is for
|
|
29
|
+
# - **No `assert_raises(T) { ... }`** — that's a block-shape matcher and Rigor's narrowing model is for
|
|
40
30
|
# straight-line locals.
|
|
41
|
-
# - **No `assert_predicate(x, :foo?)`** — needs a
|
|
42
|
-
#
|
|
43
|
-
# - **No `
|
|
44
|
-
# (respond-to-set carrier).
|
|
45
|
-
# - **No legacy bare `x.must_be_kind_of(T)`** — the
|
|
46
|
-
# receiver IS the value, so the analyzer has nothing to
|
|
31
|
+
# - **No `assert_predicate(x, :foo?)`** — needs a predicate-state carrier Rigor doesn't model today.
|
|
32
|
+
# - **No `assert_respond_to(x, :m)`** — same shape gap (respond-to-set carrier).
|
|
33
|
+
# - **No legacy bare `x.must_be_kind_of(T)`** — the receiver IS the value, so the analyzer has nothing to
|
|
47
34
|
# narrow against. Users should migrate to `_(x).must_*`.
|
|
48
|
-
# - **No `assert_in_delta` / `assert_operator`** — float-
|
|
49
|
-
#
|
|
50
|
-
# - **No `assert_throws(:tag) { ... }` / `assert_raises`** —
|
|
51
|
-
# block-form expectations need a separate slice.
|
|
35
|
+
# - **No `assert_in_delta` / `assert_operator`** — float-range / generic operator narrowing is future work.
|
|
36
|
+
# - **No `assert_throws(:tag) { ... }` / `assert_raises`** — block-form expectations need a separate slice.
|
|
52
37
|
class Minitest < Rigor::Plugin::Base
|
|
53
38
|
manifest(
|
|
54
39
|
id: "minitest",
|
|
55
40
|
version: "0.1.0",
|
|
56
41
|
description: "Narrows locals through Minitest / Test::Unit `assert_*` / `refute_*` " \
|
|
57
42
|
"and Minitest/spec `_(x).must_*` / `.wont_*` matchers.",
|
|
58
|
-
# ADR-38 — `def setup` runs before every `def test_*`, so
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
# type `@conn` (set in `setup`, read in a test) as
|
|
63
|
-
# `T | nil` and surface a false nil-receiver diagnostic.
|
|
43
|
+
# ADR-38 — `def setup` runs before every `def test_*`, so ivars it assigns are initialised by the
|
|
44
|
+
# time a test body reads them. Declaring it an additional initializer stops the read-before-write
|
|
45
|
+
# nil widening that would otherwise type `@conn` (set in `setup`, read in a test) as `T | nil` and
|
|
46
|
+
# surface a false nil-receiver diagnostic.
|
|
64
47
|
additional_initializers: [
|
|
65
48
|
Rigor::Plugin::AdditionalInitializer.new(
|
|
66
49
|
receiver_constraint: "Minitest::Test", methods: [:setup]
|
|
@@ -74,10 +57,8 @@ module Rigor
|
|
|
74
57
|
]
|
|
75
58
|
)
|
|
76
59
|
|
|
77
|
-
# ADR-37 slice 2 — emits `post_return_facts` for every recognised
|
|
78
|
-
#
|
|
79
|
-
# `:local`-kind facts through
|
|
80
|
-
# `StatementEvaluator#apply_local_post_return_fact`.
|
|
60
|
+
# ADR-37 slice 2 — emits `post_return_facts` for every recognised assertion, method-gated by the
|
|
61
|
+
# engine. The engine routes `:local`-kind facts through `StatementEvaluator#apply_local_post_return_fact`.
|
|
81
62
|
narrowing_facts methods: AssertionAnalyzer::SUPPORTED_METHODS do |call_node, scope|
|
|
82
63
|
AssertionAnalyzer.contribution_for(call_node, environment: scope&.environment)&.post_return_facts
|
|
83
64
|
end
|
|
@@ -6,41 +6,32 @@ require "prism"
|
|
|
6
6
|
module Rigor
|
|
7
7
|
module Plugin
|
|
8
8
|
class Pundit < Rigor::Plugin::Base
|
|
9
|
-
# Walks a parsed file's AST looking for Pundit
|
|
10
|
-
# entry-point calls and validates each against the
|
|
9
|
+
# Walks a parsed file's AST looking for Pundit entry-point calls and validates each against the
|
|
11
10
|
# {PolicyIndex}.
|
|
12
11
|
#
|
|
13
12
|
# Recognised shapes:
|
|
14
13
|
#
|
|
15
|
-
# - `authorize(record, :action)` — record's inferred
|
|
16
|
-
# type → `<Type>Policy#<action>?` lookup. Both the
|
|
14
|
+
# - `authorize(record, :action)` — record's inferred type → `<Type>Policy#<action>?` lookup. Both the
|
|
17
15
|
# policy class and the predicate must exist.
|
|
18
|
-
# - `authorize(record)` — without an action argument,
|
|
19
|
-
#
|
|
20
|
-
# action name is determined at runtime from the
|
|
21
|
-
# controller's current action; static validation
|
|
16
|
+
# - `authorize(record)` — without an action argument, we only validate that `<Type>Policy` exists. The
|
|
17
|
+
# action name is determined at runtime from the controller's current action; static validation
|
|
22
18
|
# isn't possible without controller context.
|
|
23
|
-
# - `policy(record)` / `policy_scope(scope)` — same
|
|
24
|
-
# `<Type>Policy` existence check.
|
|
19
|
+
# - `policy(record)` / `policy_scope(scope)` — same `<Type>Policy` existence check.
|
|
25
20
|
#
|
|
26
|
-
# When the first argument's inferred type is NOT a
|
|
27
|
-
#
|
|
28
|
-
# call is silently passed through. The plugin only
|
|
29
|
-
# validates what it can prove from the static type
|
|
21
|
+
# When the first argument's inferred type is NOT a `Nominal[T]` (e.g. an untyped local variable), the
|
|
22
|
+
# call is silently passed through. The plugin only validates what it can prove from the static type
|
|
30
23
|
# carrier.
|
|
31
24
|
module Analyzer
|
|
32
25
|
ENTRY_METHODS = %i[authorize policy policy_scope].freeze
|
|
33
26
|
|
|
34
|
-
# One policy violation: the kebab-case `rule`, `severity`, and
|
|
35
|
-
#
|
|
36
|
-
# (the `node_rule` block) positions it via `Plugin::Base#diagnostic`.
|
|
27
|
+
# One policy violation: the kebab-case `rule`, `severity`, and human-readable `message`. Carries no
|
|
28
|
+
# path/location — the caller (the `node_rule` block) positions it via `Plugin::Base#diagnostic`.
|
|
37
29
|
Violation = Struct.new(:rule, :severity, :message, keyword_init: true)
|
|
38
30
|
|
|
39
31
|
module_function
|
|
40
32
|
|
|
41
|
-
# The policy violations for a single call node (0..2 of them), or
|
|
42
|
-
#
|
|
43
|
-
# is not statically determinable. ADR-37: the engine owns the
|
|
33
|
+
# The policy violations for a single call node (0..2 of them), or `[]` when the node is not a
|
|
34
|
+
# Pundit entry call or its record type is not statically determinable. ADR-37: the engine owns the
|
|
44
35
|
# walk, so this is per-node logic.
|
|
45
36
|
#
|
|
46
37
|
# @param call_node [Prism::Node]
|
|
@@ -71,15 +62,11 @@ module Rigor
|
|
|
71
62
|
ENTRY_METHODS.include?(node.name) && node.receiver.nil?
|
|
72
63
|
end
|
|
73
64
|
|
|
74
|
-
# Resolves the first-argument expression to a policy
|
|
75
|
-
# class name. The candidates are:
|
|
65
|
+
# Resolves the first-argument expression to a policy class name. The candidates are:
|
|
76
66
|
# - `Foo` (a constant) → `FooPolicy`
|
|
77
|
-
# - `Foo.method(...)` whose inferred type is
|
|
78
|
-
#
|
|
79
|
-
#
|
|
80
|
-
# `Nominal[Bar]` → `BarPolicy`
|
|
81
|
-
# Returns `nil` when the type isn't statically
|
|
82
|
-
# determinable.
|
|
67
|
+
# - `Foo.method(...)` whose inferred type is `Nominal[Bar]` → `BarPolicy`
|
|
68
|
+
# - any other expression whose inferred type is `Nominal[Bar]` → `BarPolicy`
|
|
69
|
+
# Returns `nil` when the type isn't statically determinable.
|
|
83
70
|
def derive_policy_class_name(record_node, scope)
|
|
84
71
|
if record_node.is_a?(Prism::ConstantReadNode) || record_node.is_a?(Prism::ConstantPathNode)
|
|
85
72
|
constant_name = constant_receiver_name(record_node)
|
|
@@ -118,10 +105,8 @@ module Rigor
|
|
|
118
105
|
)
|
|
119
106
|
end
|
|
120
107
|
|
|
121
|
-
# Validates the `authorize(record, :action)` form.
|
|
122
|
-
#
|
|
123
|
-
# (the runtime infers it from the controller — out
|
|
124
|
-
# of scope here) or when the second argument isn't
|
|
108
|
+
# Validates the `authorize(record, :action)` form. Returns nil when the call has no second argument
|
|
109
|
+
# (the runtime infers it from the controller — out of scope here) or when the second argument isn't
|
|
125
110
|
# a literal symbol / string.
|
|
126
111
|
def action_violation(call_node, policy_entry)
|
|
127
112
|
args = call_node.arguments&.arguments || []
|
|
@@ -7,22 +7,17 @@ require_relative "policy_index"
|
|
|
7
7
|
module Rigor
|
|
8
8
|
module Plugin
|
|
9
9
|
class Pundit < Rigor::Plugin::Base
|
|
10
|
-
# Walks the configured policy-search paths via the
|
|
11
|
-
#
|
|
12
|
-
# Prism, and collects classes whose immediate
|
|
13
|
-
# superclass is one of the configured base classes.
|
|
10
|
+
# Walks the configured policy-search paths via the plugin's `IoBoundary`, parses each `.rb` file with
|
|
11
|
+
# Prism, and collects classes whose immediate superclass is one of the configured base classes.
|
|
14
12
|
#
|
|
15
|
-
# For each discovered policy class, the discoverer
|
|
16
|
-
#
|
|
17
|
-
# method. Non-predicate methods (`initialize`,
|
|
18
|
-
# `resolve`, helper methods) are ignored — Pundit's
|
|
13
|
+
# For each discovered policy class, the discoverer collects every instance-side `def name?` predicate
|
|
14
|
+
# method. Non-predicate methods (`initialize`, `resolve`, helper methods) are ignored — Pundit's
|
|
19
15
|
# `authorize` only ever calls predicate methods.
|
|
20
16
|
#
|
|
21
17
|
# Limitations (intentional for v0.1.0):
|
|
22
18
|
#
|
|
23
19
|
# - Direct-superclass match only.
|
|
24
|
-
# - Predicate methods are read from the syntactic
|
|
25
|
-
# `def` list. Methods built via `define_method` /
|
|
20
|
+
# - Predicate methods are read from the syntactic `def` list. Methods built via `define_method` /
|
|
26
21
|
# inherited from a sibling concern are out of scope.
|
|
27
22
|
class PolicyDiscoverer
|
|
28
23
|
def initialize(io_boundary:, search_paths:, base_classes:)
|
|
@@ -120,8 +115,7 @@ module Rigor
|
|
|
120
115
|
end
|
|
121
116
|
end
|
|
122
117
|
|
|
123
|
-
# Returns symbolic predicate names (`:update?`,
|
|
124
|
-
# `:show?`, …) defined on the policy. Only
|
|
118
|
+
# Returns symbolic predicate names (`:update?`, `:show?`, …) defined on the policy. Only
|
|
125
119
|
# instance-side names that end in `?` are recorded.
|
|
126
120
|
def collect_predicate_methods(body)
|
|
127
121
|
return [] if body.nil?
|
|
@@ -3,14 +3,11 @@
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module Plugin
|
|
5
5
|
class Pundit < Rigor::Plugin::Base
|
|
6
|
-
# Frozen catalogue of discovered Pundit policy classes
|
|
7
|
-
#
|
|
8
|
-
# Each entry tracks the set of predicate methods
|
|
9
|
-
# defined on the policy (instance-side `def name?`)
|
|
6
|
+
# Frozen catalogue of discovered Pundit policy classes keyed by policy class name (e.g. `"PostPolicy"`).
|
|
7
|
+
# Each entry tracks the set of predicate methods defined on the policy (instance-side `def name?`)
|
|
10
8
|
# plus the source file path.
|
|
11
9
|
#
|
|
12
|
-
# The analyzer maps a record's inferred type
|
|
13
|
-
# (`Nominal[Post]`) to the policy class name
|
|
10
|
+
# The analyzer maps a record's inferred type (`Nominal[Post]`) to the policy class name
|
|
14
11
|
# (`"PostPolicy"`) and looks up the predicate.
|
|
15
12
|
class PolicyIndex
|
|
16
13
|
Entry = Data.define(:policy_class_name, :file_path, :predicate_methods) do
|
|
@@ -22,8 +19,7 @@ module Rigor
|
|
|
22
19
|
predicate_methods.to_a.sort
|
|
23
20
|
end
|
|
24
21
|
|
|
25
|
-
# Normalises an action symbol / string by ensuring
|
|
26
|
-
# a trailing `?`. `:update` and `:update?` both
|
|
22
|
+
# Normalises an action symbol / string by ensuring a trailing `?`. `:update` and `:update?` both
|
|
27
23
|
# resolve to `update?`.
|
|
28
24
|
def normalize(name)
|
|
29
25
|
string = name.to_s
|
|
@@ -8,13 +8,11 @@ require_relative "pundit/analyzer"
|
|
|
8
8
|
|
|
9
9
|
module Rigor
|
|
10
10
|
module Plugin
|
|
11
|
-
# rigor-pundit — validates Pundit `authorize` /
|
|
12
|
-
# `policy` / `policy_scope` calls against the project's
|
|
11
|
+
# rigor-pundit — validates Pundit `authorize` / `policy` / `policy_scope` calls against the project's
|
|
13
12
|
# `app/policies/` tree.
|
|
14
13
|
#
|
|
15
14
|
# Tier 3B of the [Rails plugins roadmap](../../../../docs/design/20260508-rails-plugins-roadmap.md).
|
|
16
|
-
# Statically discovers policy classes by walking
|
|
17
|
-
# `policy_search_paths` and parsing each file with
|
|
15
|
+
# Statically discovers policy classes by walking `policy_search_paths` and parsing each file with
|
|
18
16
|
# Prism — no `pundit` runtime dependency.
|
|
19
17
|
#
|
|
20
18
|
# ## Configuration
|
|
@@ -27,34 +25,22 @@ module Rigor
|
|
|
27
25
|
#
|
|
28
26
|
# ## What it checks
|
|
29
27
|
#
|
|
30
|
-
# 1. **Policy class existence** — `authorize(record, ...)`
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
# 2. **Predicate method existence** — for the
|
|
35
|
-
# `authorize(record, :action)` form, validates that
|
|
36
|
-
# `<Policy>#<action>?` is defined. Missing methods
|
|
37
|
-
# emit `unknown-policy-method` listing the known
|
|
28
|
+
# 1. **Policy class existence** — `authorize(record, ...)` looks up `<inferred-type>Policy` in the index.
|
|
29
|
+
# Missing policies emit `unknown-policy-class` with a did-you-mean suggestion.
|
|
30
|
+
# 2. **Predicate method existence** — for the `authorize(record, :action)` form, validates that
|
|
31
|
+
# `<Policy>#<action>?` is defined. Missing methods emit `unknown-policy-method` listing the known
|
|
38
32
|
# predicates.
|
|
39
33
|
#
|
|
40
34
|
# ## Limitations (v0.1.0)
|
|
41
35
|
#
|
|
42
|
-
# - Records whose inferred type is NOT a `Nominal[T]`
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
# `authorize(record)` (which Pundit resolves at
|
|
51
|
-
# runtime against the controller's current action) is
|
|
52
|
-
# passed through with the policy-class check only.
|
|
53
|
-
# - Direct-superclass match for `policy_base_classes`.
|
|
54
|
-
# Indirect inheritance (`AdminPolicy <
|
|
55
|
-
# ApplicationPolicy`) needs `AdminPolicy` listed in
|
|
56
|
-
# `policy_base_classes` if subclasses inherit from
|
|
57
|
-
# it.
|
|
36
|
+
# - Records whose inferred type is NOT a `Nominal[T]` (untyped local variables, untyped instance
|
|
37
|
+
# variables) are silently passed through. The plugin only validates what it can prove from the
|
|
38
|
+
# static carrier.
|
|
39
|
+
# - The two-argument form `authorize(record, :action_symbol)` is the only one validated. The implicit
|
|
40
|
+
# form `authorize(record)` (which Pundit resolves at runtime against the controller's current action)
|
|
41
|
+
# is passed through with the policy-class check only.
|
|
42
|
+
# - Direct-superclass match for `policy_base_classes`. Indirect inheritance (`AdminPolicy <
|
|
43
|
+
# ApplicationPolicy`) needs `AdminPolicy` listed in `policy_base_classes` if subclasses inherit from it.
|
|
58
44
|
class Pundit < Rigor::Plugin::Base
|
|
59
45
|
manifest(
|
|
60
46
|
id: "pundit",
|
|
@@ -79,10 +65,9 @@ module Rigor
|
|
|
79
65
|
@policy_base_classes = Array(config.fetch("policy_base_classes")).map(&:to_s)
|
|
80
66
|
end
|
|
81
67
|
|
|
82
|
-
# File-level only: the load-error emission. The per-call policy
|
|
83
|
-
#
|
|
84
|
-
#
|
|
85
|
-
# `producer_value`, so both surfaces share one load.
|
|
68
|
+
# File-level only: the load-error emission. The per-call policy validation runs over the engine-owned
|
|
69
|
+
# walk via the node_rule below (ADR-37). The index is lazily loaded + memoised by `producer_value`,
|
|
70
|
+
# so both surfaces share one load.
|
|
86
71
|
def diagnostics_for_file(path:, scope:, root:) # rubocop:disable Lint/UnusedMethodArgument
|
|
87
72
|
index = producer_value(:policy_index)
|
|
88
73
|
return [load_error_diagnostic(path)] if index.nil? && producer_error(:policy_index)
|