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
|
@@ -14,46 +14,33 @@ require_relative "sorbet/sigil_detector"
|
|
|
14
14
|
|
|
15
15
|
module Rigor
|
|
16
16
|
module Plugin
|
|
17
|
-
# rigor-sorbet — ingests Sorbet `sig { ... }` blocks as
|
|
18
|
-
#
|
|
17
|
+
# rigor-sorbet — ingests Sorbet `sig { ... }` blocks as method-signature contributions to Rigor's
|
|
18
|
+
# analyzer.
|
|
19
19
|
#
|
|
20
20
|
# ADR-11 slice 1 — first deliverable. Recognises:
|
|
21
21
|
#
|
|
22
|
-
# - `sig { params(x: Integer).returns(String) }` above a
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
# - `class Foo` / `module Foo::Bar` / `class << self`
|
|
29
|
-
# nesting; `def self.foo` is recognised as a singleton
|
|
30
|
-
# method.
|
|
22
|
+
# - `sig { params(x: Integer).returns(String) }` above a `def foo(x)` definition, contributing the
|
|
23
|
+
# parsed return type at every call site.
|
|
24
|
+
# - The `void` terminus and the `abstract` / `override` / `overridable` / `final` modifiers (recorded
|
|
25
|
+
# on the {MethodSignature} for slice ≥2).
|
|
26
|
+
# - `class Foo` / `module Foo::Bar` / `class << self` nesting; `def self.foo` is recognised as a
|
|
27
|
+
# singleton method.
|
|
31
28
|
#
|
|
32
|
-
# The {TypeTranslator} table documents coverage. Most of
|
|
33
|
-
#
|
|
34
|
-
# `T::Struct` subclasses, `T.attached_class`, etc.) degrade
|
|
35
|
-
# silently to `Dynamic[top]`.
|
|
29
|
+
# The {TypeTranslator} table documents coverage. Most of Sorbet's vocabulary translates; remaining
|
|
30
|
+
# gaps (`T.proc`, `T::Struct` subclasses, `T.attached_class`, etc.) degrade silently to `Dynamic[top]`.
|
|
36
31
|
#
|
|
37
|
-
# Architecture: per-run `Catalog` is built lazily on first
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
# `
|
|
45
|
-
# `(class_name, method_name, kind)`. When a key collides
|
|
46
|
-
# across files, the last-walked sig wins (ordering is
|
|
47
|
-
# platform-dependent: `Dir.glob` returns directory entries
|
|
48
|
-
# in filesystem order). Sorbet's full shim-override
|
|
49
|
-
# semantics — `sorbet/rbi/shims/` overriding
|
|
50
|
-
# `sorbet/rbi/gems/` — lands in a later slice once the
|
|
51
|
-
# catalog gains per-source provenance.
|
|
32
|
+
# Architecture: per-run `Catalog` is built lazily on first access by walking every configured `paths:`
|
|
33
|
+
# entry's `.rb` files plus every `rbi_paths:` entry's `.rbi` files (slice 4) via the plugin's
|
|
34
|
+
# `IoBoundary`. The catalog is frozen after the first build and consulted by the `dynamic_return` rule
|
|
35
|
+
# at every gated call site. RBI files share the catalog with project-source sigs — both produce
|
|
36
|
+
# `MethodSignature` entries keyed by `(class_name, method_name, kind)`. When a key collides across
|
|
37
|
+
# files, the last-walked sig wins (ordering is platform-dependent: `Dir.glob` returns directory
|
|
38
|
+
# entries in filesystem order). Sorbet's full shim-override semantics — `sorbet/rbi/shims/` overriding
|
|
39
|
+
# `sorbet/rbi/gems/` — lands in a later slice once the catalog gains per-source provenance.
|
|
52
40
|
#
|
|
53
|
-
# The plugin emits `plugin.sorbet.parse-error` warnings for
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
# not followed by a def) but never aborts a run.
|
|
41
|
+
# The plugin emits `plugin.sorbet.parse-error` warnings for malformed sig blocks (no block / empty
|
|
42
|
+
# block / no `returns` or `void` terminus / two consecutive sigs / sig not followed by a def) but
|
|
43
|
+
# never aborts a run.
|
|
57
44
|
#
|
|
58
45
|
# ## Configuration
|
|
59
46
|
#
|
|
@@ -63,27 +50,23 @@ module Rigor
|
|
|
63
50
|
# paths: ["lib", "app"] # directories to scan for `.rb` sigs; defaults to `paths:`
|
|
64
51
|
# rbi_paths: ["sorbet/rbi"] # directories to scan for `.rbi` files; default shown
|
|
65
52
|
#
|
|
66
|
-
# The `paths:` config key narrows the plugin's `.rb` walk;
|
|
67
|
-
#
|
|
68
|
-
# `
|
|
69
|
-
#
|
|
70
|
-
# layout (`gems/`, `annotations/`, `dsl/`, `shims/`). Set
|
|
71
|
-
# to `[]` to opt out of RBI loading entirely.
|
|
53
|
+
# The `paths:` config key narrows the plugin's `.rb` walk; omit it to inherit the project-wide
|
|
54
|
+
# `paths:` value. The `rbi_paths:` key controls where Sorbet's RBI tree is read from — defaults to
|
|
55
|
+
# `sorbet/rbi/` per Tapioca's standard layout (`gems/`, `annotations/`, `dsl/`, `shims/`). Set to `[]`
|
|
56
|
+
# to opt out of RBI loading entirely.
|
|
72
57
|
class Sorbet < Rigor::Plugin::Base
|
|
73
58
|
manifest(
|
|
74
59
|
id: "sorbet",
|
|
75
60
|
version: "0.1.0",
|
|
76
61
|
description: "Ingests Sorbet `sig` blocks as method-signature contributions.",
|
|
77
62
|
config_schema: {
|
|
78
|
-
# `paths` keeps a `fetch`-with-default in `init` because its
|
|
79
|
-
#
|
|
80
|
-
# static literal the manifest can declare.
|
|
63
|
+
# `paths` keeps a `fetch`-with-default in `init` because its default is dynamic (the project's
|
|
64
|
+
# configured `paths`), not a static literal the manifest can declare.
|
|
81
65
|
"paths" => :array,
|
|
82
|
-
# Default RBI directory tree. Matches the layout `tapioca init`
|
|
83
|
-
#
|
|
84
|
-
#
|
|
85
|
-
#
|
|
86
|
-
# picked up as a side effect of recursing into the parent root.
|
|
66
|
+
# Default RBI directory tree. Matches the layout `tapioca init` generates — see Sorbet's
|
|
67
|
+
# `rbi.md`. Slice 4 walks every `.rbi` file under these roots recursively; the four standard
|
|
68
|
+
# Tapioca subdirectories (`gems` / `annotations` / `dsl` / `shims`) are picked up as a side
|
|
69
|
+
# effect of recursing into the parent root.
|
|
87
70
|
"rbi_paths" => { kind: :array, default: ["sorbet/rbi"] },
|
|
88
71
|
"enforce_sigil" => { kind: :boolean, default: true }
|
|
89
72
|
}
|
|
@@ -93,97 +76,70 @@ module Rigor
|
|
|
93
76
|
@services = services
|
|
94
77
|
@configured_paths = Array(config.fetch("paths", services.configuration.paths)).map(&:to_s)
|
|
95
78
|
@rbi_paths = Array(config.fetch("rbi_paths")).map(&:to_s)
|
|
96
|
-
# Schema default `true` — only files marked `# typed: true` /
|
|
97
|
-
#
|
|
98
|
-
#
|
|
99
|
-
# sigil (current behaviour pre-this-config).
|
|
79
|
+
# Schema default `true` — only files marked `# typed: true` / `:strict` / `:strong` contribute
|
|
80
|
+
# their sigs. Set to `false` to record every file's sigs regardless of sigil (current behaviour
|
|
81
|
+
# pre-this-config).
|
|
100
82
|
@enforce_sigil = config.fetch("enforce_sigil")
|
|
101
|
-
# Per-call-site assertion gating: `@sigil_by_path` (built
|
|
102
|
-
#
|
|
103
|
-
# `
|
|
104
|
-
#
|
|
105
|
-
#
|
|
106
|
-
# gate is open everywhere. Missing-sigil paths (synthetic
|
|
107
|
-
# fixtures, out-of-tree call sites) default to enforced —
|
|
108
|
-
# failing-open suits spec ergonomics better than
|
|
109
|
-
# failing-closed. See `assertion_enforced_here?`.
|
|
83
|
+
# Per-call-site assertion gating: `@sigil_by_path` (built during catalog harvest) is consulted so
|
|
84
|
+
# `T.let` / `T.cast` / `T.must` / `T.bind` / `T.assert_type!` only fire in files Sorbet itself
|
|
85
|
+
# would enforce (`:true` / `:strict` / `:strong`). With `enforce_sigil: false` the gate is open
|
|
86
|
+
# everywhere. Missing-sigil paths (synthetic fixtures, out-of-tree call sites) default to enforced
|
|
87
|
+
# — failing-open suits spec ergonomics better than failing-closed. See `assertion_enforced_here?`.
|
|
110
88
|
@sigil_by_path = {}
|
|
111
89
|
@catalog = nil
|
|
112
90
|
@parse_errors_by_path = {}
|
|
113
91
|
@catalog_built = false
|
|
114
|
-
# ADR-11 slice 6 — Prism nodes for `T.absurd` calls
|
|
115
|
-
#
|
|
116
|
-
#
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
# warnings. Hash is keyed on the Prism node's
|
|
120
|
-
# `object_id` because the runner only parses each file
|
|
121
|
-
# once per run, so identity is stable across the two
|
|
122
|
-
# plugin hooks.
|
|
92
|
+
# ADR-11 slice 6 — Prism nodes for `T.absurd` calls we observed in the `dynamic_return` rule to be
|
|
93
|
+
# *reachable* (i.e., their discriminant didn't narrow to `bot`). `diagnostics_for_file` walks the
|
|
94
|
+
# per-file AST and surfaces these as `plugin.sorbet.absurd-reachable` warnings. Hash is keyed on
|
|
95
|
+
# the Prism node's `object_id` because the runner only parses each file once per run, so identity
|
|
96
|
+
# is stable across the two plugin hooks.
|
|
123
97
|
@reachable_absurd_nodes = {}.compare_by_identity
|
|
124
|
-
# ADR-11 light follow-up — `T.reveal_type` calls
|
|
125
|
-
#
|
|
126
|
-
#
|
|
127
|
-
# Mirrors the absurd-node compare-by-identity hash;
|
|
128
|
-
# `diagnostics_for_file` surfaces each entry as a
|
|
98
|
+
# ADR-11 light follow-up — `T.reveal_type` calls observed in the `dynamic_return` rule, paired
|
|
99
|
+
# with the display string for the inferred type at the call site. Mirrors the absurd-node
|
|
100
|
+
# compare-by-identity hash; `diagnostics_for_file` surfaces each entry as a
|
|
129
101
|
# `plugin.sorbet.reveal-type` `:info` diagnostic.
|
|
130
102
|
@reveal_type_calls = {}.compare_by_identity
|
|
131
|
-
# T.bind / T.assert_type! priority slice 1 —
|
|
132
|
-
# `
|
|
133
|
-
#
|
|
134
|
-
#
|
|
135
|
-
#
|
|
136
|
-
# `diagnostics_for_file` walks the file AST for
|
|
137
|
-
# `T.assert_type!` calls and surfaces matching entries
|
|
138
|
-
# as `plugin.sorbet.assert-type-mismatch` `:error`
|
|
139
|
-
# diagnostics.
|
|
103
|
+
# T.bind / T.assert_type! priority slice 1 — `T.assert_type!` calls observed in the
|
|
104
|
+
# `dynamic_return` rule whose static subtype check FAILED, paired with the inferred + asserted
|
|
105
|
+
# type display strings. Same compare-by-identity discipline. `diagnostics_for_file` walks the
|
|
106
|
+
# file AST for `T.assert_type!` calls and surfaces matching entries as
|
|
107
|
+
# `plugin.sorbet.assert-type-mismatch` `:error` diagnostics.
|
|
140
108
|
@assert_type_mismatches = {}.compare_by_identity
|
|
141
109
|
end
|
|
142
110
|
|
|
143
111
|
def diagnostics_for_file(path:, scope:, root:) # rubocop:disable Lint/UnusedMethodArgument
|
|
144
112
|
ensure_catalog
|
|
145
|
-
# The catalog records errors under the canonicalised
|
|
146
|
-
#
|
|
147
|
-
# symlink-bearing form here. Look up under both so the
|
|
148
|
-
# match is symlink-agnostic.
|
|
113
|
+
# The catalog records errors under the canonicalised (realpath-resolved) form; the runner may pass
|
|
114
|
+
# the symlink-bearing form here. Look up under both so the match is symlink-agnostic.
|
|
149
115
|
errors = @parse_errors_by_path[path] || @parse_errors_by_path[canonicalize(path)] || []
|
|
150
116
|
errors.map { |error| parse_error_diagnostic(path, error) }
|
|
151
117
|
end
|
|
152
118
|
|
|
153
|
-
# ADR-52 slice 4 — per-call return-type path via the
|
|
154
|
-
#
|
|
155
|
-
#
|
|
156
|
-
#
|
|
157
|
-
#
|
|
158
|
-
#
|
|
159
|
-
# resolved Symbol Set. The gate is a safe over-approximation —
|
|
160
|
-
# a project method merely *named* `cast` or `find` passes it
|
|
161
|
-
# and is declined by the block's own `T.`-receiver / catalog
|
|
162
|
-
# checks.
|
|
119
|
+
# ADR-52 slice 4 — per-call return-type path via the method-name-gated `dynamic_return` DSL. The
|
|
120
|
+
# recognised name set is only known at run time (the catalog's `def` names come from the lazy
|
|
121
|
+
# catalog build), so it is declared as a callable: the engine `instance_exec`s it once per run on
|
|
122
|
+
# first dispatch (always after `#prepare`) and memoises the resolved Symbol Set. The gate is a safe
|
|
123
|
+
# over-approximation — a project method merely *named* `cast` or `find` passes it and is declined by
|
|
124
|
+
# the block's own `T.`-receiver / catalog checks.
|
|
163
125
|
dynamic_return methods: -> { recognised_method_names } do |call_node, scope|
|
|
164
126
|
contribution_return_type(call_node, scope)
|
|
165
127
|
end
|
|
166
128
|
|
|
167
|
-
# ADR-52 slice 4 — `T.bind(self, T)`'s self-narrowing fact,
|
|
168
|
-
#
|
|
169
|
-
#
|
|
170
|
-
#
|
|
171
|
-
# `dynamic_return` rule above; the block re-checks the `T.`
|
|
172
|
-
# receiver via the recogniser, so an unrelated `bind` call
|
|
173
|
-
# contributes nothing.
|
|
129
|
+
# ADR-52 slice 4 — `T.bind(self, T)`'s self-narrowing fact, contributed via the method-gated
|
|
130
|
+
# `narrowing_facts` DSL. The statement evaluator consults this path for narrowing facts. The
|
|
131
|
+
# return-type half (`Constant[nil]`) flows through the `dynamic_return` rule above; the block
|
|
132
|
+
# re-checks the `T.` receiver via the recogniser, so an unrelated `bind` call contributes nothing.
|
|
174
133
|
narrowing_facts methods: [:bind] do |call_node, scope|
|
|
175
134
|
bind_post_return_facts(call_node, scope)
|
|
176
135
|
end
|
|
177
136
|
|
|
178
|
-
# ADR-37 — the three per-call diagnostics ride the engine-owned
|
|
179
|
-
#
|
|
180
|
-
#
|
|
181
|
-
#
|
|
182
|
-
#
|
|
183
|
-
# the
|
|
184
|
-
# `delete` both gates the emission and pops the entry so a re-run
|
|
185
|
-
# cannot double-fire. The recorded set is the gate — no per-node
|
|
186
|
-
# `AbsurdRecognizer` / name check is needed here.
|
|
137
|
+
# ADR-37 — the three per-call diagnostics ride the engine-owned walk instead of three hand-rolled
|
|
138
|
+
# `walk_for_*` recursions. Each candidate `T.` call is *recorded by object identity* during the
|
|
139
|
+
# inference pass (the `dynamic_return` / `narrowing_facts` rules above call `record_*`), so by the
|
|
140
|
+
# time these node rules fire in the diagnostics phase the sets are populated; the membership
|
|
141
|
+
# `delete` both gates the emission and pops the entry so a re-run cannot double-fire. The recorded
|
|
142
|
+
# set is the gate — no per-node `AbsurdRecognizer` / name check is needed here.
|
|
187
143
|
node_rule Prism::CallNode do |node, _scope, path|
|
|
188
144
|
next [] unless @reachable_absurd_nodes.delete(node)
|
|
189
145
|
|
|
@@ -206,10 +162,8 @@ module Rigor
|
|
|
206
162
|
|
|
207
163
|
private
|
|
208
164
|
|
|
209
|
-
# Run-time method-name gate for the `dynamic_return` rule
|
|
210
|
-
# (
|
|
211
|
-
# (`T.let` / `T.cast` / …), `T.absurd`, and every method name
|
|
212
|
-
# the catalog carries a sig for.
|
|
165
|
+
# Run-time method-name gate for the `dynamic_return` rule (ADR-52 slice 4): the static assertion
|
|
166
|
+
# vocabulary (`T.let` / `T.cast` / …), `T.absurd`, and every method name the catalog carries a sig for.
|
|
213
167
|
def recognised_method_names
|
|
214
168
|
ensure_catalog
|
|
215
169
|
names = AssertionRecognizer::SORBET_ASSERTIONS.dup
|
|
@@ -218,54 +172,39 @@ module Rigor
|
|
|
218
172
|
names
|
|
219
173
|
end
|
|
220
174
|
|
|
221
|
-
# Main contribution body for the `dynamic_return` rule.
|
|
222
|
-
#
|
|
223
|
-
# Resolves the receiver in three passes:
|
|
175
|
+
# Main contribution body for the `dynamic_return` rule. Returns the bare `Rigor::Type` the contract
|
|
176
|
+
# expects. Resolves the receiver in three passes:
|
|
224
177
|
#
|
|
225
|
-
# 1. Constant receiver (`User.find(...)`) → singleton-side
|
|
226
|
-
#
|
|
227
|
-
#
|
|
228
|
-
#
|
|
229
|
-
#
|
|
230
|
-
# 3. Implicit-self (receiver-less inside a method body) →
|
|
231
|
-
# current-class lookup via `implicit_self_lookup`.
|
|
178
|
+
# 1. Constant receiver (`User.find(...)`) → singleton-side catalog lookup.
|
|
179
|
+
# 2. Nominal receiver-type (`user.name` where `user`'s inferred type is `Nominal["User"]`) →
|
|
180
|
+
# instance-side catalog lookup.
|
|
181
|
+
# 3. Implicit-self (receiver-less inside a method body) → current-class lookup via
|
|
182
|
+
# `implicit_self_lookup`.
|
|
232
183
|
def contribution_return_type(call_node, scope)
|
|
233
184
|
return nil unless call_node.is_a?(Prism::CallNode)
|
|
234
185
|
|
|
235
|
-
# ADR-11 slice 6 — `T.absurd(x)` exhaustiveness. Always
|
|
236
|
-
#
|
|
237
|
-
#
|
|
238
|
-
#
|
|
239
|
-
#
|
|
240
|
-
#
|
|
241
|
-
# only `return_type` survives the merge). When the
|
|
242
|
-
# discriminant *isn't* narrowed to `bot` at this scope, also
|
|
243
|
-
# records the call node so `diagnostics_for_file` can surface
|
|
244
|
-
# a `plugin.sorbet.absurd-reachable` warning.
|
|
186
|
+
# ADR-11 slice 6 — `T.absurd(x)` exhaustiveness. Always contributes a `bot` return (matches
|
|
187
|
+
# Sorbet's runtime behaviour: `T.absurd` raises, so its value type is `bot` and the engine's flow
|
|
188
|
+
# analysis treats code after it as unreachable; the legacy contribution's `exceptional: :raises`
|
|
189
|
+
# slot was never consumed on the dispatcher path — only `return_type` survives the merge). When
|
|
190
|
+
# the discriminant *isn't* narrowed to `bot` at this scope, also records the call node so
|
|
191
|
+
# `diagnostics_for_file` can surface a `plugin.sorbet.absurd-reachable` warning.
|
|
245
192
|
if AbsurdRecognizer.absurd_call?(call_node)
|
|
246
193
|
@reachable_absurd_nodes[call_node] = true unless AbsurdRecognizer.exhaustive?(call_node, scope)
|
|
247
194
|
return Rigor::Type::Combinator.bot
|
|
248
195
|
end
|
|
249
196
|
|
|
250
|
-
# ADR-11 slice 2 — `T.let` / `T.cast` / `T.must` /
|
|
251
|
-
#
|
|
252
|
-
#
|
|
253
|
-
#
|
|
254
|
-
#
|
|
255
|
-
# follow-up extends the recogniser to `T.must_because`
|
|
256
|
-
# (alias of `T.must`) and `T.reveal_type` (passes the
|
|
257
|
-
# type through; the human-facing diagnostic is recorded
|
|
258
|
-
# here for `diagnostics_for_file` to emit).
|
|
197
|
+
# ADR-11 slice 2 — `T.let` / `T.cast` / `T.must` / `T.unsafe` are checked first because they're
|
|
198
|
+
# cheaper to recognise (no catalog walk required) and they win over any cataloged signature: the
|
|
199
|
+
# user explicitly asserted the type at the call site. The light follow-up extends the recogniser
|
|
200
|
+
# to `T.must_because` (alias of `T.must`) and `T.reveal_type` (passes the type through; the
|
|
201
|
+
# human-facing diagnostic is recorded here for `diagnostics_for_file` to emit).
|
|
259
202
|
#
|
|
260
|
-
# Per-call-site sigil gating: with `enforce_sigil: true`
|
|
261
|
-
#
|
|
262
|
-
#
|
|
263
|
-
#
|
|
264
|
-
#
|
|
265
|
-
# through the next tier as if the wrapper weren't
|
|
266
|
-
# there. The catalog tier already gates by sigil at
|
|
267
|
-
# harvest time; this closes the matching gap for
|
|
268
|
-
# caller-side recognition.
|
|
203
|
+
# Per-call-site sigil gating: with `enforce_sigil: true` (default), assertions only fire in files
|
|
204
|
+
# Sorbet itself would enforce. Files at `# typed: false` (or sigil-less, which Sorbet treats as
|
|
205
|
+
# `:false`) skip the assertion path entirely so the dispatcher continues through the next tier as
|
|
206
|
+
# if the wrapper weren't there. The catalog tier already gates by sigil at harvest time; this
|
|
207
|
+
# closes the matching gap for caller-side recognition.
|
|
269
208
|
ensure_catalog
|
|
270
209
|
if assertion_enforced_here?(scope)
|
|
271
210
|
assertion = AssertionRecognizer.recognize(
|
|
@@ -283,10 +222,9 @@ module Rigor
|
|
|
283
222
|
lookup_signature(call_node, scope)&.return_type
|
|
284
223
|
end
|
|
285
224
|
|
|
286
|
-
# The `narrowing_facts` body for `T.bind` — same sigil gate as
|
|
287
|
-
# the
|
|
288
|
-
#
|
|
289
|
-
# narrows `scope.self_type` for the rest of the block).
|
|
225
|
+
# The `narrowing_facts` body for `T.bind` — same sigil gate as the return-type path, then the
|
|
226
|
+
# recogniser's `post_return_facts` (the `Fact(target_kind: :self)` that narrows `scope.self_type`
|
|
227
|
+
# for the rest of the block).
|
|
290
228
|
def bind_post_return_facts(call_node, scope)
|
|
291
229
|
return nil unless call_node.is_a?(Prism::CallNode)
|
|
292
230
|
|
|
@@ -299,24 +237,16 @@ module Rigor
|
|
|
299
237
|
contribution&.post_return_facts
|
|
300
238
|
end
|
|
301
239
|
|
|
302
|
-
# Per-call-site assertion gating (ADR-11). With
|
|
303
|
-
# `enforce_sigil:
|
|
304
|
-
# `
|
|
305
|
-
# sigil must reach `:true` / `:strict` / `:strong` for
|
|
306
|
-
# assertions to fire. Three honest fallbacks:
|
|
240
|
+
# Per-call-site assertion gating (ADR-11). With `enforce_sigil: false` the gate is fully open. With
|
|
241
|
+
# `enforce_sigil: true` (default), the caller file's sigil must reach `:true` / `:strict` /
|
|
242
|
+
# `:strong` for assertions to fire. Three honest fallbacks:
|
|
307
243
|
#
|
|
308
|
-
# - `scope.source_path` is nil — synthetic call sites
|
|
309
|
-
#
|
|
310
|
-
#
|
|
311
|
-
#
|
|
312
|
-
#
|
|
313
|
-
#
|
|
314
|
-
# (out-of-tree call site, or a path the
|
|
315
|
-
# `configured_paths` config excluded). Sorbet itself
|
|
316
|
-
# has no opinion on such files; default to enforced
|
|
317
|
-
# so the recogniser still fires.
|
|
318
|
-
# - the path IS in `@sigil_by_path` but at `:false` /
|
|
319
|
-
# `:ignore` — gate closes.
|
|
244
|
+
# - `scope.source_path` is nil — synthetic call sites (specs, virtual-node fixtures) have no file
|
|
245
|
+
# context. Default to enforced so existing recogniser tests keep working.
|
|
246
|
+
# - the path is canonicalised to a form not in `@sigil_by_path` — the harvest never saw this file
|
|
247
|
+
# (out-of-tree call site, or a path the `configured_paths` config excluded). Sorbet itself has no
|
|
248
|
+
# opinion on such files; default to enforced so the recogniser still fires.
|
|
249
|
+
# - the path IS in `@sigil_by_path` but at `:false` / `:ignore` — gate closes.
|
|
320
250
|
def assertion_enforced_here?(scope)
|
|
321
251
|
return true unless @enforce_sigil
|
|
322
252
|
|
|
@@ -336,8 +266,8 @@ module Rigor
|
|
|
336
266
|
return nil if method_name.nil?
|
|
337
267
|
|
|
338
268
|
if (singleton_target = constant_receiver_name(receiver))
|
|
339
|
-
# `Post.find(...)` — direct singleton method, or
|
|
340
|
-
#
|
|
269
|
+
# `Post.find(...)` — direct singleton method, or `extend M` lifting `M#find` to the extending
|
|
270
|
+
# class.
|
|
341
271
|
chain_lookup(singleton_target, method_name, anchor_kind: :singleton, mixin_kind: :extend)
|
|
342
272
|
elsif receiver
|
|
343
273
|
instance_chain_lookup(receiver, method_name, scope)
|
|
@@ -347,16 +277,13 @@ module Rigor
|
|
|
347
277
|
end
|
|
348
278
|
|
|
349
279
|
# ADR-11 slice 2 — implicit-self calls.
|
|
350
|
-
# A receiver-less call inside a method body resolves against the
|
|
351
|
-
#
|
|
352
|
-
#
|
|
353
|
-
#
|
|
354
|
-
#
|
|
355
|
-
#
|
|
356
|
-
#
|
|
357
|
-
# `T.untyped` opt-out (the dispatcher's plugin tier had already
|
|
358
|
-
# run and declined). Anything else (toplevel / Dynamic / DSL
|
|
359
|
-
# self) contributes nothing and the dispatcher continues.
|
|
280
|
+
# A receiver-less call inside a method body resolves against the engine's own `scope.self_type`:
|
|
281
|
+
# `Nominal[Foo]` inside an instance method (instance-side lookup), `Singleton[Foo]` inside a `def
|
|
282
|
+
# self.x` body (singleton-side lookup, `extend` mixins). Without this, an enforced sig on a sibling
|
|
283
|
+
# method was invisible to in-class calls — the engine's body-inference tiers then re-typed the
|
|
284
|
+
# sibling's body, overriding an explicit `T.untyped` opt-out (the dispatcher's plugin tier had
|
|
285
|
+
# already run and declined). Anything else (toplevel / Dynamic / DSL self) contributes nothing and
|
|
286
|
+
# the dispatcher continues.
|
|
360
287
|
def implicit_self_lookup(method_name, scope)
|
|
361
288
|
self_type = scope&.self_type
|
|
362
289
|
case self_type
|
|
@@ -377,9 +304,8 @@ module Rigor
|
|
|
377
304
|
|
|
378
305
|
chain_lookup(receiver_type.class_name, method_name, anchor_kind: :instance, mixin_kind: :include)
|
|
379
306
|
rescue StandardError
|
|
380
|
-
# `scope.type_of` can raise on unrecognised synthetic
|
|
381
|
-
#
|
|
382
|
-
# bubbling the failure into the dispatcher.
|
|
307
|
+
# `scope.type_of` can raise on unrecognised synthetic nodes; degrade to "no contribution" rather
|
|
308
|
+
# than bubbling the failure into the dispatcher.
|
|
383
309
|
nil
|
|
384
310
|
end
|
|
385
311
|
|
|
@@ -387,15 +313,13 @@ module Rigor
|
|
|
387
313
|
#
|
|
388
314
|
# For instance-side calls (`post.body`):
|
|
389
315
|
# - `anchor_kind: :instance` (try `Post#body` first)
|
|
390
|
-
# - `mixin_kind: :include` (then walk Post's `include`d
|
|
391
|
-
# modules and try `Foo#body` on each)
|
|
316
|
+
# - `mixin_kind: :include` (then walk Post's `include`d modules and try `Foo#body` on each)
|
|
392
317
|
#
|
|
393
318
|
# For singleton-side calls (`Post.find`):
|
|
394
319
|
# - `anchor_kind: :singleton` (try `Post.find` first)
|
|
395
|
-
# - `mixin_kind: :extend` (then walk Post's `extend`ed
|
|
396
|
-
#
|
|
397
|
-
#
|
|
398
|
-
# class's SINGLETON methods, matching Ruby's MRO).
|
|
320
|
+
# - `mixin_kind: :extend` (then walk Post's `extend`ed modules and try `Foo#find` *as :instance* —
|
|
321
|
+
# `extend Foo` lifts Foo's INSTANCE methods to the extending class's SINGLETON methods, matching
|
|
322
|
+
# Ruby's MRO).
|
|
399
323
|
def chain_lookup(class_name, method_name, anchor_kind:, mixin_kind:)
|
|
400
324
|
each_class_form(class_name).each do |form|
|
|
401
325
|
sig = @catalog.lookup(class_name: form, method_name: method_name, kind: anchor_kind)
|
|
@@ -413,8 +337,7 @@ module Rigor
|
|
|
413
337
|
sig = @catalog.lookup(class_name: form, method_name: method_name, kind: :instance)
|
|
414
338
|
return sig if sig
|
|
415
339
|
|
|
416
|
-
# Transitive: an `include` inside the mixed-in
|
|
417
|
-
# module is also inherited by the host class.
|
|
340
|
+
# Transitive: an `include` inside the mixed-in module is also inherited by the host class.
|
|
418
341
|
mixin_modules_for(form, :include).each do |inner|
|
|
419
342
|
queue << inner unless visited.include?(inner)
|
|
420
343
|
end
|
|
@@ -424,20 +347,16 @@ module Rigor
|
|
|
424
347
|
nil
|
|
425
348
|
end
|
|
426
349
|
|
|
427
|
-
# `Post` and `::Post` are routinely confused at the catalog
|
|
428
|
-
#
|
|
429
|
-
# often writes the rooted form). Try both at every lookup.
|
|
350
|
+
# `Post` and `::Post` are routinely confused at the catalog boundary (the walker records the lexical
|
|
351
|
+
# name; user code often writes the rooted form). Try both at every lookup.
|
|
430
352
|
def each_class_form(class_name)
|
|
431
353
|
[class_name, "::#{class_name}"]
|
|
432
354
|
end
|
|
433
355
|
|
|
434
|
-
# Resolution forms for a mixed-in module name. Tapioca's
|
|
435
|
-
#
|
|
436
|
-
# (`
|
|
437
|
-
#
|
|
438
|
-
# (`module GeneratedAttributeMethods; ...; end` outside any
|
|
439
|
-
# class); explicit rooting (`::GeneratedAttributeMethods`)
|
|
440
|
-
# is occasionally seen. Try all three.
|
|
356
|
+
# Resolution forms for a mixed-in module name. Tapioca's generated DSL RBIs use the nested form
|
|
357
|
+
# (`class Post; module GeneratedAttributeMethods; ...; end`); hand-written shims often use the
|
|
358
|
+
# top-level form (`module GeneratedAttributeMethods; ...; end` outside any class); explicit rooting
|
|
359
|
+
# (`::GeneratedAttributeMethods`) is occasionally seen. Try all three.
|
|
441
360
|
def forms_for_mixin(host_class, mixin_name)
|
|
442
361
|
if mixin_name.start_with?("::")
|
|
443
362
|
[mixin_name, mixin_name.delete_prefix("::")]
|
|
@@ -486,8 +405,7 @@ module Rigor
|
|
|
486
405
|
|
|
487
406
|
# @param root [String] directory or single file.
|
|
488
407
|
# @param catalog [Catalog]
|
|
489
|
-
# @param extensions [Array<String>] file extensions to
|
|
490
|
-
# accept (e.g. `[".rb"]` for project source,
|
|
408
|
+
# @param extensions [Array<String>] file extensions to accept (e.g. `[".rb"]` for project source,
|
|
491
409
|
# `[".rbi"]` for Sorbet RBI tree).
|
|
492
410
|
def harvest_path(root, catalog, extensions:)
|
|
493
411
|
absolute = canonicalize(root)
|
|
@@ -498,18 +416,15 @@ module Rigor
|
|
|
498
416
|
end
|
|
499
417
|
end
|
|
500
418
|
elsif File.file?(absolute) && extensions.any? { |ext| absolute.end_with?(ext) }
|
|
501
|
-
# `paths:` may list individual files (the demos do
|
|
502
|
-
# this); walk them directly rather than skipping.
|
|
419
|
+
# `paths:` may list individual files (the demos do this); walk them directly rather than skipping.
|
|
503
420
|
harvest_file(absolute, catalog)
|
|
504
421
|
end
|
|
505
422
|
end
|
|
506
423
|
|
|
507
|
-
# Canonicalises a path through `File.realpath` so it
|
|
508
|
-
#
|
|
509
|
-
#
|
|
510
|
-
#
|
|
511
|
-
# `/private/tmp` etc.). Falls back to `File.expand_path`
|
|
512
|
-
# when realpath fails (e.g. the path no longer exists).
|
|
424
|
+
# Canonicalises a path through `File.realpath` so it matches the form `Plugin::TrustPolicy#allow_read?`
|
|
425
|
+
# sees (the runner builds the policy's roots from `Dir.pwd`, which has symlinks resolved on macOS —
|
|
426
|
+
# `/tmp` → `/private/tmp` etc.). Falls back to `File.expand_path` when realpath fails (e.g. the path
|
|
427
|
+
# no longer exists).
|
|
513
428
|
def canonicalize(path)
|
|
514
429
|
expanded = File.expand_path(path)
|
|
515
430
|
File.exist?(expanded) ? File.realpath(expanded) : expanded
|
|
@@ -521,31 +436,23 @@ module Rigor
|
|
|
521
436
|
contents = io_boundary.read_file(path)
|
|
522
437
|
return if contents.nil?
|
|
523
438
|
|
|
524
|
-
# ADR-11 slice 5 — honour Sorbet's `# typed: ignore`
|
|
525
|
-
# magic comment by skipping the file entirely.
|
|
439
|
+
# ADR-11 slice 5 — honour Sorbet's `# typed: ignore` magic comment by skipping the file entirely.
|
|
526
440
|
level = SigilDetector.detect(contents)
|
|
527
|
-
# Per-call-site assertion gating consults this map at
|
|
528
|
-
#
|
|
529
|
-
#
|
|
530
|
-
# reports its level to the gate (the gate then chooses
|
|
531
|
-
# to suppress assertions there too — `ignore` is
|
|
532
|
-
# stricter than `false`).
|
|
441
|
+
# Per-call-site assertion gating consults this map at recognition time. Recorded BEFORE the
|
|
442
|
+
# ignored short-circuit so a `# typed: ignore` file still reports its level to the gate (the gate
|
|
443
|
+
# then chooses to suppress assertions there too — `ignore` is stricter than `false`).
|
|
533
444
|
@sigil_by_path[path] = level
|
|
534
445
|
return if SigilDetector.ignored?(level)
|
|
535
446
|
|
|
536
447
|
result = Prism.parse(contents)
|
|
537
448
|
return unless result.errors.empty?
|
|
538
449
|
|
|
539
|
-
# `enforce_sigil` follow-up — when on (default), files
|
|
540
|
-
#
|
|
541
|
-
#
|
|
542
|
-
#
|
|
543
|
-
#
|
|
544
|
-
#
|
|
545
|
-
# that for sig contributions. Assertion recognisers
|
|
546
|
-
# (`T.let` / `T.cast` / `T.must` / `T.bind` /
|
|
547
|
-
# `T.assert_type!`) stay live regardless of sigil — the
|
|
548
|
-
# user wrote those deliberately.
|
|
450
|
+
# `enforce_sigil` follow-up — when on (default), files at `:false` (or sigil-less, which Sorbet
|
|
451
|
+
# treats as `:false`) are STILL walked so parse-error diagnostics surface, but sigs flow into a
|
|
452
|
+
# discardable catalog rather than the per-run one. Sorbet itself doesn't enforce types at `#
|
|
453
|
+
# typed: false`, and Rigor mirrors that for sig contributions. Assertion recognisers (`T.let` /
|
|
454
|
+
# `T.cast` / `T.must` / `T.bind` / `T.assert_type!`) stay live regardless of sigil — the user
|
|
455
|
+
# wrote those deliberately.
|
|
549
456
|
sig_catalog = if @enforce_sigil && !SigilDetector.enforced?(level)
|
|
550
457
|
Catalog.new
|
|
551
458
|
else
|
|
@@ -555,15 +462,13 @@ module Rigor
|
|
|
555
462
|
errors = CatalogWalker.walk(root: result.value, catalog: sig_catalog, path: path)
|
|
556
463
|
@parse_errors_by_path[path] = errors unless errors.empty?
|
|
557
464
|
rescue Plugin::AccessDeniedError, Errno::ENOENT
|
|
558
|
-
# Skip files outside the trusted read scope or that
|
|
559
|
-
#
|
|
560
|
-
# no output for them.
|
|
465
|
+
# Skip files outside the trusted read scope or that vanished between glob and read; the plugin
|
|
466
|
+
# produces no output for them.
|
|
561
467
|
nil
|
|
562
468
|
end
|
|
563
469
|
|
|
564
|
-
# Emits a `plugin.sorbet.absurd-reachable` warning for the
|
|
565
|
-
# `
|
|
566
|
-
# inference; the node rule above does the identity match and pop.
|
|
470
|
+
# Emits a `plugin.sorbet.absurd-reachable` warning for the `T.absurd(x)` call recorded in
|
|
471
|
+
# `@reachable_absurd_nodes` during inference; the node rule above does the identity match and pop.
|
|
567
472
|
def absurd_diagnostic(path, call_node)
|
|
568
473
|
Rigor::Analysis::Diagnostic.from_node(
|
|
569
474
|
call_node,
|
|
@@ -576,19 +481,16 @@ module Rigor
|
|
|
576
481
|
)
|
|
577
482
|
end
|
|
578
483
|
|
|
579
|
-
# ADR-11 light follow-up — `T.reveal_type(expr)` records
|
|
580
|
-
#
|
|
581
|
-
#
|
|
582
|
-
#
|
|
583
|
-
# inferred type for downstream chaining; this hash carries
|
|
584
|
-
# the *display* string that the diagnostic shows.
|
|
484
|
+
# ADR-11 light follow-up — `T.reveal_type(expr)` records the inferred type at recogniser time so the
|
|
485
|
+
# per-file diagnostic hook can surface the human-facing message. The reveal call's contribution
|
|
486
|
+
# already preserved the inferred type for downstream chaining; this hash carries the *display*
|
|
487
|
+
# string that the diagnostic shows.
|
|
585
488
|
def record_reveal_type_call(call_node, return_type)
|
|
586
489
|
@reveal_type_calls[call_node] = display_for_type(return_type)
|
|
587
490
|
end
|
|
588
491
|
|
|
589
492
|
def display_for_type(type)
|
|
590
|
-
# `Type#describe` is the human-facing display contract
|
|
591
|
-
# used by `rigor type-of`'s text renderer.
|
|
493
|
+
# `Type#describe` is the human-facing display contract used by `rigor type-of`'s text renderer.
|
|
592
494
|
return "untyped" if type.nil?
|
|
593
495
|
|
|
594
496
|
type.respond_to?(:describe) ? type.describe : type.inspect
|
|
@@ -604,16 +506,11 @@ module Rigor
|
|
|
604
506
|
)
|
|
605
507
|
end
|
|
606
508
|
|
|
607
|
-
# T.bind / T.assert_type! priority slice 1 — runs the
|
|
608
|
-
#
|
|
609
|
-
#
|
|
610
|
-
#
|
|
611
|
-
#
|
|
612
|
-
# `Dynamic[top]` inferred type
|
|
613
|
-
# silences the check; a definite `:no` records for
|
|
614
|
-
# diagnostic emission; `:maybe` (uncertain) is treated as
|
|
615
|
-
# "trust the user" and silenced — the runtime check is
|
|
616
|
-
# there for those cases.
|
|
509
|
+
# T.bind / T.assert_type! priority slice 1 — runs the static subtype check at recogniser time and
|
|
510
|
+
# records the call only when the inferred type is *provably incompatible* with the asserted type.
|
|
511
|
+
# Gradual consistency rules (`Type#accepts` mode `:gradual`): a `Dynamic[top]` inferred type
|
|
512
|
+
# silences the check; a definite `:no` records for diagnostic emission; `:maybe` (uncertain) is
|
|
513
|
+
# treated as "trust the user" and silenced — the runtime check is there for those cases.
|
|
617
514
|
def record_assert_type_check(call_node, scope)
|
|
618
515
|
check = AssertionRecognizer.assert_type_check(call_node, scope)
|
|
619
516
|
return if check.nil?
|