rigortype 0.2.7 → 0.2.9
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/data/core_overlay/csv.rbs +28 -0
- data/data/core_overlay/psych.rbs +22 -0
- data/docs/handbook/01-getting-started.md +9 -1
- data/docs/handbook/02-everyday-types.md +4 -1
- data/docs/handbook/08-understanding-errors.md +3 -3
- data/docs/manual/01-installation.md +1 -0
- data/docs/manual/02-cli-reference.md +16 -7
- data/docs/manual/04-diagnostics.md +7 -4
- data/docs/manual/06-baseline.md +5 -2
- data/docs/manual/07-plugins.md +1 -1
- data/docs/manual/14-rails-quickstart.md +4 -2
- data/docs/manual/15-type-protection-coverage.md +27 -4
- data/docs/manual/plugins/rigor-actionpack.md +1 -1
- data/docs/manual/plugins/rigor-activerecord.md +12 -5
- data/docs/manual/plugins/rigor-rails-routes.md +11 -0
- 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 +248 -186
- 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 +62 -48
- 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 +46 -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_fork_scan.rb +55 -0
- data/lib/rigor/cli/protection_renderer.rb +5 -7
- data/lib/rigor/cli/protection_report.rb +34 -18
- 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/missing_gem_constant_index.rb +128 -0
- 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 +225 -304
- 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 +794 -1154
- 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/fork_map.rb +87 -0
- 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 +627 -727
- data/lib/rigor/inference/origin_lookup.rb +38 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +154 -109
- 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 -821
- data/lib/rigor/inference/statement_evaluator.rb +754 -1114
- 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 +141 -184
- 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 +92 -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 +40 -48
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +140 -129
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +56 -59
- 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/structure_sql_parser.rb +172 -0
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +168 -241
- 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-activesupport-core-ext/sig/active_support/core_ext.rbs +32 -0
- 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/grape_api_discoverer.rb +189 -0
- 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 +61 -75
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +319 -505
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +91 -96
- 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/environment.rbs +1 -0
- data/sig/rigor/scope.rbs +6 -0
- metadata +9 -1
|
@@ -12,51 +12,41 @@ module Rigor
|
|
|
12
12
|
# [Rails plugins roadmap](../../../../../docs/design/20260508-rails-plugins-roadmap.md)
|
|
13
13
|
# § "3D".
|
|
14
14
|
#
|
|
15
|
-
# Recognises `class T < GraphQL::Schema::Object` subclasses
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
# `field` DSL emits NO Ruby methods (it just records a
|
|
24
|
-
# `Schema::Field` on the class's `own_fields`). The user writes
|
|
25
|
-
# resolver methods themselves; rigor's value here is producing a
|
|
26
|
-
# static type table downstream consumers can cross-reference.
|
|
15
|
+
# Recognises `class T < GraphQL::Schema::Object` subclasses and walks every `field :name, Type,
|
|
16
|
+
# null: false` declaration inside, publishing the resulting field-type map as the
|
|
17
|
+
# `:graphql_type_table` cross-plugin fact (ADR-9). The macro expansion library survey at
|
|
18
|
+
# docs/notes/20260515-macro-expansion-library-survey.md § "GraphQL-Ruby" documents WHY this is a
|
|
19
|
+
# pure metadata-recorder plugin rather than an ADR-16
|
|
20
|
+
# substrate consumer: graphql-ruby's `field` DSL emits NO Ruby methods (it just records a
|
|
21
|
+
# `Schema::Field` on the class's `own_fields`). The user writes resolver methods themselves; rigor's
|
|
22
|
+
# value here is producing a static type table downstream consumers can cross-reference.
|
|
27
23
|
#
|
|
28
24
|
# ## What downstream consumers DO with the published facts
|
|
29
25
|
#
|
|
30
|
-
# The tables are the substrate for two future capabilities
|
|
31
|
-
# (demand-driven, not yet implemented):
|
|
26
|
+
# The tables are the substrate for two future capabilities (demand-driven, not yet implemented):
|
|
32
27
|
#
|
|
33
|
-
# - Resolver-method check: for each `field :name, Type` whose
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
# plugin could type `Schema.execute(query).to_h` against the
|
|
38
|
-
# queried fields.
|
|
28
|
+
# - Resolver-method check: for each `field :name, Type` whose `name` is also defined as a Ruby method
|
|
29
|
+
# on the class, verify the method's return type matches `Type`'s underlying class.
|
|
30
|
+
# - Schema-query result typing: a future `rigor-graphql-execute` plugin could type
|
|
31
|
+
# `Schema.execute(query).to_h` against the queried fields.
|
|
39
32
|
#
|
|
40
33
|
# ## What's recognised
|
|
41
34
|
#
|
|
42
|
-
# - `class T < GraphQL::Schema::Object` subclasses (including
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
# and GraphQL→Ruby scalar mapping.
|
|
35
|
+
# - `class T < GraphQL::Schema::Object` subclasses (including nested namespaces); `field :name, Type,
|
|
36
|
+
# null: ...` declarations with constant-reference or list-array types and GraphQL→Ruby scalar
|
|
37
|
+
# mapping.
|
|
46
38
|
# - `class T < GraphQL::Schema::Enum`; `value "ACTIVE"` calls.
|
|
47
|
-
# - `class T < GraphQL::Schema::InputObject` /
|
|
48
|
-
# `GraphQL::Schema::Mutation`; `argument :name, Type,
|
|
39
|
+
# - `class T < GraphQL::Schema::InputObject` / `GraphQL::Schema::Mutation`; `argument :name, Type,
|
|
49
40
|
# required: ...` declarations.
|
|
50
41
|
# - No user-facing diagnostics yet.
|
|
51
42
|
#
|
|
52
43
|
# ## Deferred (demand-driven)
|
|
53
44
|
#
|
|
54
45
|
# - **`resolver:` / `mutation:` reroute** recognition.
|
|
55
|
-
# - **String type expressions** (`field :foo, "User"`) — defeats
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
# user at the constant-reference form for static typing.
|
|
46
|
+
# - **String type expressions** (`field :foo, "User"`) — defeats static resolution by design
|
|
47
|
+
# (graphql-ruby's `BuildType.parse_type` constantizes at runtime); a future slice could surface
|
|
48
|
+
# these as `graphql.string-type` `:info` diagnostics pointing the user at the constant-reference
|
|
49
|
+
# form for static typing.
|
|
60
50
|
class Graphql < Rigor::Plugin::Base
|
|
61
51
|
manifest(
|
|
62
52
|
id: "graphql",
|
|
@@ -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/graphql.rb` performs at load time.
|
|
3
|
+
# Gem entry point. Required by Rigor's plugin loader when `.rigor.yml` lists `rigor-graphql` 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/graphql.rb` performs at load time.
|
|
8
6
|
require_relative "rigor/plugin/graphql"
|
|
@@ -5,16 +5,12 @@ require "prism"
|
|
|
5
5
|
module Rigor
|
|
6
6
|
module Plugin
|
|
7
7
|
class Hanami < Rigor::Plugin::Base
|
|
8
|
-
# Checks the "presence" half of the Hanami::Action
|
|
9
|
-
# ADR-28 protocol contract.
|
|
8
|
+
# Checks the "presence" half of the Hanami::Action ADR-28 protocol contract.
|
|
10
9
|
#
|
|
11
|
-
# Every class defined in a file matching a contract's
|
|
12
|
-
# `
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
# is handled engine-side by
|
|
16
|
-
# `Inference::MethodParameterBinder`. Return type is
|
|
17
|
-
# void so no conformance check is performed here.
|
|
10
|
+
# Every class defined in a file matching a contract's `path_glob` must declare `#handle(request,
|
|
11
|
+
# response)`. The "provide" half — binding `Hanami::Action::Request` and `Hanami::Action::Response`
|
|
12
|
+
# into the method body — is handled engine-side by `Inference::MethodParameterBinder`. Return type
|
|
13
|
+
# is void so no conformance check is performed here.
|
|
18
14
|
class ActionChecker
|
|
19
15
|
FNMATCH_FLAGS = File::FNM_PATHNAME | File::FNM_EXTGLOB
|
|
20
16
|
|
|
@@ -22,11 +18,10 @@ module Rigor
|
|
|
22
18
|
@contracts = contracts
|
|
23
19
|
end
|
|
24
20
|
|
|
25
|
-
# Per-`ClassNode` check over the engine-owned walk (ADR-37):
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
# ships no `class_nodes` traversal of its own.
|
|
21
|
+
# Per-`ClassNode` check over the engine-owned walk (ADR-37): called once per class node the
|
|
22
|
+
# `node_rule` in `hanami.rb` dispatches, against every contract whose `path_glob` matches the
|
|
23
|
+
# file. No cross-class-node state is needed, so the checker ships no `class_nodes` traversal of
|
|
24
|
+
# its own.
|
|
30
25
|
def check_class(class_node, path:)
|
|
31
26
|
@contracts.filter_map do |contract|
|
|
32
27
|
next unless path_matches?(contract.path_glob, path)
|
|
@@ -42,9 +37,8 @@ module Rigor
|
|
|
42
37
|
|
|
43
38
|
private
|
|
44
39
|
|
|
45
|
-
# Contract globs are project-root-relative; the analyzer
|
|
46
|
-
#
|
|
47
|
-
# matched both directly and with a `**/`-prefixed suffix.
|
|
40
|
+
# Contract globs are project-root-relative; the analyzer may supply a relative or absolute path,
|
|
41
|
+
# so the glob is matched both directly and with a `**/`-prefixed suffix.
|
|
48
42
|
def path_matches?(glob, path)
|
|
49
43
|
return false if path.nil?
|
|
50
44
|
|
|
@@ -9,37 +9,28 @@ module Rigor
|
|
|
9
9
|
module Plugin
|
|
10
10
|
# rigor-hanami — enforces the Hanami::Action protocol.
|
|
11
11
|
#
|
|
12
|
-
# Hanami 3.x actions inherit from `Hanami::Action` and
|
|
13
|
-
# define a single entry-point:
|
|
12
|
+
# Hanami 3.x actions inherit from `Hanami::Action` and define a single entry-point:
|
|
14
13
|
#
|
|
15
14
|
# def handle(request, response)
|
|
16
15
|
# response.status = 200
|
|
17
16
|
# response.body = "Hello"
|
|
18
17
|
# end
|
|
19
18
|
#
|
|
20
|
-
# The method is void — the response is mutated in-place;
|
|
21
|
-
#
|
|
19
|
+
# The method is void — the response is mutated in-place; the return value is discarded by the
|
|
20
|
+
# framework.
|
|
22
21
|
#
|
|
23
|
-
# This plugin uses the **ADR-28 path-scoped
|
|
24
|
-
# method-protocol contract** to:
|
|
22
|
+
# This plugin uses the **ADR-28 path-scoped method-protocol contract** to:
|
|
25
23
|
#
|
|
26
|
-
# 1. **Provide** `Hanami::Action::Request` and
|
|
27
|
-
# `
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
# The engine substitutes these types for the usual
|
|
31
|
-
# `Dynamic[Top]` fallback, so misuse of `request` or
|
|
32
|
-
# `response` inside an action body surfaces as a
|
|
33
|
-
# core diagnostic.
|
|
24
|
+
# 1. **Provide** `Hanami::Action::Request` and `Hanami::Action::Response` as the types of the first
|
|
25
|
+
# and second parameters of every `#handle` method defined inside `app/actions/**/*.rb`. The
|
|
26
|
+
# engine substitutes these types for the usual `Dynamic[Top]` fallback, so misuse of `request` or
|
|
27
|
+
# `response` inside an action body surfaces as a core diagnostic.
|
|
34
28
|
#
|
|
35
|
-
# 2. **Check** that every class under `app/actions/`
|
|
36
|
-
# defines `#handle`. Missing definitions emit
|
|
29
|
+
# 2. **Check** that every class under `app/actions/` defines `#handle`. Missing definitions emit
|
|
37
30
|
# `missing-handle-method`.
|
|
38
31
|
#
|
|
39
|
-
# Return-type conformance is NOT checked — Hanami
|
|
40
|
-
#
|
|
41
|
-
# return would produce false positives on every `if`
|
|
42
|
-
# branch that doesn't end with an explicit `nil`.
|
|
32
|
+
# Return-type conformance is NOT checked — Hanami actions are void by contract, and checking a void
|
|
33
|
+
# return would produce false positives on every `if` branch that doesn't end with an explicit `nil`.
|
|
43
34
|
#
|
|
44
35
|
# ## Configuration
|
|
45
36
|
#
|
|
@@ -55,10 +46,8 @@ module Rigor
|
|
|
55
46
|
# | action class defines no `#handle` | `:error` | `missing-handle-method` |
|
|
56
47
|
# | `request` / `response` misuse in body | core engine diagnostic |
|
|
57
48
|
#
|
|
58
|
-
# A misused `request` or `response` (e.g.
|
|
59
|
-
# `
|
|
60
|
-
# engine `call.undefined-method` diagnostic once the
|
|
61
|
-
# plugin provides the parameter types.
|
|
49
|
+
# A misused `request` or `response` (e.g. `request.no_such_method`) surfaces as a standard engine
|
|
50
|
+
# `call.undefined-method` diagnostic once the plugin provides the parameter types.
|
|
62
51
|
class Hanami < Rigor::Plugin::Base
|
|
63
52
|
manifest(
|
|
64
53
|
id: "hanami",
|
|
@@ -91,20 +80,17 @@ module Rigor
|
|
|
91
80
|
end
|
|
92
81
|
end
|
|
93
82
|
|
|
94
|
-
# ADR-28: override so the per-project `action_path` config
|
|
95
|
-
#
|
|
96
|
-
# this plugin's check half.
|
|
83
|
+
# ADR-28: override so the per-project `action_path` config reaches both the engine's
|
|
84
|
+
# parameter-provision tier and this plugin's check half.
|
|
97
85
|
def protocol_contracts
|
|
98
86
|
@protocol_contracts || manifest.protocol_contracts
|
|
99
87
|
end
|
|
100
88
|
|
|
101
|
-
# ADR-37 — per-class-node validation over the engine-owned walk.
|
|
102
|
-
#
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
#
|
|
106
|
-
# `node_rule` is for — the return type is void, so no `scope`
|
|
107
|
-
# query is needed.)
|
|
89
|
+
# ADR-37 — per-class-node validation over the engine-owned walk. Each `Prism::ClassNode` is checked
|
|
90
|
+
# against every contract independently, so the plugin no longer ships its own `class_nodes`
|
|
91
|
+
# traversal; `ActionChecker#check_class` keeps the per-class contract logic. (A per-class contract
|
|
92
|
+
# check is exactly what `node_rule` is for — the return type is void, so no `scope` query is
|
|
93
|
+
# needed.)
|
|
108
94
|
node_rule Prism::ClassNode do |node, _scope, path|
|
|
109
95
|
contracts = protocol_contracts
|
|
110
96
|
next [] if contracts.empty?
|
|
@@ -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
|
|