rigortype 0.2.7 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/docs/manual/04-diagnostics.md +7 -4
- data/docs/manual/06-baseline.md +5 -2
- data/docs/manual/15-type-protection-coverage.md +6 -4
- data/lib/rigor/analysis/baseline.rb +56 -96
- data/lib/rigor/analysis/buffer_binding.rb +13 -20
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +29 -48
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +35 -54
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +23 -37
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +16 -23
- data/lib/rigor/analysis/check_rules/rule_walk.rb +48 -71
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +19 -28
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +48 -67
- data/lib/rigor/analysis/check_rules.rb +174 -274
- data/lib/rigor/analysis/dependency_recorder.rb +26 -38
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +13 -25
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +17 -30
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +15 -26
- data/lib/rigor/analysis/dependency_source_inference/index.rb +45 -76
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +23 -40
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +50 -83
- data/lib/rigor/analysis/dependency_source_inference.rb +6 -10
- data/lib/rigor/analysis/diagnostic.rb +48 -75
- data/lib/rigor/analysis/erb_template_detector.rb +13 -18
- data/lib/rigor/analysis/fact_store.rb +12 -22
- data/lib/rigor/analysis/incremental.rb +47 -63
- data/lib/rigor/analysis/incremental_session.rb +74 -100
- data/lib/rigor/analysis/project_scan.rb +12 -23
- data/lib/rigor/analysis/result.rb +3 -5
- data/lib/rigor/analysis/rule_catalog.rb +67 -95
- data/lib/rigor/analysis/run_stats.rb +38 -61
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +101 -160
- data/lib/rigor/analysis/runner/pool_coordinator.rb +121 -188
- data/lib/rigor/analysis/runner/project_pre_passes.rb +62 -96
- data/lib/rigor/analysis/runner/run_snapshots.rb +12 -18
- data/lib/rigor/analysis/runner.rb +220 -333
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +30 -43
- data/lib/rigor/analysis/worker_session.rb +64 -102
- data/lib/rigor/ast/type_node.rb +6 -8
- data/lib/rigor/ast.rb +6 -10
- data/lib/rigor/bleeding_edge.rb +27 -35
- data/lib/rigor/builtins/hkt_builtins.rb +7 -14
- data/lib/rigor/builtins/imported_refinements.rb +106 -149
- data/lib/rigor/builtins/predefined_constant_refinements.rb +51 -65
- data/lib/rigor/builtins/regex_refinement.rb +32 -43
- data/lib/rigor/builtins/static_return_refinements.rb +57 -81
- data/lib/rigor/cache/descriptor.rb +58 -94
- data/lib/rigor/cache/incremental_snapshot.rb +32 -48
- data/lib/rigor/cache/rbs_cache_producer.rb +10 -15
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +10 -16
- data/lib/rigor/cache/rbs_class_type_param_names.rb +10 -16
- data/lib/rigor/cache/rbs_constant_table.rb +8 -13
- data/lib/rigor/cache/rbs_descriptor.rb +8 -13
- data/lib/rigor/cache/rbs_environment.rb +11 -19
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +11 -19
- data/lib/rigor/cache/rbs_known_class_names.rb +8 -12
- data/lib/rigor/cache/store.rb +96 -151
- data/lib/rigor/cli/annotate_command.rb +51 -88
- data/lib/rigor/cli/baseline_command.rb +10 -17
- data/lib/rigor/cli/check_command.rb +121 -199
- data/lib/rigor/cli/check_runner_factory.rb +8 -14
- data/lib/rigor/cli/ci_detector.rb +9 -14
- data/lib/rigor/cli/command.rb +8 -14
- data/lib/rigor/cli/coverage_command.rb +19 -26
- data/lib/rigor/cli/coverage_mutation.rb +14 -19
- data/lib/rigor/cli/coverage_report.rb +0 -2
- data/lib/rigor/cli/coverage_scan.rb +7 -11
- data/lib/rigor/cli/diagnostic_formats.rb +43 -62
- data/lib/rigor/cli/diff_command.rb +10 -20
- data/lib/rigor/cli/docs_command.rb +26 -40
- data/lib/rigor/cli/doctor_command.rb +8 -9
- data/lib/rigor/cli/explain_command.rb +4 -7
- data/lib/rigor/cli/fused_protection_renderer.rb +4 -5
- data/lib/rigor/cli/fused_protection_report.rb +6 -8
- data/lib/rigor/cli/lsp_command.rb +11 -19
- data/lib/rigor/cli/mcp_command.rb +4 -6
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -4
- data/lib/rigor/cli/mutation_protection_report.rb +6 -9
- data/lib/rigor/cli/options.rb +11 -19
- data/lib/rigor/cli/plugin_command.rb +18 -30
- data/lib/rigor/cli/plugins_command.rb +29 -51
- data/lib/rigor/cli/plugins_renderer.rb +12 -20
- data/lib/rigor/cli/prism_colorizer.rb +13 -19
- data/lib/rigor/cli/protection_renderer.rb +5 -7
- data/lib/rigor/cli/protection_report.rb +27 -17
- data/lib/rigor/cli/renderable.rb +4 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +5 -8
- data/lib/rigor/cli/sig_gen_command.rb +14 -26
- data/lib/rigor/cli/skill_command.rb +30 -47
- data/lib/rigor/cli/skill_describe.rb +40 -64
- data/lib/rigor/cli/trace_command.rb +9 -16
- data/lib/rigor/cli/trace_renderer.rb +35 -51
- data/lib/rigor/cli/triage_command.rb +6 -10
- data/lib/rigor/cli/triage_renderer.rb +1 -2
- data/lib/rigor/cli/type_of_command.rb +14 -23
- data/lib/rigor/cli/type_of_renderer.rb +3 -5
- data/lib/rigor/cli/type_scan_command.rb +6 -10
- data/lib/rigor/cli/type_scan_renderer.rb +3 -5
- data/lib/rigor/cli/type_scan_report.rb +2 -3
- data/lib/rigor/cli/upgrade_command.rb +2 -3
- data/lib/rigor/cli.rb +16 -28
- data/lib/rigor/config_audit.rb +35 -44
- data/lib/rigor/configuration/dependencies.rb +41 -70
- data/lib/rigor/configuration/severity_profile.rb +28 -42
- data/lib/rigor/configuration.rb +150 -241
- data/lib/rigor/environment/bundle_sig_discovery.rb +74 -118
- data/lib/rigor/environment/class_registry.rb +19 -26
- data/lib/rigor/environment/constant_type_cache_holder.rb +14 -21
- data/lib/rigor/environment/hkt_registry_holder.rb +6 -12
- data/lib/rigor/environment/lockfile_resolver.rb +31 -47
- data/lib/rigor/environment/rbs_collection_discovery.rb +39 -63
- data/lib/rigor/environment/rbs_coverage_report.rb +24 -42
- data/lib/rigor/environment/rbs_loader.rb +420 -518
- data/lib/rigor/environment/reflection.rb +28 -53
- data/lib/rigor/environment/reporters.rb +13 -24
- data/lib/rigor/environment.rb +176 -291
- data/lib/rigor/flow_contribution/conflict.rb +16 -27
- data/lib/rigor/flow_contribution/element.rb +7 -12
- data/lib/rigor/flow_contribution/fact.rb +33 -57
- data/lib/rigor/flow_contribution/merge_result.rb +6 -9
- data/lib/rigor/flow_contribution/merger.rb +32 -47
- data/lib/rigor/flow_contribution.rb +37 -55
- data/lib/rigor/inference/acceptance.rb +133 -219
- data/lib/rigor/inference/block_parameter_binder.rb +52 -82
- data/lib/rigor/inference/body_fixpoint.rb +22 -30
- data/lib/rigor/inference/budget_trace.rb +43 -63
- data/lib/rigor/inference/builtins/array_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/comparable_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/complex_catalog.rb +13 -19
- data/lib/rigor/inference/builtins/date_catalog.rb +38 -67
- data/lib/rigor/inference/builtins/encoding_catalog.rb +22 -36
- data/lib/rigor/inference/builtins/enumerable_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/exception_catalog.rb +38 -57
- data/lib/rigor/inference/builtins/hash_catalog.rb +9 -14
- data/lib/rigor/inference/builtins/method_catalog.rb +29 -48
- data/lib/rigor/inference/builtins/numeric_catalog.rb +13 -18
- data/lib/rigor/inference/builtins/pathname_catalog.rb +8 -13
- data/lib/rigor/inference/builtins/proc_catalog.rb +38 -62
- data/lib/rigor/inference/builtins/random_catalog.rb +21 -32
- data/lib/rigor/inference/builtins/range_catalog.rb +12 -21
- data/lib/rigor/inference/builtins/rational_catalog.rb +9 -15
- data/lib/rigor/inference/builtins/re_catalog.rb +29 -48
- data/lib/rigor/inference/builtins/set_catalog.rb +18 -27
- data/lib/rigor/inference/builtins/string_catalog.rb +12 -19
- data/lib/rigor/inference/builtins/struct_catalog.rb +18 -30
- data/lib/rigor/inference/builtins/time_catalog.rb +28 -44
- data/lib/rigor/inference/closure_escape_analyzer.rb +44 -67
- data/lib/rigor/inference/coverage_scanner.rb +13 -18
- data/lib/rigor/inference/def_return_typer.rb +8 -14
- data/lib/rigor/inference/dynamic_origin.rb +23 -20
- data/lib/rigor/inference/expression_typer.rb +774 -1152
- data/lib/rigor/inference/fallback.rb +8 -12
- data/lib/rigor/inference/fallback_tracer.rb +4 -10
- data/lib/rigor/inference/flow_tracer.rb +25 -36
- data/lib/rigor/inference/hkt_body.rb +45 -68
- data/lib/rigor/inference/hkt_body_parser.rb +33 -52
- data/lib/rigor/inference/hkt_reducer.rb +38 -59
- data/lib/rigor/inference/hkt_registry.rb +50 -76
- data/lib/rigor/inference/indexed_narrowing.rb +55 -81
- data/lib/rigor/inference/macro_block_self_type.rb +21 -34
- data/lib/rigor/inference/method_dispatcher/array_to_h_folding.rb +11 -18
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +49 -83
- data/lib/rigor/inference/method_dispatcher/call_context.rb +16 -23
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +13 -22
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +307 -454
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +24 -34
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +22 -37
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +52 -81
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +51 -78
- data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +60 -97
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +23 -34
- data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +18 -26
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +37 -71
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +114 -182
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +122 -193
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +15 -24
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +64 -93
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +31 -45
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +6 -9
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +356 -511
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +22 -32
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +15 -21
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +66 -82
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +10 -15
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +9 -13
- data/lib/rigor/inference/method_dispatcher.rb +378 -524
- data/lib/rigor/inference/method_parameter_binder.rb +81 -114
- data/lib/rigor/inference/multi_target_binder.rb +51 -68
- data/lib/rigor/inference/mutation_widening.rb +115 -158
- data/lib/rigor/inference/narrowing.rb +509 -727
- data/lib/rigor/inference/origin_lookup.rb +38 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +100 -100
- data/lib/rigor/inference/precision_scanner.rb +26 -35
- data/lib/rigor/inference/project_patched_methods.rb +20 -32
- data/lib/rigor/inference/project_patched_scanner.rb +23 -37
- data/lib/rigor/inference/protection_scanner.rb +16 -18
- data/lib/rigor/inference/rbs_type_translator.rb +49 -66
- data/lib/rigor/inference/scope_indexer.rb +473 -817
- data/lib/rigor/inference/statement_evaluator.rb +706 -1111
- data/lib/rigor/inference/struct_fold_safety.rb +34 -48
- data/lib/rigor/inference/synthetic_method.rb +9 -16
- data/lib/rigor/inference/synthetic_method_index.rb +20 -35
- data/lib/rigor/inference/synthetic_method_scanner.rb +63 -103
- data/lib/rigor/language_server/buffer_resolution.rb +6 -9
- data/lib/rigor/language_server/buffer_table.rb +11 -18
- data/lib/rigor/language_server/completion_provider.rb +69 -116
- data/lib/rigor/language_server/debouncer.rb +16 -25
- data/lib/rigor/language_server/diagnostic_publisher.rb +26 -40
- data/lib/rigor/language_server/document_symbol_provider.rb +14 -19
- data/lib/rigor/language_server/folding_range_provider.rb +11 -19
- data/lib/rigor/language_server/hover_provider.rb +8 -11
- data/lib/rigor/language_server/hover_renderer.rb +56 -93
- data/lib/rigor/language_server/loop.rb +14 -19
- data/lib/rigor/language_server/project_context.rb +46 -74
- data/lib/rigor/language_server/selection_range_provider.rb +9 -12
- data/lib/rigor/language_server/server.rb +55 -83
- data/lib/rigor/language_server/signature_help_provider.rb +29 -46
- data/lib/rigor/language_server/synchronized_writer.rb +4 -7
- data/lib/rigor/language_server/uri.rb +8 -13
- data/lib/rigor/language_server.rb +4 -6
- data/lib/rigor/mcp/loop.rb +2 -3
- data/lib/rigor/mcp/server.rb +4 -7
- data/lib/rigor/mcp.rb +3 -6
- data/lib/rigor/plugin/access_denied_error.rb +5 -8
- data/lib/rigor/plugin/additional_initializer.rb +21 -31
- data/lib/rigor/plugin/base.rb +335 -518
- data/lib/rigor/plugin/blueprint.rb +14 -23
- data/lib/rigor/plugin/box.rb +18 -29
- data/lib/rigor/plugin/fact_store.rb +16 -26
- data/lib/rigor/plugin/inflector.rb +37 -53
- data/lib/rigor/plugin/io_boundary.rb +33 -56
- data/lib/rigor/plugin/isolation.rb +42 -55
- data/lib/rigor/plugin/load_error.rb +10 -15
- data/lib/rigor/plugin/loader.rb +30 -49
- data/lib/rigor/plugin/macro/block_as_method.rb +20 -32
- data/lib/rigor/plugin/macro/heredoc_template.rb +35 -58
- data/lib/rigor/plugin/macro/nested_class_template.rb +22 -36
- data/lib/rigor/plugin/macro/trait_registry.rb +34 -51
- data/lib/rigor/plugin/macro.rb +10 -15
- data/lib/rigor/plugin/manifest.rb +85 -144
- data/lib/rigor/plugin/node_context.rb +14 -22
- data/lib/rigor/plugin/node_rule_walk.rb +49 -74
- data/lib/rigor/plugin/protocol_contract.rb +25 -39
- data/lib/rigor/plugin/registry.rb +132 -205
- data/lib/rigor/plugin/services.rb +21 -33
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -19
- data/lib/rigor/plugin/trust_policy.rb +24 -38
- data/lib/rigor/plugin/type_node_resolver.rb +15 -24
- data/lib/rigor/protection/diagnostic_oracle.rb +11 -13
- data/lib/rigor/protection/mutation_scanner.rb +27 -35
- data/lib/rigor/protection/mutator.rb +50 -70
- data/lib/rigor/protection/test_suite_oracle.rb +20 -27
- data/lib/rigor/rbs_extended/conformance_checker.rb +48 -68
- data/lib/rigor/rbs_extended/hkt_directives.rb +28 -54
- data/lib/rigor/rbs_extended/reporter.rb +24 -40
- data/lib/rigor/rbs_extended.rb +107 -197
- data/lib/rigor/reflection.rb +68 -86
- data/lib/rigor/scope/discovery_index.rb +14 -19
- data/lib/rigor/scope.rb +255 -310
- data/lib/rigor/sig_gen/classification.rb +6 -10
- data/lib/rigor/sig_gen/generator.rb +197 -323
- data/lib/rigor/sig_gen/layout_index.rb +12 -20
- data/lib/rigor/sig_gen/method_candidate.rb +12 -17
- data/lib/rigor/sig_gen/observation_collector.rb +38 -70
- data/lib/rigor/sig_gen/observed_call.rb +13 -23
- data/lib/rigor/sig_gen/path_mapper.rb +17 -29
- data/lib/rigor/sig_gen/renderer.rb +7 -13
- data/lib/rigor/sig_gen/type_elaborator.rb +15 -28
- data/lib/rigor/sig_gen/write_result.rb +8 -16
- data/lib/rigor/sig_gen/writer.rb +95 -174
- data/lib/rigor/sig_gen.rb +3 -6
- data/lib/rigor/signature_path_audit.rb +24 -30
- data/lib/rigor/source/constant_path.rb +10 -14
- data/lib/rigor/source/literals.rb +31 -45
- data/lib/rigor/source/node_locator.rb +9 -11
- data/lib/rigor/source/node_walker.rb +9 -13
- data/lib/rigor/source.rb +3 -4
- data/lib/rigor/testing.rb +16 -20
- data/lib/rigor/triage/catalogue.rb +38 -62
- data/lib/rigor/triage.rb +31 -52
- data/lib/rigor/trinary.rb +9 -13
- data/lib/rigor/type/acceptance_router.rb +4 -6
- data/lib/rigor/type/accepts_result.rb +10 -14
- data/lib/rigor/type/app.rb +19 -27
- data/lib/rigor/type/bot.rb +4 -6
- data/lib/rigor/type/bound_method.rb +10 -15
- data/lib/rigor/type/combinator.rb +165 -257
- data/lib/rigor/type/constant.rb +23 -34
- data/lib/rigor/type/data_class.rb +10 -15
- data/lib/rigor/type/data_instance.rb +14 -20
- data/lib/rigor/type/difference.rb +21 -32
- data/lib/rigor/type/dynamic.rb +3 -5
- data/lib/rigor/type/hash_shape.rb +32 -18
- data/lib/rigor/type/integer_range.rb +11 -16
- data/lib/rigor/type/intersection.rb +27 -42
- data/lib/rigor/type/nominal.rb +10 -15
- data/lib/rigor/type/plain_lattice.rb +9 -13
- data/lib/rigor/type/refined.rb +67 -114
- data/lib/rigor/type/singleton.rb +4 -6
- data/lib/rigor/type/struct_class.rb +11 -16
- data/lib/rigor/type/struct_instance.rb +15 -21
- data/lib/rigor/type/tuple.rb +14 -19
- data/lib/rigor/type/union.rb +30 -42
- data/lib/rigor/type_node/generic.rb +14 -26
- data/lib/rigor/type_node/identifier.rb +12 -19
- data/lib/rigor/type_node.rb +3 -12
- data/lib/rigor/value_semantics.rb +16 -21
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +18 -30
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +16 -29
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +12 -23
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +22 -39
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +26 -46
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +51 -87
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +10 -17
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +26 -39
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +118 -176
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +30 -51
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +57 -92
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +71 -108
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +13 -22
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +15 -25
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +8 -13
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +16 -24
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +30 -47
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +79 -129
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +36 -57
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +27 -41
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +16 -24
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +148 -235
- data/plugins/rigor-activerecord/lib/rigor-activerecord.rb +3 -5
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +8 -14
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +9 -15
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +4 -7
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +23 -41
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +3 -5
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +6 -10
- data/plugins/rigor-activesupport-core-ext/lib/rigor-activesupport-core-ext.rb +8 -11
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +30 -45
- data/plugins/rigor-devise/lib/rigor-devise.rb +3 -5
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +43 -69
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +26 -35
- data/plugins/rigor-dry-schema/lib/rigor-dry-schema.rb +3 -5
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +30 -47
- data/plugins/rigor-dry-struct/lib/rigor-dry-struct.rb +3 -5
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +67 -113
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +29 -46
- data/plugins/rigor-dry-types/lib/rigor-dry-types.rb +3 -5
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -20
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +14 -23
- data/plugins/rigor-dry-validation/lib/rigor-dry-validation.rb +2 -3
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +22 -37
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +28 -46
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +13 -17
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +30 -47
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +52 -80
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +21 -31
- data/plugins/rigor-graphql/lib/rigor-graphql.rb +3 -5
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +11 -17
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +20 -34
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +45 -75
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +22 -36
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +22 -41
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +17 -32
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +6 -12
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +4 -8
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +17 -32
- data/plugins/rigor-rails/lib/rigor-rails.rb +11 -18
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +58 -92
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +20 -34
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +20 -31
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +49 -82
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +55 -92
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +51 -90
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +17 -30
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +38 -70
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +42 -74
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +280 -497
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +54 -89
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +32 -54
- data/plugins/rigor-rbs-inline/lib/rigor-rbs-inline.rb +8 -13
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +9 -16
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +24 -39
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +13 -22
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +34 -56
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +22 -42
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +43 -75
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +27 -39
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +14 -19
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +20 -34
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +28 -47
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +21 -37
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +13 -22
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +18 -31
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +6 -10
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +14 -29
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +23 -41
- data/plugins/rigor-sinatra/lib/rigor-sinatra.rb +3 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +23 -38
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +57 -96
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +22 -35
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +41 -69
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +6 -10
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +24 -41
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +29 -50
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +55 -88
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +159 -262
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +21 -29
- data/plugins/rigor-statesman/lib/rigor-statesman.rb +3 -5
- data/plugins/rigor-typescript-utility-types/lib/rigor/plugin/typescript_utility_types.rb +23 -37
- data/plugins/rigor-typescript-utility-types/lib/rigor-typescript-utility-types.rb +4 -6
- data/sig/rigor/scope.rbs +6 -0
- metadata +2 -1
|
@@ -4,8 +4,7 @@ require "rigor/plugin"
|
|
|
4
4
|
|
|
5
5
|
module Rigor
|
|
6
6
|
module Plugin
|
|
7
|
-
# ADR-16 Tier A worked plugin: recognises Sinatra's class-
|
|
8
|
-
# level route DSL.
|
|
7
|
+
# ADR-16 Tier A worked plugin: recognises Sinatra's class-level route DSL.
|
|
9
8
|
#
|
|
10
9
|
# Sinatra's modular style:
|
|
11
10
|
#
|
|
@@ -20,52 +19,35 @@ module Rigor
|
|
|
20
19
|
# end
|
|
21
20
|
# end
|
|
22
21
|
#
|
|
23
|
-
# At runtime `Sinatra::Base#generate_method`
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
# `self : Nominal[MyApp]`, so bare identifiers (`params`,
|
|
30
|
-
# `redirect`, `halt`, `session`, `headers`, `content_type`,
|
|
31
|
-
# `body`, `status`, `erb`, …) resolve through
|
|
32
|
-
# `Sinatra::Base`'s RBS via rigor's normal inference path.
|
|
22
|
+
# At runtime `Sinatra::Base#generate_method` (`lib/sinatra/base.rb:1788-1793`) does
|
|
23
|
+
# `define_method(name, &block); remove_method`, turning each block into a real instance method of the
|
|
24
|
+
# user's app class. The substrate's Tier A hook (`Rigor::Inference::MacroBlockSelfType`) replays the
|
|
25
|
+
# same contract statically: the block runs with `self : Nominal[MyApp]`, so bare identifiers
|
|
26
|
+
# (`params`, `redirect`, `halt`, `session`, `headers`, `content_type`, `body`, `status`, `erb`, …)
|
|
27
|
+
# resolve through `Sinatra::Base`'s RBS via rigor's normal inference path.
|
|
33
28
|
#
|
|
34
29
|
# ## Reach
|
|
35
30
|
#
|
|
36
|
-
# All nine class-level HTTP verb methods Sinatra exposes:
|
|
37
|
-
# `
|
|
38
|
-
# `
|
|
39
|
-
#
|
|
40
|
-
# (classic top-level style, when used via `class App <
|
|
41
|
-
# Sinatra::Application`) match because the receiver
|
|
42
|
-
# constraint accepts every subclass.
|
|
31
|
+
# All nine class-level HTTP verb methods Sinatra exposes: `get`, `post`, `put`, `delete`, `head`,
|
|
32
|
+
# `options`, `patch`, `link`, `unlink` (`lib/sinatra/base.rb:1531-1553`). Both modular-style subclasses
|
|
33
|
+
# of `Sinatra::Base` and `Sinatra::Application` (classic top-level style, when used via `class App <
|
|
34
|
+
# Sinatra::Application`) match because the receiver constraint accepts every subclass.
|
|
43
35
|
#
|
|
44
36
|
# ## What the plugin does NOT do (yet)
|
|
45
37
|
#
|
|
46
|
-
# - **Routing diagnostics.** Path uniqueness, conflict
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
# - **
|
|
53
|
-
# `
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
# `get '/path' do ... end` at the top of a script (no
|
|
58
|
-
# enclosing `class < Sinatra::Base`) is the classic-mode
|
|
59
|
-
# pattern (`lib/sinatra/main.rb`). Tier A as currently
|
|
60
|
-
# wired requires the receiver's class to be visible at
|
|
61
|
-
# the call site; a top-level call's receiver is the
|
|
62
|
-
# classic-mode `Sinatra::Application`, which the
|
|
63
|
-
# `Sinatra::Delegator` mixin forwards from `main`. The
|
|
64
|
-
# classic style is deferred until the demand justifies
|
|
65
|
-
# the extra match shape.
|
|
38
|
+
# - **Routing diagnostics.** Path uniqueness, conflict detection, named-route reverse lookup — none of
|
|
39
|
+
# these are in slice 1c's scope.
|
|
40
|
+
# - **Custom helpers.** `helpers do ... end` blocks that inject module methods into the app's instance
|
|
41
|
+
# namespace are Tier C / Tier B work, not Tier A.
|
|
42
|
+
# - **Configure / settings.** `configure do ... end` and `set :session_secret, "..."` are settings
|
|
43
|
+
# DSL, not route DSL — handled by separate substrate entries when demand surfaces.
|
|
44
|
+
# - **Classic-style top-level routes.** A bare `get '/path' do ... end` at the top of a script (no
|
|
45
|
+
# enclosing `class < Sinatra::Base`) is the classic-mode pattern (`lib/sinatra/main.rb`). Tier A as
|
|
46
|
+
# currently wired requires the receiver's class to be visible at the call site; a top-level call's
|
|
47
|
+
# receiver is the classic-mode `Sinatra::Application`, which the `Sinatra::Delegator` mixin forwards
|
|
48
|
+
# from `main`. The classic style is deferred until the demand justifies the extra match shape.
|
|
66
49
|
#
|
|
67
|
-
# See `plugins/rigor-sinatra/README.md` for usage and the
|
|
68
|
-
# demo script under `plugins/rigor-sinatra/demo/`.
|
|
50
|
+
# See `plugins/rigor-sinatra/README.md` for usage and the demo script under `plugins/rigor-sinatra/demo/`.
|
|
69
51
|
class Sinatra < Rigor::Plugin::Base
|
|
70
52
|
manifest(
|
|
71
53
|
id: "sinatra",
|
|
@@ -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/sinatra.rb` performs at load time.
|
|
3
|
+
# Gem entry point. Required by Rigor's plugin loader when `.rigor.yml` lists `rigor-sinatra` under
|
|
4
|
+
# `plugins:`. The loader expects this `require` to side-effect a call to `Rigor::Plugin.register`, which
|
|
5
|
+
# the body of `lib/rigor/plugin/sinatra.rb` performs at load time.
|
|
8
6
|
require_relative "rigor/plugin/sinatra"
|
|
@@ -7,11 +7,9 @@ require_relative "type_translator"
|
|
|
7
7
|
module Rigor
|
|
8
8
|
module Plugin
|
|
9
9
|
class Sorbet < Rigor::Plugin::Base
|
|
10
|
-
# Slice 6 of ADR-11 — recognises `T.absurd(x)` calls and
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
# statically unreachable; it's the standard Sorbet idiom
|
|
14
|
-
# for case/when exhaustiveness:
|
|
10
|
+
# Slice 6 of ADR-11 — recognises `T.absurd(x)` calls and composes them with the engine's
|
|
11
|
+
# flow-sensitive narrowing. `T.absurd` asserts that a code branch is statically unreachable; it's
|
|
12
|
+
# the standard Sorbet idiom for case/when exhaustiveness:
|
|
15
13
|
#
|
|
16
14
|
# case x
|
|
17
15
|
# when A then ...
|
|
@@ -20,33 +18,23 @@ module Rigor
|
|
|
20
18
|
# T.absurd(x)
|
|
21
19
|
# end
|
|
22
20
|
#
|
|
23
|
-
# If every case has been handled, `x` at the `else` branch
|
|
24
|
-
#
|
|
25
|
-
# and the assertion
|
|
26
|
-
# narrows to whatever's left and the assertion is wrong —
|
|
27
|
-
# we surface that mistake as `plugin.sorbet.absurd-reachable`.
|
|
21
|
+
# If every case has been handled, `x` at the `else` branch has been narrowed to `T.noreturn`
|
|
22
|
+
# (Rigor's `Type::Bot`) and the assertion holds. If the user forgot a case, `x` narrows to whatever's
|
|
23
|
+
# left and the assertion is wrong — we surface that mistake as `plugin.sorbet.absurd-reachable`.
|
|
28
24
|
#
|
|
29
25
|
# ## Two-phase mechanism
|
|
30
26
|
#
|
|
31
|
-
# The recogniser is invoked from the plugin's `dynamic_return` rule
|
|
32
|
-
#
|
|
33
|
-
# context. The rule:
|
|
27
|
+
# The recogniser is invoked from the plugin's `dynamic_return` rule where the per-node `scope:`
|
|
28
|
+
# carries the proper narrowing context. The rule:
|
|
34
29
|
#
|
|
35
|
-
# - Contributes a `bot` return type regardless of
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
# node in
|
|
43
|
-
# `diagnostics_for_file` later walks the AST for
|
|
44
|
-
# `T.absurd` calls and emits a
|
|
45
|
-
# `plugin.sorbet.absurd-reachable` warning at every
|
|
46
|
-
# call_node whose object identity matches the recorded
|
|
47
|
-
# set. We rely on the runner only parsing each file
|
|
48
|
-
# once per run, so the same Prism node object is seen
|
|
49
|
-
# in both the `dynamic_return` rule and
|
|
30
|
+
# - Contributes a `bot` return type regardless of reachability (faithful to `T.absurd`'s runtime
|
|
31
|
+
# behaviour: it always raises). This lets the engine's existing flow analysis treat code after
|
|
32
|
+
# `T.absurd` as unreachable, matching what users of Sorbet expect.
|
|
33
|
+
# - When the branch is REACHABLE (the discriminant's type isn't `bot`), the recogniser also records
|
|
34
|
+
# the call node in a per-plugin set. The plugin's `diagnostics_for_file` later walks the AST for
|
|
35
|
+
# `T.absurd` calls and emits a `plugin.sorbet.absurd-reachable` warning at every call_node whose
|
|
36
|
+
# object identity matches the recorded set. We rely on the runner only parsing each file once per
|
|
37
|
+
# run, so the same Prism node object is seen in both the `dynamic_return` rule and
|
|
50
38
|
# `diagnostics_for_file`.
|
|
51
39
|
module AbsurdRecognizer
|
|
52
40
|
# @param call_node [Prism::CallNode]
|
|
@@ -56,19 +44,17 @@ module Rigor
|
|
|
56
44
|
return false unless call_node.name == :absurd
|
|
57
45
|
return false unless TypeTranslator.sorbet_t_namespaced?(call_node.receiver)
|
|
58
46
|
|
|
59
|
-
# Slice 6 only handles single-argument `T.absurd(x)`;
|
|
60
|
-
#
|
|
61
|
-
# Sorbet's level too.
|
|
47
|
+
# Slice 6 only handles single-argument `T.absurd(x)`; no-arg / multi-arg shapes are syntax
|
|
48
|
+
# errors at Sorbet's level too.
|
|
62
49
|
arguments = call_node.arguments&.arguments
|
|
63
50
|
arguments&.size == 1
|
|
64
51
|
end
|
|
65
52
|
|
|
66
53
|
# @param call_node [Prism::CallNode]
|
|
67
54
|
# @param scope [Rigor::Scope, nil]
|
|
68
|
-
# @return [Boolean] true when the discriminant has been
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
# `absurd-reachable` diagnostic in this case.
|
|
55
|
+
# @return [Boolean] true when the discriminant has been narrowed to `bot` (the branch is
|
|
56
|
+
# unreachable, so `T.absurd` is correct). The caller suppresses the `absurd-reachable` diagnostic
|
|
57
|
+
# in this case.
|
|
72
58
|
def self.exhaustive?(call_node, scope)
|
|
73
59
|
return false if scope.nil?
|
|
74
60
|
|
|
@@ -76,9 +62,8 @@ module Rigor
|
|
|
76
62
|
arg_type = scope.type_of(arg)
|
|
77
63
|
arg_type.equal?(Rigor::Type::Bot.instance) || arg_type.is_a?(Rigor::Type::Bot)
|
|
78
64
|
rescue StandardError
|
|
79
|
-
# On synthetic / unrecognised nodes the typer may
|
|
80
|
-
#
|
|
81
|
-
# diagnostic fires conservatively.
|
|
65
|
+
# On synthetic / unrecognised nodes the typer may raise; treat as "can't prove unreachable" so
|
|
66
|
+
# the diagnostic fires conservatively.
|
|
82
67
|
false
|
|
83
68
|
end
|
|
84
69
|
end
|
|
@@ -7,13 +7,10 @@ require_relative "type_translator"
|
|
|
7
7
|
module Rigor
|
|
8
8
|
module Plugin
|
|
9
9
|
class Sorbet < Rigor::Plugin::Base
|
|
10
|
-
# Lifts Sorbet's type-assertion calls (`T.let`, `T.cast`,
|
|
11
|
-
# `T.
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# `must_because` / `reveal_type` light follow-up extends
|
|
15
|
-
# the same module rather than splitting into a parallel
|
|
16
|
-
# recogniser.
|
|
10
|
+
# Lifts Sorbet's type-assertion calls (`T.let`, `T.cast`, `T.must`, `T.must_because`, `T.unsafe`,
|
|
11
|
+
# `T.reveal_type`) into `FlowContribution` return-type contributions. ADR-11 slice 2 covered the
|
|
12
|
+
# original four; the `must_because` / `reveal_type` light follow-up extends the same module rather
|
|
13
|
+
# than splitting into a parallel recogniser.
|
|
17
14
|
#
|
|
18
15
|
# | Sorbet form | Contribution |
|
|
19
16
|
# | ---------------------------- | --------------------------------------- |
|
|
@@ -26,54 +23,39 @@ module Rigor
|
|
|
26
23
|
# | `T.assert_type!(expr, T)` | return type ← translated `T` + static subtype check |
|
|
27
24
|
# | `T.bind(self, T)` | return type ← `Constant[nil]` + post_return_fact narrowing self to translated `T` |
|
|
28
25
|
#
|
|
29
|
-
# The Sorbet runtime's `T.let` / `T.cast` actually return
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
# site's downstream uses see that type. This matches what
|
|
35
|
-
# `%a{rigor:v1:assert: x is T}` would do for an assignment
|
|
36
|
-
# in the surrounding scope.
|
|
26
|
+
# The Sorbet runtime's `T.let` / `T.cast` actually return the inner expression unchanged at runtime;
|
|
27
|
+
# their job is purely to *assert* a static type. From Rigor's static perspective the simplest
|
|
28
|
+
# faithful translation is "the call's return type IS the asserted type" — the call site's downstream
|
|
29
|
+
# uses see that type. This matches what `%a{rigor:v1:assert: x is T}` would do for an assignment in
|
|
30
|
+
# the surrounding scope.
|
|
37
31
|
#
|
|
38
|
-
# `T.must_because` is `T.must` with a second-argument
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
# recogniser dispatches through the same path.
|
|
32
|
+
# `T.must_because` is `T.must` with a second-argument string explanation. The static behaviour is
|
|
33
|
+
# identical to `T.must` — strip `nil` from the inferred type — so the recogniser dispatches through
|
|
34
|
+
# the same path.
|
|
42
35
|
#
|
|
43
|
-
# `T.reveal_type` is "diagnostic-only" in Sorbet: it
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
# `T.reveal_type` wrapper weren't there); the plugin's
|
|
49
|
-
# `diagnostics_for_file` hook surfaces the
|
|
50
|
-
# `plugin.sorbet.reveal-type` `:info` message for human
|
|
51
|
-
# consumption.
|
|
36
|
+
# `T.reveal_type` is "diagnostic-only" in Sorbet: it passes the value through unchanged at runtime
|
|
37
|
+
# AND surfaces the inferred static type as a build-time message. The recogniser contributes the
|
|
38
|
+
# inferred type (so chained call sites still resolve as if the `T.reveal_type` wrapper weren't
|
|
39
|
+
# there); the plugin's `diagnostics_for_file` hook surfaces the `plugin.sorbet.reveal-type` `:info`
|
|
40
|
+
# message for human consumption.
|
|
52
41
|
#
|
|
53
|
-
# `T.bind(self, T)` is recognised as block-scope self
|
|
54
|
-
#
|
|
55
|
-
# `
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
# a literal `Prism::SelfNode` — Sorbet rejects other
|
|
60
|
-
# receivers and the recogniser mirrors that. The runtime
|
|
61
|
-
# call returns nil, so the static return type is
|
|
62
|
-
# `Constant[nil]`.
|
|
42
|
+
# `T.bind(self, T)` is recognised as block-scope self narrowing. The recogniser returns a
|
|
43
|
+
# contribution whose `post_return_facts` carries a `Fact(target_kind: :self)` so the engine's
|
|
44
|
+
# `apply_self_post_return_fact` narrows `scope.self_type` for the surrounding scope (in a block
|
|
45
|
+
# body, the rest of the block). The first argument MUST be a literal `Prism::SelfNode` — Sorbet
|
|
46
|
+
# rejects other receivers and the recogniser mirrors that. The runtime call returns nil, so the
|
|
47
|
+
# static return type is `Constant[nil]`.
|
|
63
48
|
module AssertionRecognizer
|
|
64
|
-
# Method names this recogniser claims as Sorbet
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
# inside an analysed file always resolves through this
|
|
68
|
-
# module.
|
|
49
|
+
# Method names this recogniser claims as Sorbet assertions. The plugin checks call sites against
|
|
50
|
+
# this set before any catalog lookup so a `T.let` call inside an analysed file always resolves
|
|
51
|
+
# through this module.
|
|
69
52
|
SORBET_ASSERTIONS = %i[let cast must must_because unsafe reveal_type assert_type! bind].freeze
|
|
70
53
|
|
|
71
54
|
module_function
|
|
72
55
|
|
|
73
56
|
# @param call_node [Prism::CallNode]
|
|
74
57
|
# @param scope [Rigor::Scope]
|
|
75
|
-
# @param plugin_id [String] used for the contribution's
|
|
76
|
-
# `provenance.source_family`.
|
|
58
|
+
# @param plugin_id [String] used for the contribution's `provenance.source_family`.
|
|
77
59
|
# @return [Rigor::FlowContribution, nil]
|
|
78
60
|
def recognize(call_node:, scope:, plugin_id:)
|
|
79
61
|
return nil unless TypeTranslator.sorbet_t_namespaced?(call_node.receiver)
|
|
@@ -97,20 +79,15 @@ module Rigor
|
|
|
97
79
|
end
|
|
98
80
|
end
|
|
99
81
|
|
|
100
|
-
# `T.bind(self, T)` recognition. Sorbet rejects any
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
# through silently. The contribution carries:
|
|
82
|
+
# `T.bind(self, T)` recognition. Sorbet rejects any non-`self` receiver argument, and the
|
|
83
|
+
# recogniser mirrors that — calls like `T.bind(other, X)` fall through silently. The contribution
|
|
84
|
+
# carries:
|
|
104
85
|
#
|
|
105
|
-
# - `return_type: Constant[nil]` — Sorbet's runtime
|
|
106
|
-
# `T.bind` returns nil; chained calls would be a
|
|
86
|
+
# - `return_type: Constant[nil]` — Sorbet's runtime `T.bind` returns nil; chained calls would be a
|
|
107
87
|
# bug, but the typing stays accurate.
|
|
108
|
-
# - `post_return_facts: [Fact(target_kind: :self,
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
# `scope.self_type` for the surrounding scope. In a
|
|
112
|
-
# block body, that scope is the block's own, so the
|
|
113
|
-
# narrowing applies to the rest of the block —
|
|
88
|
+
# - `post_return_facts: [Fact(target_kind: :self, type: T)]` — the engine's
|
|
89
|
+
# `apply_self_post_return_fact` narrows `scope.self_type` for the surrounding scope. In a block
|
|
90
|
+
# body, that scope is the block's own, so the narrowing applies to the rest of the block —
|
|
114
91
|
# matching Sorbet's documented contract.
|
|
115
92
|
def recognize_bind(call_node, plugin_id)
|
|
116
93
|
first_arg = nth_argument(call_node, 0)
|
|
@@ -137,15 +114,11 @@ module Rigor
|
|
|
137
114
|
)
|
|
138
115
|
end
|
|
139
116
|
|
|
140
|
-
# `T.assert_type!(expr, T)` shares the typed-assertion
|
|
141
|
-
#
|
|
142
|
-
#
|
|
143
|
-
#
|
|
144
|
-
#
|
|
145
|
-
# `assert_type!` from `cast` lives in the plugin's
|
|
146
|
-
# `diagnostics_for_file` hook (mirroring the
|
|
147
|
-
# absurd-recognizer pattern: record the call here, emit
|
|
148
|
-
# the diagnostic from the per-file walker).
|
|
117
|
+
# `T.assert_type!(expr, T)` shares the typed-assertion contribution shape with `T.cast` (return is
|
|
118
|
+
# the asserted type), so the recogniser delegates the return-type half through
|
|
119
|
+
# `resolve_typed_assertion`. The static subtype check that distinguishes `assert_type!` from
|
|
120
|
+
# `cast` lives in the plugin's `diagnostics_for_file` hook (mirroring the absurd-recognizer
|
|
121
|
+
# pattern: record the call here, emit the diagnostic from the per-file walker).
|
|
149
122
|
def assert_type_check(call_node, scope)
|
|
150
123
|
return nil if scope.nil?
|
|
151
124
|
|
|
@@ -162,14 +135,11 @@ module Rigor
|
|
|
162
135
|
nil
|
|
163
136
|
end
|
|
164
137
|
|
|
165
|
-
# `T.reveal_type(expr)` returns `expr` unchanged at
|
|
166
|
-
#
|
|
167
|
-
#
|
|
168
|
-
#
|
|
169
|
-
#
|
|
170
|
-
# inferred type. The companion diagnostic is emitted by
|
|
171
|
-
# the plugin's `diagnostics_for_file` hook; this
|
|
172
|
-
# method handles the contribution only.
|
|
138
|
+
# `T.reveal_type(expr)` returns `expr` unchanged at runtime; the Sorbet-side semantics is "make
|
|
139
|
+
# the inferred static type visible to the user." The contribution mirrors `T.must` minus the
|
|
140
|
+
# nil-stripping: the call's return type is the inner expression's inferred type. The companion
|
|
141
|
+
# diagnostic is emitted by the plugin's `diagnostics_for_file` hook; this method handles the
|
|
142
|
+
# contribution only.
|
|
173
143
|
def resolve_reveal_type(call_node, scope)
|
|
174
144
|
inner = nth_argument(call_node, 0)
|
|
175
145
|
return Rigor::Type::Combinator.untyped if inner.nil? || scope.nil?
|
|
@@ -177,17 +147,14 @@ module Rigor
|
|
|
177
147
|
inner_type = scope.type_of(inner)
|
|
178
148
|
inner_type || Rigor::Type::Combinator.untyped
|
|
179
149
|
rescue StandardError
|
|
180
|
-
# Synthetic / virtual nodes can raise from
|
|
181
|
-
# `scope.type_of`; degrade gracefully so the dispatcher
|
|
150
|
+
# Synthetic / virtual nodes can raise from `scope.type_of`; degrade gracefully so the dispatcher
|
|
182
151
|
# can still proceed with a benign untyped envelope.
|
|
183
152
|
Rigor::Type::Combinator.untyped
|
|
184
153
|
end
|
|
185
154
|
|
|
186
|
-
# `T.let(expr, T)` and `T.cast(expr, T)` share the same
|
|
187
|
-
#
|
|
188
|
-
#
|
|
189
|
-
# we don't try to verify it at runtime; that's `srb tc`'s
|
|
190
|
-
# job and is out of scope per ADR-11.
|
|
155
|
+
# `T.let(expr, T)` and `T.cast(expr, T)` share the same 2-argument shape: `arguments[1]` is the
|
|
156
|
+
# type expression. The first argument is opaque to slice 2 — we don't try to verify it at runtime;
|
|
157
|
+
# that's `srb tc`'s job and is out of scope per ADR-11.
|
|
191
158
|
def resolve_typed_assertion(call_node)
|
|
192
159
|
type_arg = nth_argument(call_node, 1)
|
|
193
160
|
return nil if type_arg.nil?
|
|
@@ -195,12 +162,9 @@ module Rigor
|
|
|
195
162
|
TypeTranslator.translate(type_arg)
|
|
196
163
|
end
|
|
197
164
|
|
|
198
|
-
# `T.must(expr)` strips `nil` from `expr`'s inferred
|
|
199
|
-
#
|
|
200
|
-
# `
|
|
201
|
-
# untyped envelope when the inferred shape is itself
|
|
202
|
-
# `Dynamic[top]` or when no scope is available
|
|
203
|
-
# (synthetic / virtual-node call sites).
|
|
165
|
+
# `T.must(expr)` strips `nil` from `expr`'s inferred type. The call's target type is therefore
|
|
166
|
+
# `inferred(expr) - Constant[nil]`. Falls back to the untyped envelope when the inferred shape is
|
|
167
|
+
# itself `Dynamic[top]` or when no scope is available (synthetic / virtual-node call sites).
|
|
204
168
|
def resolve_must(call_node, scope)
|
|
205
169
|
inner = nth_argument(call_node, 0)
|
|
206
170
|
return nil if inner.nil? || scope.nil?
|
|
@@ -210,18 +174,15 @@ module Rigor
|
|
|
210
174
|
|
|
211
175
|
strip_nil(inner_type)
|
|
212
176
|
rescue StandardError
|
|
213
|
-
# `scope.type_of` may raise on synthetic nodes; degrade
|
|
214
|
-
#
|
|
177
|
+
# `scope.type_of` may raise on synthetic nodes; degrade to "no contribution" rather than crash
|
|
178
|
+
# the dispatcher.
|
|
215
179
|
nil
|
|
216
180
|
end
|
|
217
181
|
|
|
218
|
-
# Removes `nil` (`Constant[nil]`) from `type` using the
|
|
219
|
-
# `Difference`
|
|
220
|
-
#
|
|
221
|
-
#
|
|
222
|
-
# removed value, but the simple form here is good enough
|
|
223
|
-
# for slice 2; the precise normalisation lands when the
|
|
224
|
-
# Difference carrier gets full algebraic support.
|
|
182
|
+
# Removes `nil` (`Constant[nil]`) from `type` using the `Difference` carrier. Idempotent on shapes
|
|
183
|
+
# that don't contain nil — the resulting `Difference[base, removed]` collapses to `base` if `base`
|
|
184
|
+
# already excludes the removed value, but the simple form here is good enough for slice 2; the
|
|
185
|
+
# precise normalisation lands when the Difference carrier gets full algebraic support.
|
|
225
186
|
def strip_nil(type)
|
|
226
187
|
Rigor::Type::Combinator.difference(
|
|
227
188
|
type, Rigor::Type::Combinator.constant_of(nil)
|
|
@@ -3,28 +3,22 @@
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module Plugin
|
|
5
5
|
class Sorbet < Rigor::Plugin::Base
|
|
6
|
-
# Per-run table of method signatures keyed by the
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# by the plugin's `dynamic_return` rule at every gated call site.
|
|
6
|
+
# Per-run table of method signatures keyed by the `(class_name, method_name, kind)` triple. Built by
|
|
7
|
+
# {CatalogWalker} during the plugin's lazy pre-walk; read by the plugin's `dynamic_return` rule at
|
|
8
|
+
# every gated call site.
|
|
10
9
|
#
|
|
11
|
-
# The catalog is mutable while it is being built, then
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# incrementally as the walker visits each project file —
|
|
15
|
-
# but consumers MUST treat the catalog as read-only.
|
|
10
|
+
# The catalog is mutable while it is being built, then frozen via {#freeze!} before the first read.
|
|
11
|
+
# Construction mutability is intentional — slice 1 builds the catalog incrementally as the walker
|
|
12
|
+
# visits each project file — but consumers MUST treat the catalog as read-only.
|
|
16
13
|
class Catalog
|
|
17
|
-
# Frozen empty bucket reused for classes that have no
|
|
18
|
-
#
|
|
19
|
-
# every `mixins_for` query.
|
|
14
|
+
# Frozen empty bucket reused for classes that have no recorded mixins. Avoids allocating a fresh
|
|
15
|
+
# Hash on every `mixins_for` query.
|
|
20
16
|
EMPTY_MIXINS = { include: [].freeze, extend: [].freeze }.freeze
|
|
21
17
|
|
|
22
18
|
def initialize
|
|
23
19
|
@entries = {}
|
|
24
|
-
# ADR-11 slice 8 — per-class mixin declarations
|
|
25
|
-
#
|
|
26
|
-
# traversal lifts sigs declared on a mixed-in
|
|
27
|
-
# module to call sites on the host class.
|
|
20
|
+
# ADR-11 slice 8 — per-class mixin declarations collected by `CatalogWalker`. Lookup-time chain
|
|
21
|
+
# traversal lifts sigs declared on a mixed-in module to call sites on the host class.
|
|
28
22
|
@mixins = {}
|
|
29
23
|
@frozen_after_build = false
|
|
30
24
|
end
|
|
@@ -37,13 +31,11 @@ module Rigor
|
|
|
37
31
|
@entries[key] = signature
|
|
38
32
|
end
|
|
39
33
|
|
|
40
|
-
# @param class_name [String] the class / module that
|
|
41
|
-
#
|
|
42
|
-
# records under `"Post"`).
|
|
34
|
+
# @param class_name [String] the class / module that carries the mixin (`class Post; include Foo;
|
|
35
|
+
# end` records under `"Post"`).
|
|
43
36
|
# @param kind [:include, :extend]
|
|
44
|
-
# @param module_name [String] the textual name of the
|
|
45
|
-
#
|
|
46
|
-
# extend site (`"Foo"`, `"Foo::Bar"`, `"::Foo"`).
|
|
37
|
+
# @param module_name [String] the textual name of the mixed-in module as it appeared at the
|
|
38
|
+
# include / extend site (`"Foo"`, `"Foo::Bar"`, `"::Foo"`).
|
|
47
39
|
def record_mixin(class_name:, kind:, module_name:)
|
|
48
40
|
raise "Catalog already finalised" if @frozen_after_build
|
|
49
41
|
|
|
@@ -69,9 +61,8 @@ module Rigor
|
|
|
69
61
|
end
|
|
70
62
|
|
|
71
63
|
# @param class_name [String]
|
|
72
|
-
# @return [Hash{Symbol => Array<String>}] frozen mapping
|
|
73
|
-
#
|
|
74
|
-
# {EMPTY_MIXINS} when no mixins were recorded.
|
|
64
|
+
# @return [Hash{Symbol => Array<String>}] frozen mapping `{ include: [...], extend: [...] }`.
|
|
65
|
+
# Returns {EMPTY_MIXINS} when no mixins were recorded.
|
|
75
66
|
def mixins_for(class_name)
|
|
76
67
|
@mixins[class_name] || EMPTY_MIXINS
|
|
77
68
|
end
|
|
@@ -80,16 +71,12 @@ module Rigor
|
|
|
80
71
|
@entries.empty?
|
|
81
72
|
end
|
|
82
73
|
|
|
83
|
-
# ADR-52 slice 4 — the distinct method names the catalog
|
|
84
|
-
#
|
|
85
|
-
#
|
|
86
|
-
# `
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
# precise `(class, kind)` lookup stays in the rule block.
|
|
90
|
-
# Computed fresh per call — the plugin memoises the
|
|
91
|
-
# resolved set, and `freeze!` freezes the catalog itself
|
|
92
|
-
# so a lazy memo ivar here would raise.
|
|
74
|
+
# ADR-52 slice 4 — the distinct method names the catalog carries at least one signature for,
|
|
75
|
+
# across every `(class_name, kind)` owner. Feeds the plugin's run-time `dynamic_return methods:`
|
|
76
|
+
# name gate: the engine only consults the plugin for a call whose name appears here (or in the
|
|
77
|
+
# static assertion vocabulary), and the precise `(class, kind)` lookup stays in the rule block.
|
|
78
|
+
# Computed fresh per call — the plugin memoises the resolved set, and `freeze!` freezes the
|
|
79
|
+
# catalog itself so a lazy memo ivar here would raise.
|
|
93
80
|
#
|
|
94
81
|
# @return [Array<Symbol>]
|
|
95
82
|
def method_names
|