rigortype 0.3.5 → 0.3.7
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 +5 -5
- data/data/builtins/ruby_core/date.yml +6 -3
- data/data/builtins/ruby_core/enumerable.yml +1 -0
- data/data/builtins/ruby_core/exception.yml +2 -1
- data/data/builtins/ruby_core/file.yml +28 -14
- data/data/builtins/ruby_core/hash.yml +5 -2
- data/data/builtins/ruby_core/io.yml +33 -16
- data/data/builtins/ruby_core/random.yml +2 -1
- data/data/builtins/ruby_core/re.yml +6 -3
- data/data/builtins/ruby_core/struct.yml +2 -0
- data/data/builtins/ruby_core/time.yml +2 -1
- data/data/gem_overlay/activesupport/core_ext.rbs +442 -11
- data/docs/handbook/02-everyday-types.md +1 -1
- data/docs/handbook/04-tuples-and-shapes.md +1 -1
- data/docs/handbook/08-understanding-errors.md +1 -1
- data/docs/handbook/09-plugins.md +2 -2
- data/docs/handbook/10-sorbet.md +1 -1
- data/docs/handbook/README.md +2 -2
- data/docs/handbook/appendix-go.md +1 -1
- data/docs/handbook/appendix-java-csharp.md +2 -2
- data/docs/handbook/appendix-mypy.md +1 -1
- data/docs/handbook/appendix-protocols-and-structural-typing.md +2 -2
- data/docs/handbook/appendix-rust.md +1 -1
- data/docs/handbook/appendix-type-theory.md +2 -2
- data/docs/handbook/appendix-typescript.md +4 -4
- data/docs/manual/02-cli-reference.md +29 -16
- data/docs/manual/03-configuration.md +1 -1
- data/docs/manual/04-diagnostics.md +12 -0
- data/docs/manual/05-inspecting-types.md +20 -8
- data/docs/manual/07-plugins.md +4 -0
- data/docs/manual/08-skills.md +1 -1
- data/docs/manual/11-ci.md +1 -1
- data/docs/manual/12-caching.md +1 -1
- data/docs/manual/15-type-protection-coverage.md +1 -1
- data/docs/manual/18-removing-dead-code.md +13 -4
- data/docs/manual/19-effect-labels.md +19 -12
- data/docs/manual/README.md +2 -2
- data/docs/manual/plugins/rigor-actionmailer.md +4 -4
- data/docs/manual/plugins/rigor-actionpack.md +61 -0
- data/docs/manual/plugins/rigor-activejob.md +3 -3
- data/docs/manual/plugins/rigor-activerecord.md +78 -5
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +114 -12
- data/docs/manual/plugins/rigor-rails-i18n.md +5 -5
- data/docs/manual/plugins/rigor-rspec.md +6 -2
- data/docs/manual/plugins/rigor-sidekiq.md +22 -0
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +12 -0
- data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +98 -0
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +3 -3
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +199 -0
- data/lib/rigor/analysis/check_rules/rule_walk.rb +1 -2
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +2 -2
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +6 -12
- data/lib/rigor/analysis/check_rules.rb +383 -48
- data/lib/rigor/analysis/crash_signature.rb +116 -0
- data/lib/rigor/analysis/dependency_recorder.rb +18 -3
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +2 -1
- data/lib/rigor/analysis/diagnostic.rb +17 -6
- data/lib/rigor/analysis/effects_cache_probe.rb +132 -0
- data/lib/rigor/analysis/incremental.rb +28 -0
- data/lib/rigor/analysis/incremental_session.rb +44 -9
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +1 -2
- data/lib/rigor/analysis/reachability/graph.rb +44 -16
- data/lib/rigor/analysis/reachability/plugin_roots.rb +11 -8
- data/lib/rigor/analysis/reachability/scan.rb +27 -7
- data/lib/rigor/analysis/reachability/scan_cache.rb +130 -0
- data/lib/rigor/analysis/result.rb +32 -0
- data/lib/rigor/analysis/run_cache_key.rb +53 -1
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +93 -1
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +14 -8
- data/lib/rigor/analysis/runner/pool_coordinator.rb +34 -0
- data/lib/rigor/analysis/runner/project_pre_passes.rb +15 -11
- data/lib/rigor/analysis/runner/run_snapshots.rb +7 -2
- data/lib/rigor/analysis/runner.rb +298 -38
- data/lib/rigor/analysis/worker_session.rb +10 -1
- data/lib/rigor/bleeding_edge.rb +7 -6
- data/lib/rigor/builtins/hkt_builtins.rb +45 -6
- data/lib/rigor/builtins/predefined_constant_refinements.rb +108 -59
- data/lib/rigor/builtins/static_return_refinements.rb +20 -1
- data/lib/rigor/cache/descriptor.rb +42 -2
- data/lib/rigor/cache/file_digest.rb +20 -2
- data/lib/rigor/cache/incremental_snapshot.rb +40 -3
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +31 -8
- data/lib/rigor/cache/store.rb +10 -1
- data/lib/rigor/cli/check_command.rb +67 -48
- data/lib/rigor/cli/coverage_command.rb +28 -24
- data/lib/rigor/cli/coverage_mutation.rb +13 -17
- data/lib/rigor/cli/coverage_scan.rb +47 -6
- data/lib/rigor/cli/doc_links.rb +100 -0
- data/lib/rigor/cli/docs_command.rb +32 -2
- data/lib/rigor/cli/effects_command.rb +27 -2
- data/lib/rigor/cli/effects_diff_renderer.rb +82 -12
- data/lib/rigor/cli/effects_explain_renderer.rb +6 -3
- data/lib/rigor/cli/effects_snapshot_command.rb +52 -7
- data/lib/rigor/cli/fused_protection_renderer.rb +19 -2
- data/lib/rigor/cli/fused_protection_report.rb +23 -2
- data/lib/rigor/cli/measurement_integrity_warning.rb +57 -0
- data/lib/rigor/cli/mutation_protection_renderer.rb +22 -2
- data/lib/rigor/cli/mutation_protection_report.rb +31 -2
- data/lib/rigor/cli/sig_gen_command.rb +18 -0
- data/lib/rigor/cli/type_of_command.rb +188 -41
- data/lib/rigor/cli/type_of_renderer.rb +72 -3
- data/lib/rigor/cli/type_scan_command.rb +10 -1
- data/lib/rigor/cli/unused_command.rb +65 -16
- data/lib/rigor/cli.rb +22 -9
- data/lib/rigor/configuration.rb +7 -4
- data/lib/rigor/effects/definition_lines.rb +100 -0
- data/lib/rigor/effects/envelope_check.rb +18 -1
- data/lib/rigor/effects/liskov_check.rb +17 -8
- data/lib/rigor/effects/plugin_facts.rb +1 -1
- data/lib/rigor/effects/scanner.rb +2 -3
- data/lib/rigor/effects/signature_sources.rb +13 -2
- data/lib/rigor/effects/snapshot.rb +53 -21
- data/lib/rigor/effects/snapshot_diff.rb +26 -3
- data/lib/rigor/environment/bundle_sig_discovery.rb +64 -13
- data/lib/rigor/environment/lockfile_resolver.rb +15 -5
- data/lib/rigor/environment/missing_gem_constant_index.rb +45 -4
- data/lib/rigor/environment/rbs_hierarchy.rb +16 -22
- data/lib/rigor/environment/rbs_loader.rb +525 -46
- data/lib/rigor/environment.rb +38 -15
- data/lib/rigor/inference/acceptance.rb +172 -20
- data/lib/rigor/inference/captured_locals.rb +62 -0
- data/lib/rigor/inference/content_join.rb +347 -0
- data/lib/rigor/inference/def_handle.rb +15 -3
- data/lib/rigor/inference/def_node_resolver.rb +62 -3
- data/lib/rigor/inference/expression_typer.rb +1020 -141
- data/lib/rigor/inference/fork_map.rb +6 -1
- data/lib/rigor/inference/hkt_reducer.rb +12 -1
- data/lib/rigor/inference/hkt_registry.rb +46 -0
- data/lib/rigor/inference/hkt_sugar_translator.rb +93 -0
- data/lib/rigor/inference/index_write_widening.rb +48 -0
- data/lib/rigor/inference/indexed_narrowing.rb +14 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +7 -5
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +10 -0
- data/lib/rigor/inference/method_dispatcher/json_folding.rb +58 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +81 -38
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +54 -17
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +63 -0
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +63 -10
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +104 -0
- data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +81 -0
- data/lib/rigor/inference/method_dispatcher.rb +155 -14
- data/lib/rigor/inference/method_parameter_binder.rb +3 -3
- data/lib/rigor/inference/mutation_widening.rb +244 -174
- data/lib/rigor/inference/narrowing.rb +331 -27
- data/lib/rigor/inference/parameter_inference_collector.rb +1 -0
- data/lib/rigor/inference/pre_eval_constants.rb +15 -0
- data/lib/rigor/inference/precision_scanner.rb +5 -2
- data/lib/rigor/inference/project_patched_scanner.rb +2 -1
- data/lib/rigor/inference/rbs_type_translator.rb +89 -39
- data/lib/rigor/inference/scope_indexer.rb +1493 -140
- data/lib/rigor/inference/statement_evaluator.rb +276 -91
- data/lib/rigor/inference/struct_fold_safety.rb +186 -27
- data/lib/rigor/inference/synthetic_method_scanner.rb +7 -0
- data/lib/rigor/inference/version_guard.rb +229 -0
- data/lib/rigor/language_server/selection_range_provider.rb +1 -1
- data/lib/rigor/plugin/base.rb +3 -2
- data/lib/rigor/plugin/inflector.rb +14 -5
- data/lib/rigor/plugin/io_boundary.rb +107 -4
- data/lib/rigor/plugin/loader.rb +14 -0
- data/lib/rigor/protection/analysis_guard.rb +60 -0
- data/lib/rigor/protection/closure_kill_oracle.rb +43 -38
- data/lib/rigor/protection/diagnostic_oracle.rb +12 -4
- data/lib/rigor/protection/discovery_seed.rb +1 -1
- data/lib/rigor/protection/measurement_integrity.rb +36 -0
- data/lib/rigor/protection/mutation_scanner.rb +61 -6
- data/lib/rigor/protection/mutator.rb +121 -18
- data/lib/rigor/rbs_extended/envelope_scanner.rb +9 -0
- data/lib/rigor/reflection.rb +182 -30
- data/lib/rigor/scope/discovery_index.rb +39 -1
- data/lib/rigor/scope.rb +385 -4
- data/lib/rigor/sig_gen/classification.rb +12 -1
- data/lib/rigor/sig_gen/generator.rb +254 -5
- data/lib/rigor/sig_gen/renderer.rb +1 -6
- data/lib/rigor/sig_gen/writer.rb +3 -0
- data/lib/rigor/source/constant_path.rb +79 -2
- data/lib/rigor/triage/catalogue.rb +1 -1
- data/lib/rigor/type/combinator.rb +10 -0
- data/lib/rigor/type/maybe.rb +47 -0
- data/lib/rigor/type/refined.rb +1 -2
- data/lib/rigor/type/result.rb +53 -0
- data/lib/rigor/type.rb +2 -0
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +3 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +58 -8
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +15 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +4 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +3 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +39 -14
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +36 -3
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -2
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +21 -5
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +47 -8
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +13 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +242 -4
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +3 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +7 -3
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +62 -11
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +15 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +3 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +5 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +798 -22
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +156 -13
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +140 -40
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +2 -2
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +20 -6
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +14 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +6 -3
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +23 -6
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +264 -9
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +876 -23
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +99 -0
- data/plugins/rigor-dry-monads/lib/rigor-dry-monads.rb +5 -0
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +34 -0
- data/plugins/rigor-ethon/lib/rigor-ethon.rb +3 -0
- data/plugins/rigor-ethon/sig/ethon.rbs +27 -0
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +3 -2
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +198 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/binding_recognizer.rb +75 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/catalog.rb +64 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +173 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/target_detector.rb +46 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +161 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +121 -0
- data/plugins/rigor-ffi/lib/rigor-ffi.rb +3 -0
- data/plugins/rigor-ffi/sig/ffi.rbs +67 -0
- data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +19 -0
- data/plugins/rigor-ffi-rzmq/lib/rigor-ffi-rzmq.rb +3 -0
- data/plugins/rigor-ffi-rzmq/sig/ffi_rzmq.rbs +29 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +2 -4
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +2 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +2 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +2 -1
- data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +156 -11
- data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +30 -0
- data/plugins/rigor-rbnacl/lib/rigor-rbnacl.rb +3 -0
- data/plugins/rigor-rbnacl/sig/rbnacl.rbs +22 -0
- data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +55 -0
- data/plugins/rigor-sassc/lib/rigor-sassc.rb +3 -0
- data/plugins/rigor-sassc/sig/sassc.rbs +24 -0
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +32 -11
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +3 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +4 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +63 -10
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +15 -2
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +61 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +4 -2
- data/sig/rigor/analysis/baseline.rbs +1 -1
- data/sig/rigor/environment.rbs +3 -0
- data/sig/rigor/inference.rbs +4 -1
- data/sig/rigor/plugin/io_boundary.rbs +2 -0
- data/sig/rigor/reflection.rbs +3 -1
- data/sig/rigor/scope.rbs +25 -1
- data/sig/rigor/type.rbs +95 -1
- data/sig/rigor.rbs +16 -0
- data/skills/rigor-ci-setup/SKILL.md +2 -2
- data/skills/rigor-editor-setup/SKILL.md +2 -2
- data/skills/rigor-mcp-setup/SKILL.md +2 -2
- data/skills/rigor-monkeypatch-resolve/SKILL.md +1 -1
- data/skills/rigor-plugin-review/SKILL.md +3 -3
- metadata +73 -3
data/lib/rigor/environment.rb
CHANGED
|
@@ -38,9 +38,9 @@ module Rigor
|
|
|
38
38
|
# ADR-87 WD4 boot-slimming probe can read it without loading the inference engine.
|
|
39
39
|
|
|
40
40
|
# ADR-72 — a Gemfile.lock gem name mapped to the opt-in plugin id that ships the SAME core-ext RBS. When
|
|
41
|
-
# that plugin
|
|
42
|
-
# methods (which would raise a duplicate-declaration error). Keyed on the gem name
|
|
43
|
-
# reports.
|
|
41
|
+
# that plugin's signatures are reachable the auto-overlay for the gem stands down, so the two never both
|
|
42
|
+
# declare the methods (which would raise a duplicate-declaration error). Keyed on the gem name
|
|
43
|
+
# `RbsCoverageReport` reports.
|
|
44
44
|
GEM_OVERLAY_PLUGIN_IDS = { "activesupport" => "activesupport-core-ext" }.freeze
|
|
45
45
|
|
|
46
46
|
attr_reader :class_registry, :rbs_loader, :plugin_registry, :dependency_source_index,
|
|
@@ -190,7 +190,7 @@ module Rigor
|
|
|
190
190
|
# {Environment::RbsLoader} so constant lookups (and, in later v0.0.9 slices, other reflection
|
|
191
191
|
# artefacts) consult the cache. Pass `nil` (the default) to skip caching for this environment.
|
|
192
192
|
# @return [Rigor::Environment]
|
|
193
|
-
# rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
|
|
193
|
+
# rubocop:disable-next Metrics/MethodLength, Metrics/ParameterLists
|
|
194
194
|
def for_project(root: Dir.pwd, libraries: [], signature_paths: nil, cache_store: nil,
|
|
195
195
|
plugin_registry: nil, dependency_source_index: nil,
|
|
196
196
|
rbs_extended_reporter: nil, boundary_cross_reporter: nil,
|
|
@@ -253,11 +253,13 @@ module Rigor
|
|
|
253
253
|
# extensions resolve (e.g. `Integer#minutes` on a Rails project) — turning a systematic false
|
|
254
254
|
# `call.undefined-method` into a no-op while a project WITHOUT the gem still sees the genuine
|
|
255
255
|
# diagnostic. Appended last so any RBS the project already supplies wins, and skipped for a gem whose
|
|
256
|
-
# opt-in plugin twin is loaded
|
|
257
|
-
#
|
|
256
|
+
# opt-in plugin twin is REACHABLE — loaded as a plugin, or wired in by `signature_paths:` (#672) —
|
|
257
|
+
# so the two never both declare the methods. The paths ride in `loader_signature_paths`, so the env
|
|
258
|
+
# cache descriptor digests them for free.
|
|
258
259
|
missing_gems, overlay_paths = missing_gems_and_overlay_paths(
|
|
259
260
|
locked: locked, default_libraries: merged_libraries, bundle_sig_paths: gem_sig_paths,
|
|
260
|
-
rbs_collection_paths: collection_paths, plugin_registry: plugin_registry
|
|
261
|
+
rbs_collection_paths: collection_paths, plugin_registry: plugin_registry,
|
|
262
|
+
signature_paths: resolved_paths
|
|
261
263
|
)
|
|
262
264
|
loader_signature_paths = resolved_paths + plugin_sig_paths + gem_sig_paths +
|
|
263
265
|
collection_paths + overlay_paths
|
|
@@ -276,7 +278,11 @@ module Rigor
|
|
|
276
278
|
libraries: merged_libraries,
|
|
277
279
|
signature_paths: loader_signature_paths,
|
|
278
280
|
cache_store: cache_store,
|
|
279
|
-
virtual_rbs: virtual_rbs
|
|
281
|
+
virtual_rbs: virtual_rbs,
|
|
282
|
+
# Issue #610 — the plugin-contributed subset is loaded LAST and may stand down against a colliding
|
|
283
|
+
# generic arity. Only the bundled plugins' own `sig/` is deferred: the user's `signature_paths:`,
|
|
284
|
+
# the bundle walk and `rbs collection install` all stay authoritative.
|
|
285
|
+
deferred_signature_paths: plugin_sig_paths
|
|
280
286
|
)
|
|
281
287
|
# ADR-20 slice 2c + 2e — seed hkt_registry with the bundled builtins. The Environment's
|
|
282
288
|
# `#hkt_registry` getter then LAZILY merges in the RBS env scan on first call so fast paths that
|
|
@@ -297,7 +303,6 @@ module Rigor
|
|
|
297
303
|
missing_rbs_bundle_path: bundle_root
|
|
298
304
|
)
|
|
299
305
|
end
|
|
300
|
-
# rubocop:enable Metrics/MethodLength, Metrics/ParameterLists
|
|
301
306
|
|
|
302
307
|
private
|
|
303
308
|
|
|
@@ -330,27 +335,45 @@ module Rigor
|
|
|
330
335
|
# and consumed twice: as `[gem_name, version]` pairs for the ADR-82 WD9 constant-ownership index, and
|
|
331
336
|
# as the eligible-gem set for the ADR-72 overlay resolution. Returns `[pairs, overlay_paths]`.
|
|
332
337
|
def missing_gems_and_overlay_paths(locked:, default_libraries:, bundle_sig_paths:,
|
|
333
|
-
rbs_collection_paths:, plugin_registry:)
|
|
338
|
+
rbs_collection_paths:, plugin_registry:, signature_paths: [])
|
|
334
339
|
return [[], []] if locked.empty?
|
|
335
340
|
|
|
336
341
|
rows = RbsCoverageReport.classify(
|
|
337
342
|
locked_gems: locked, default_libraries: default_libraries,
|
|
338
343
|
bundle_sig_paths: bundle_sig_paths, rbs_collection_paths: rbs_collection_paths
|
|
339
344
|
).select { |row| row.source == :missing }
|
|
340
|
-
overlays = gem_overlay_paths(
|
|
345
|
+
overlays = gem_overlay_paths(
|
|
346
|
+
missing_gem_names: rows.map(&:gem_name), plugin_registry: plugin_registry,
|
|
347
|
+
signature_paths: signature_paths
|
|
348
|
+
)
|
|
341
349
|
[rows.map { |row| [row.gem_name, row.version] }, overlays]
|
|
342
350
|
end
|
|
343
351
|
|
|
344
352
|
# ADR-72 — resolve the bundled RBS overlay directories to load for this project. A gem is eligible when
|
|
345
|
-
# it is classified `:missing` (see {missing_rbs_gem_rows}) and its conflicting opt-in plugin (if
|
|
346
|
-
# is
|
|
347
|
-
|
|
353
|
+
# it is classified `:missing` (see {missing_rbs_gem_rows}) and its conflicting opt-in plugin twin (if
|
|
354
|
+
# any) is NOT already reachable. Returns `[Pathname]`, deterministically ordered, or `[]` when no
|
|
355
|
+
# eligible gem.
|
|
356
|
+
#
|
|
357
|
+
# "Reachable" is two questions, not one. The plugin being LOADED is the route ADR-72 anticipated. The
|
|
358
|
+
# second is the route issue #672 found: a project that wires the plugin's signatures through
|
|
359
|
+
# `signature_paths:` instead of `plugins:` reaches the same `.rbs` with no registry entry to key on, so
|
|
360
|
+
# both halves loaded, `RBS::DefinitionBuilder` raised `DuplicatedMethodDefinitionError` for every class
|
|
361
|
+
# they share, and each one silently degraded to `Dynamic[top]` — the run reported LESS while exiting 0.
|
|
362
|
+
#
|
|
363
|
+
# This method keeps the POLICY (which gem maps to which plugin id, and when an overlay is eligible at
|
|
364
|
+
# all); `RbsLoader.bundled_overlay_twin_signatures_loaded?` answers the filesystem half, beside the
|
|
365
|
+
# overlay-side answer to the same question, and carries the reasoning for why it asks what an entry
|
|
366
|
+
# LOADS rather than what it looks like.
|
|
367
|
+
def gem_overlay_paths(missing_gem_names:, plugin_registry:, signature_paths: [])
|
|
348
368
|
return [] if missing_gem_names.empty?
|
|
349
369
|
|
|
350
370
|
loaded_ids = plugin_registry ? plugin_registry.ids.to_set : Set.new
|
|
351
371
|
eligible = missing_gem_names.reject do |gem_name|
|
|
352
372
|
plugin_id = GEM_OVERLAY_PLUGIN_IDS[gem_name]
|
|
353
|
-
|
|
373
|
+
next false unless plugin_id
|
|
374
|
+
|
|
375
|
+
loaded_ids.include?(plugin_id) ||
|
|
376
|
+
RbsLoader.bundled_overlay_twin_signatures_loaded?(plugin_id, signature_paths)
|
|
354
377
|
end.sort
|
|
355
378
|
RbsLoader.gem_overlay_sig_paths(eligible)
|
|
356
379
|
end
|
|
@@ -23,7 +23,7 @@ module Rigor
|
|
|
23
23
|
# `Nominal[Hash, [K, V]]` the shape is projected to its underlying nominal so the existing
|
|
24
24
|
# generic-acceptance pipeline continues to apply; the converse direction (a Tuple receiver accepting a
|
|
25
25
|
# generic Array) stays conservative because the analyzer cannot verify arity from a raw nominal alone.
|
|
26
|
-
# rubocop:disable Metrics/ModuleLength
|
|
26
|
+
# rubocop:disable-next Metrics/ModuleLength
|
|
27
27
|
module Acceptance
|
|
28
28
|
module_function
|
|
29
29
|
|
|
@@ -67,11 +67,15 @@ module Rigor
|
|
|
67
67
|
Type::Refined => :accepts_refined,
|
|
68
68
|
Type::Intersection => :accepts_intersection,
|
|
69
69
|
Type::Tuple => :accepts_tuple,
|
|
70
|
-
Type::HashShape => :accepts_hash_shape
|
|
70
|
+
Type::HashShape => :accepts_hash_shape,
|
|
71
|
+
Type::Result => :accepts_result,
|
|
72
|
+
Type::Maybe => :accepts_maybe,
|
|
73
|
+
Type::StructClass => :accepts_class_factory,
|
|
74
|
+
Type::DataClass => :accepts_class_factory
|
|
71
75
|
}.freeze
|
|
72
76
|
private_constant :TYPE_HANDLERS
|
|
73
77
|
|
|
74
|
-
# rubocop:disable Metrics/ClassLength
|
|
78
|
+
# rubocop:disable-next Metrics/ClassLength
|
|
75
79
|
class << self
|
|
76
80
|
private
|
|
77
81
|
|
|
@@ -165,20 +169,28 @@ module Rigor
|
|
|
165
169
|
end
|
|
166
170
|
end
|
|
167
171
|
|
|
168
|
-
# Singleton[C] only accepts another Singleton[D] where D is a subclass
|
|
169
|
-
# carrier (instance, constant, ...) is no, because the singleton type's
|
|
170
|
-
# objects themselves.
|
|
172
|
+
# Singleton[C] only accepts another Singleton[D] (or StructClass / DataClass) where D is a subclass
|
|
173
|
+
# of (or equal to) C. Any other carrier (instance, constant, ...) is no, because the singleton type's
|
|
174
|
+
# inhabitants are the class objects themselves.
|
|
171
175
|
def accepts_singleton(self_type, other_type, mode)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
176
|
+
actual_name =
|
|
177
|
+
case other_type
|
|
178
|
+
when Type::Singleton then other_type.class_name
|
|
179
|
+
when Type::StructClass, Type::DataClass
|
|
180
|
+
return Type::AcceptsResult.yes(mode: mode, reasons: "exact name match") if
|
|
181
|
+
other_type.class_name == self_type.class_name
|
|
182
|
+
|
|
183
|
+
class_factory_base_name(other_type)
|
|
184
|
+
else
|
|
185
|
+
return Type::AcceptsResult.no(
|
|
186
|
+
mode: mode,
|
|
187
|
+
reasons: "Singleton[#{self_type.class_name}] does not accept #{other_type.class}"
|
|
188
|
+
)
|
|
189
|
+
end
|
|
178
190
|
|
|
179
191
|
class_subtype_result(
|
|
180
192
|
target_name: self_type.class_name,
|
|
181
|
-
actual_name:
|
|
193
|
+
actual_name: actual_name,
|
|
182
194
|
mode: mode,
|
|
183
195
|
kind: :singleton
|
|
184
196
|
)
|
|
@@ -195,11 +207,13 @@ module Rigor
|
|
|
195
207
|
# - HashShape{*} when self is the Hash (or a supertype) family, projected to
|
|
196
208
|
# `Nominal[Hash, [union(keys), union(values)]]`.
|
|
197
209
|
# - Singleton: never (wrong value kind).
|
|
210
|
+
# - StructClass / DataClass: class-side carriers, accepted when C is a meta nominal (Class, Module, ...).
|
|
198
211
|
def accepts_nominal(self_type, other_type, mode)
|
|
199
212
|
case other_type
|
|
200
213
|
when Type::Nominal then accepts_nominal_from_nominal(self_type, other_type, mode)
|
|
201
214
|
when Type::Constant then accepts_nominal_from_constant(self_type, other_type, mode)
|
|
202
215
|
when Type::Singleton then accepts_nominal_from_singleton(self_type, other_type, mode)
|
|
216
|
+
when Type::StructClass, Type::DataClass then accepts_nominal_from_class_factory(self_type, other_type, mode)
|
|
203
217
|
when Type::IntegerRange then accepts_nominal_from_integer_range(self_type, other_type, mode)
|
|
204
218
|
else accepts_nominal_from_shape(self_type, other_type, mode)
|
|
205
219
|
end
|
|
@@ -234,6 +248,8 @@ module Rigor
|
|
|
234
248
|
# is bounded because every refinement carrier's `base` is closer to the nominal layer.
|
|
235
249
|
accepts(self_type, other_type.base, mode: mode)
|
|
236
250
|
.with_reason("projected #{other_type.class.name.split('::').last} to its base")
|
|
251
|
+
when Type::Result, Type::Maybe
|
|
252
|
+
accepts_nominal_from_monad(self_type, other_type, mode)
|
|
237
253
|
else
|
|
238
254
|
Type::AcceptsResult.no(
|
|
239
255
|
mode: mode,
|
|
@@ -242,6 +258,32 @@ module Rigor
|
|
|
242
258
|
end
|
|
243
259
|
end
|
|
244
260
|
|
|
261
|
+
def accepts_nominal_from_monad(self_type, other_type, mode)
|
|
262
|
+
cls = self_type.class_name.sub(/\A::/, "")
|
|
263
|
+
case other_type
|
|
264
|
+
when Type::Result
|
|
265
|
+
unless %w[Dry::Monads::Result Result].include?(cls)
|
|
266
|
+
return Type::AcceptsResult.no(mode: mode, reasons: "Nominal[#{self_type.class_name}] rejects Result")
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
target = Type::Combinator.nominal_of(
|
|
270
|
+
self_type.class_name,
|
|
271
|
+
type_args: [other_type.ok_type, other_type.err_type]
|
|
272
|
+
)
|
|
273
|
+
accepts(self_type, target, mode: mode).with_reason("projected Result to Nominal")
|
|
274
|
+
when Type::Maybe
|
|
275
|
+
unless %w[Dry::Monads::Maybe Maybe].include?(cls)
|
|
276
|
+
return Type::AcceptsResult.no(mode: mode, reasons: "Nominal[#{self_type.class_name}] rejects Maybe")
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
target = Type::Combinator.nominal_of(
|
|
280
|
+
self_type.class_name,
|
|
281
|
+
type_args: [other_type.value_type]
|
|
282
|
+
)
|
|
283
|
+
accepts(self_type, target, mode: mode).with_reason("projected Maybe to Nominal")
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
245
287
|
# `Nominal[Integer]` (and anything Integer is-a, like Numeric) accepts any `IntegerRange`; nothing
|
|
246
288
|
# else does. Argument-bearing `Nominal`s never accept `IntegerRange` because IntegerRange has no
|
|
247
289
|
# type args.
|
|
@@ -275,11 +317,11 @@ module Rigor
|
|
|
275
317
|
# introspection patterns. The rule conservatively answers `:yes` for `Module` (every singleton is at
|
|
276
318
|
# least a Module) and for `Class` / `Object` / `BasicObject` (the class object inherits from those).
|
|
277
319
|
# Other Nominals fall through to the default `:no`.
|
|
278
|
-
|
|
279
|
-
private_constant :
|
|
320
|
+
CLASS_OBJECT_NOMINALS = %w[Module Class Object Kernel BasicObject].freeze
|
|
321
|
+
private_constant :CLASS_OBJECT_NOMINALS
|
|
280
322
|
|
|
281
323
|
def accepts_nominal_from_singleton(self_type, other_type, mode)
|
|
282
|
-
if
|
|
324
|
+
if CLASS_OBJECT_NOMINALS.include?(self_type.class_name)
|
|
283
325
|
return Type::AcceptsResult.yes(
|
|
284
326
|
mode: mode,
|
|
285
327
|
reasons: "Singleton[#{other_type.class_name}] is-a #{self_type.class_name}"
|
|
@@ -292,6 +334,38 @@ module Rigor
|
|
|
292
334
|
)
|
|
293
335
|
end
|
|
294
336
|
|
|
337
|
+
def accepts_nominal_from_class_factory(self_type, other_type, mode)
|
|
338
|
+
if CLASS_OBJECT_NOMINALS.include?(self_type.class_name) && self_type.type_args.empty?
|
|
339
|
+
return Type::AcceptsResult.yes(
|
|
340
|
+
mode: mode,
|
|
341
|
+
reasons: "#{other_type.describe} is-a #{self_type.class_name}"
|
|
342
|
+
)
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
if self_type.class_name == "Class" && self_type.type_args.one?
|
|
346
|
+
return accepts_class_factory_instance_arg(self_type.type_args.first, other_type, mode)
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
Type::AcceptsResult.no(
|
|
350
|
+
mode: mode,
|
|
351
|
+
reasons: "Nominal[#{self_type.class_name}] rejects #{other_type.class}"
|
|
352
|
+
)
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def accepts_class_factory_instance_arg(formal, factory, mode)
|
|
356
|
+
if factory.class_name
|
|
357
|
+
named = accepts(formal, Type::Combinator.nominal_of(factory.class_name), mode: mode)
|
|
358
|
+
return named.with_reason("matched the factory class name") if named.yes?
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
accepts(formal, Type::Combinator.nominal_of(class_factory_base_name(factory)), mode: mode)
|
|
362
|
+
.with_reason("projected the factory instance type")
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def class_factory_base_name(factory)
|
|
366
|
+
factory.is_a?(Type::StructClass) ? "Struct" : "Data"
|
|
367
|
+
end
|
|
368
|
+
|
|
295
369
|
def accepts_nominal_from_nominal(self_type, other_type, mode)
|
|
296
370
|
class_result = class_subtype_result(
|
|
297
371
|
target_name: self_type.class_name,
|
|
@@ -753,6 +827,63 @@ module Rigor
|
|
|
753
827
|
Type::AcceptsResult.no(mode: mode, reasons: reason)
|
|
754
828
|
end
|
|
755
829
|
|
|
830
|
+
def accepts_result(self_type, other_type, mode)
|
|
831
|
+
case other_type
|
|
832
|
+
when Type::Result
|
|
833
|
+
combine_arg_results(
|
|
834
|
+
[
|
|
835
|
+
accepts(self_type.ok_type, other_type.ok_type, mode: mode),
|
|
836
|
+
accepts(self_type.err_type, other_type.err_type, mode: mode)
|
|
837
|
+
],
|
|
838
|
+
mode
|
|
839
|
+
)
|
|
840
|
+
when Type::Nominal
|
|
841
|
+
cls = other_type.class_name.sub(/\A::/, "")
|
|
842
|
+
if %w[Dry::Monads::Result Result].include?(cls) && other_type.type_args.size == 2
|
|
843
|
+
combine_arg_results(
|
|
844
|
+
[
|
|
845
|
+
accepts(self_type.ok_type, other_type.type_args[0], mode: mode),
|
|
846
|
+
accepts(self_type.err_type, other_type.type_args[1], mode: mode)
|
|
847
|
+
],
|
|
848
|
+
mode
|
|
849
|
+
)
|
|
850
|
+
else
|
|
851
|
+
Type::AcceptsResult.no(mode: mode, reasons: "Result does not accept Nominal[#{other_type.class_name}]")
|
|
852
|
+
end
|
|
853
|
+
else
|
|
854
|
+
Type::AcceptsResult.no(
|
|
855
|
+
mode: mode,
|
|
856
|
+
reasons: "Result does not accept #{other_type.class}"
|
|
857
|
+
)
|
|
858
|
+
end
|
|
859
|
+
end
|
|
860
|
+
|
|
861
|
+
def accepts_maybe(self_type, other_type, mode)
|
|
862
|
+
case other_type
|
|
863
|
+
when Type::Maybe
|
|
864
|
+
accepts(self_type.value_type, other_type.value_type, mode: mode)
|
|
865
|
+
when Type::Nominal
|
|
866
|
+
cls = other_type.class_name.sub(/\A::/, "")
|
|
867
|
+
if %w[Dry::Monads::Maybe Maybe].include?(cls) && other_type.type_args.size == 1
|
|
868
|
+
accepts(self_type.value_type, other_type.type_args[0], mode: mode)
|
|
869
|
+
else
|
|
870
|
+
Type::AcceptsResult.no(mode: mode, reasons: "Maybe does not accept Nominal[#{other_type.class_name}]")
|
|
871
|
+
end
|
|
872
|
+
else
|
|
873
|
+
Type::AcceptsResult.no(
|
|
874
|
+
mode: mode,
|
|
875
|
+
reasons: "Maybe does not accept #{other_type.class}"
|
|
876
|
+
)
|
|
877
|
+
end
|
|
878
|
+
end
|
|
879
|
+
|
|
880
|
+
def accepts_class_factory(self_type, other_type, mode)
|
|
881
|
+
Type::AcceptsResult.no(
|
|
882
|
+
mode: mode,
|
|
883
|
+
reasons: "#{self_type.describe} rejects #{other_type.class}"
|
|
884
|
+
)
|
|
885
|
+
end
|
|
886
|
+
|
|
756
887
|
# Uses Ruby's actual class hierarchy via Object.const_get to answer "is D a subclass of C?" for core,
|
|
757
888
|
# stdlib, and application classes. When either name fails to resolve we surface "maybe": the caller
|
|
758
889
|
# (overload selector) treats yes/maybe identically, so the conservative answer keeps overload
|
|
@@ -791,14 +922,35 @@ module Rigor
|
|
|
791
922
|
end
|
|
792
923
|
end
|
|
793
924
|
|
|
925
|
+
# Resolves a class name taken from the analysed program without triggering `const_missing`
|
|
926
|
+
# or executing a pending autoload.
|
|
927
|
+
#
|
|
928
|
+
# `const_defined?(part, false)` answers true for a REGISTERED-BUT-NOT-YET-TRIGGERED autoload,
|
|
929
|
+
# so on its own it does not mean "already in memory" and `const_get` would execute the target
|
|
930
|
+
# file inside the analyzer. `Module#autoload?` separates the two: it returns the registered
|
|
931
|
+
# path while the autoload is pending and nil once it has run, so declining on it declines
|
|
932
|
+
# exactly the dangerous case and still resolves a class genuinely loaded ([#680], [#689]).
|
|
933
|
+
#
|
|
934
|
+
# The rescue catches `StandardError`, `ScriptError` and `SystemExit`. `Interrupt`,
|
|
935
|
+
# `SignalException` and `NoMemoryError` stay uncaught on purpose.
|
|
794
936
|
def resolve_class(name)
|
|
795
|
-
|
|
796
|
-
|
|
937
|
+
return nil unless name.is_a?(::String)
|
|
938
|
+
|
|
939
|
+
parts = name.delete_prefix("::").split("::")
|
|
940
|
+
return nil if parts.empty?
|
|
941
|
+
|
|
942
|
+
mod = ::Object
|
|
943
|
+
parts.each do |part|
|
|
944
|
+
return nil unless mod.is_a?(::Module) && mod.const_defined?(part, false)
|
|
945
|
+
return nil if mod.autoload?(part)
|
|
946
|
+
|
|
947
|
+
mod = mod.const_get(part, false)
|
|
948
|
+
end
|
|
949
|
+
mod.is_a?(::Module) ? mod : nil
|
|
950
|
+
rescue ::StandardError, ::ScriptError, ::SystemExit
|
|
797
951
|
nil
|
|
798
952
|
end
|
|
799
953
|
end
|
|
800
|
-
# rubocop:enable Metrics/ClassLength
|
|
801
954
|
end
|
|
802
|
-
# rubocop:enable Metrics/ModuleLength
|
|
803
955
|
end
|
|
804
956
|
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
|
|
5
|
+
require_relative "../source/node_walker"
|
|
6
|
+
require_relative "block_parameter_binder"
|
|
7
|
+
|
|
8
|
+
module Rigor
|
|
9
|
+
module Inference
|
|
10
|
+
# The outer locals a block body can REBIND — the one name set that ADR-56's captured-local write-back
|
|
11
|
+
# (`StatementEvaluator#write_back_block_captures`), the escaping-block narrowing drop
|
|
12
|
+
# (`StatementEvaluator#drop_captured_narrowing`) and issue #587's per-element fold
|
|
13
|
+
# (`ExpressionTyper#per_element_captured_bindings`) all key on. It lives in one place so the four
|
|
14
|
+
# cannot disagree about what "captured" means.
|
|
15
|
+
#
|
|
16
|
+
# A write counts across every local-write form — plain `=` (`LocalVariableWriteNode`), the operator /
|
|
17
|
+
# `||=` / `&&=` compound forms, and a multi-assign target (`x, y = …` → `LocalVariableTargetNode` under
|
|
18
|
+
# a `MultiWriteNode`) — at ANY depth: a block is a closure, so a write inside a nested block binds the
|
|
19
|
+
# same outer variable. Block-introduced names (parameters, numbered parameters, `;`-locals) and names
|
|
20
|
+
# not bound in the outer scope are excluded; a write to either is not a captured rebind of an outer
|
|
21
|
+
# variable.
|
|
22
|
+
module CapturedLocals
|
|
23
|
+
LOCAL_WRITE_NODES = [
|
|
24
|
+
Prism::LocalVariableWriteNode,
|
|
25
|
+
Prism::LocalVariableOperatorWriteNode,
|
|
26
|
+
Prism::LocalVariableOrWriteNode,
|
|
27
|
+
Prism::LocalVariableAndWriteNode,
|
|
28
|
+
Prism::LocalVariableTargetNode
|
|
29
|
+
].freeze
|
|
30
|
+
|
|
31
|
+
module_function
|
|
32
|
+
|
|
33
|
+
# @param block_node [Prism::BlockNode]
|
|
34
|
+
# @param base_scope [Rigor::Scope] the call-site scope the block closes over.
|
|
35
|
+
# @return [Array<Symbol>] the captured names the body writes, each once, in first-write order.
|
|
36
|
+
def writes(block_node, base_scope)
|
|
37
|
+
body = block_node.body
|
|
38
|
+
return [] if body.nil?
|
|
39
|
+
|
|
40
|
+
introduced = introduced_locals(block_node)
|
|
41
|
+
outer_writes = []
|
|
42
|
+
Source::NodeWalker.each(body) do |descendant|
|
|
43
|
+
next unless LOCAL_WRITE_NODES.any? { |klass| descendant.is_a?(klass) }
|
|
44
|
+
next if introduced.include?(descendant.name)
|
|
45
|
+
next unless base_scope.locals.key?(descendant.name)
|
|
46
|
+
|
|
47
|
+
outer_writes << descendant.name
|
|
48
|
+
end
|
|
49
|
+
outer_writes.uniq
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Names the block itself introduces: parameters (numbered parameters included, via
|
|
53
|
+
# `BlockParameterBinder`) plus the explicit `;`-prefixed block-locals on `BlockParametersNode`.
|
|
54
|
+
def introduced_locals(block_node)
|
|
55
|
+
introduced = Set.new(BlockParameterBinder.new.bind(block_node).keys)
|
|
56
|
+
params_root = block_node.parameters
|
|
57
|
+
params_root.locals.each { |loc| introduced << loc.name } if params_root.is_a?(Prism::BlockParametersNode)
|
|
58
|
+
introduced
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|