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
|
@@ -29,7 +29,10 @@ require_relative "method_dispatcher/regexp_folding"
|
|
|
29
29
|
require_relative "method_dispatcher/cgi_folding"
|
|
30
30
|
require_relative "method_dispatcher/uri_folding"
|
|
31
31
|
require_relative "method_dispatcher/set_folding"
|
|
32
|
+
require_relative "method_dispatcher/json_folding"
|
|
32
33
|
require_relative "method_dispatcher/kernel_dispatch"
|
|
34
|
+
require_relative "method_dispatcher/universal_object_dispatch"
|
|
35
|
+
require_relative "method_dispatcher/singleton_mixin_dispatch"
|
|
33
36
|
require_relative "method_dispatcher/method_folding"
|
|
34
37
|
|
|
35
38
|
module Rigor
|
|
@@ -119,6 +122,11 @@ module Rigor
|
|
|
119
122
|
# skip this tier.
|
|
120
123
|
plugin_result = try_plugin_contribution(call_node, scope, receiver_type)
|
|
121
124
|
if plugin_result
|
|
125
|
+
# Issue #653 — this tier answering is what makes the site's type the plugin's rather than the
|
|
126
|
+
# RBS's, and `call.undefined-method` reads that record instead of re-deciding from the receiver's
|
|
127
|
+
# (possibly partial) signature. Recorded for EVERY plugin answer, dynamic or precise: the
|
|
128
|
+
# incoherence the record closes is about which subsystem typed the call, not about the type.
|
|
129
|
+
scope&.record_plugin_typed_call(call_node)
|
|
122
130
|
if plugin_result.is_a?(Type::Dynamic)
|
|
123
131
|
scope&.record_dynamic_origin(call_node, DynamicOrigin::FRAMEWORK_DSL_BOUNDARY)
|
|
124
132
|
end
|
|
@@ -144,6 +152,12 @@ module Rigor
|
|
|
144
152
|
static_refinement = try_static_refinement(receiver_type, method_name, arg_types)
|
|
145
153
|
return static_refinement if static_refinement
|
|
146
154
|
|
|
155
|
+
# `Foo.instance` for a class that includes the stdlib `Singleton` mixin. Ruby grows that method through
|
|
156
|
+
# an `included` hook and the upstream RBS leaves `Singleton::SingletonClassMethods` empty, so nothing
|
|
157
|
+
# below can answer it. See {SingletonMixinDispatch} for why the value matters more than the site count.
|
|
158
|
+
singleton_mixin = SingletonMixinDispatch.try_dispatch(context)
|
|
159
|
+
return singleton_mixin if singleton_mixin
|
|
160
|
+
|
|
147
161
|
rbs_result = RbsDispatch.try_dispatch(context)
|
|
148
162
|
if rbs_result
|
|
149
163
|
record_boundary_cross_if_applicable(receiver_type, method_name, rbs_result, environment)
|
|
@@ -218,6 +232,15 @@ module Rigor
|
|
|
218
232
|
stub_result = try_synthesized_stub_type(receiver_type, environment)
|
|
219
233
|
return stub_result if stub_result
|
|
220
234
|
|
|
235
|
+
# The receiver-independent `BasicObject` / `Object` / `Kernel` selectors, for a receiver no tier
|
|
236
|
+
# above could name. `x.nil?` is `bool` whatever `x` is; without this tier it was `Dynamic[Top]`
|
|
237
|
+
# like every other call on an untyped receiver. Sits here, below every real resolution tier, so a
|
|
238
|
+
# nameable receiver always answers from its own signature first — this only ever replaces a
|
|
239
|
+
# `Dynamic[Top]` the typer was about to produce. See {UniversalObjectDispatch} for the table and
|
|
240
|
+
# for the selectors deliberately left out of it.
|
|
241
|
+
universal_result = UniversalObjectDispatch.try_dispatch(context)
|
|
242
|
+
return universal_result if universal_result
|
|
243
|
+
|
|
221
244
|
# Slice 7 phase 10 — user-class ancestor fallback. When the receiver is `Nominal[T]` or
|
|
222
245
|
# `Singleton[T]` for a class not in the RBS environment (typically a user-defined class),
|
|
223
246
|
# retry the dispatch against the implicit ancestor: `Nominal[Object]` for instance
|
|
@@ -305,9 +328,13 @@ module Rigor
|
|
|
305
328
|
# on. `Nominal[X]` looks up instance methods on X; `Singleton[X]` looks up singleton
|
|
306
329
|
# methods on X. Other carriers return `[nil, nil]` so the tier declines.
|
|
307
330
|
def discovered_method_lookup(receiver_type)
|
|
331
|
+
# #525 — the ADR-48 member carriers name their class too: a `Line = Struct.new(...) do ... end`
|
|
332
|
+
# value is a StructInstance whose block-def methods are discovered under "Line".
|
|
308
333
|
case receiver_type
|
|
309
|
-
when Type::Nominal
|
|
310
|
-
|
|
334
|
+
when Type::Nominal, Type::StructInstance, Type::DataInstance
|
|
335
|
+
[receiver_type.class_name, :instance]
|
|
336
|
+
when Type::Singleton, Type::StructClass, Type::DataClass
|
|
337
|
+
[receiver_type.class_name, :singleton]
|
|
311
338
|
else [nil, nil]
|
|
312
339
|
end
|
|
313
340
|
end
|
|
@@ -711,7 +738,7 @@ module Rigor
|
|
|
711
738
|
#
|
|
712
739
|
# The precise-tier folders, consulted in order via the uniform `_DispatchTier` interface
|
|
713
740
|
# (`try_dispatch(CallContext) -> Type?`). Order is significant: ConstantFolding's
|
|
714
|
-
# exact-value folds win first, the
|
|
741
|
+
# exact-value folds win first, the stdlib singleton folders sit in the middle (each
|
|
715
742
|
# gates on a distinct `Singleton` receiver, so their relative order is immaterial), and
|
|
716
743
|
# BlockFolding runs last because its rules only apply to block-taking calls — the cheaper
|
|
717
744
|
# arity folds above it filter the common cases first. Adding a precise tier is a one-line
|
|
@@ -721,13 +748,13 @@ module Rigor
|
|
|
721
748
|
].freeze)
|
|
722
749
|
private_constant :PRECISE_TIERS_HEAD
|
|
723
750
|
|
|
724
|
-
# ADR-53 re-review follow-up (gate-by-held-key applied to the built-in tiers): the
|
|
725
|
-
#
|
|
751
|
+
# ADR-53 re-review follow-up (gate-by-held-key applied to the built-in tiers): the stdlib
|
|
752
|
+
# singleton folders are mutually exclusive — each fires only on `Singleton[<its
|
|
726
753
|
# class>]`, the first check in every `try_dispatch` — so at most one can match a given
|
|
727
754
|
# receiver and their relative trial order was never observable. Compiling them into a
|
|
728
|
-
# class-name table turns
|
|
755
|
+
# class-name table turns nine no-op trials per call into one Hash read, skipped entirely
|
|
729
756
|
# when the receiver is not a `Singleton` (the overwhelmingly common case). The table sits
|
|
730
|
-
# where the eight sat in the old flat list: after ShapeDispatch, before KernelDispatch.
|
|
757
|
+
# where the original eight sat in the old flat list: after ShapeDispatch, before KernelDispatch.
|
|
731
758
|
STDLIB_SINGLETON_FOLDERS = Ractor.make_shareable({
|
|
732
759
|
"File" => FileFolding,
|
|
733
760
|
"Shellwords" => ShellwordsFolding,
|
|
@@ -736,7 +763,8 @@ module Rigor
|
|
|
736
763
|
"Regexp" => RegexpFolding,
|
|
737
764
|
"CGI" => CGIFolding,
|
|
738
765
|
"URI" => URIFolding,
|
|
739
|
-
"Set" => SetFolding
|
|
766
|
+
"Set" => SetFolding,
|
|
767
|
+
"JSON" => JSONFolding
|
|
740
768
|
}.freeze)
|
|
741
769
|
private_constant :STDLIB_SINGLETON_FOLDERS
|
|
742
770
|
|
|
@@ -975,7 +1003,9 @@ module Rigor
|
|
|
975
1003
|
return nil unless arg_types.size == 1
|
|
976
1004
|
|
|
977
1005
|
parent = arg_types.first
|
|
978
|
-
|
|
1006
|
+
factory_parent = parent.is_a?(Type::StructClass) || parent.is_a?(Type::DataClass)
|
|
1007
|
+
return nil if factory_parent && context&.call_node&.block
|
|
1008
|
+
return parent if parent.is_a?(Type::Singleton) || factory_parent
|
|
979
1009
|
|
|
980
1010
|
nil
|
|
981
1011
|
end
|
|
@@ -1022,8 +1052,8 @@ module Rigor
|
|
|
1022
1052
|
|
|
1023
1053
|
# `Array.new(n, value)` and `Array.new(n)` (no value, default `nil`) lift to a per-position
|
|
1024
1054
|
# `Tuple[…]` when `n` is a small `Constant<Integer>`. Cap at `ARRAY_NEW_TUPLE_LIMIT` (16)
|
|
1025
|
-
# so a `Array.new(1_000_000)` does not balloon the carrier; oversize calls fall
|
|
1026
|
-
#
|
|
1055
|
+
# so a `Array.new(1_000_000)` does not balloon the carrier; oversize calls fall through to
|
|
1056
|
+
# the element-parameter seed below.
|
|
1027
1057
|
#
|
|
1028
1058
|
# #317 — `Array.new(n) { |i| ... }` fills every slot from the BLOCK's return type instead
|
|
1029
1059
|
# of the two-arg `(n, default_value)` overload's `nil` fill. The block and the trailing
|
|
@@ -1039,10 +1069,121 @@ module Rigor
|
|
|
1039
1069
|
return nil if arg_types.empty? || arg_types.size > 2
|
|
1040
1070
|
|
|
1041
1071
|
size = array_new_size(arg_types.first)
|
|
1042
|
-
|
|
1072
|
+
if size && !size.negative? && size <= ARRAY_NEW_TUPLE_LIMIT
|
|
1073
|
+
return Type::Combinator.tuple_of(*Array.new(size, block_type || array_new_fill(arg_types[1])))
|
|
1074
|
+
end
|
|
1075
|
+
|
|
1076
|
+
array_new_seed(arg_types, block_type)
|
|
1077
|
+
end
|
|
1078
|
+
|
|
1079
|
+
# The carrier for a constructor whose size is not a small literal — `Array.new(n)`,
|
|
1080
|
+
# `Array.new(n, v)`, `Array.new(n) { … }`.
|
|
1081
|
+
#
|
|
1082
|
+
# This must never be a BARE `Array`. A nominal with no type argument carries no element
|
|
1083
|
+
# arm, and the ADR-56 block/loop seams read exactly that as an ELEMENTLESS seed — a fresh
|
|
1084
|
+
# accumulator whose every store the seam saw — so they CLOSE the carrier over the block's
|
|
1085
|
+
# own stores. `acc = Array.new(n); [1].each { acc.push(1) }` then read `Array[1]` for an
|
|
1086
|
+
# array whose other `n` slots hold `nil`, and `acc = Array.new(n, "x")` read `Array[1]` and
|
|
1087
|
+
# drew `undefined method 'upcase'` on the correct `acc.first.upcase` (issue #615). Handing
|
|
1088
|
+
# the seams a real element arm is what puts the constructor under the #586 rule, which
|
|
1089
|
+
# keeps every seed arm through the join.
|
|
1090
|
+
#
|
|
1091
|
+
# The element itself is the fill value's / block result's type, value-pin widened for the
|
|
1092
|
+
# reason `hash_new_lift` widens its default: the slots are rewritten over the array's
|
|
1093
|
+
# lifetime, so pinning the parameter to the literal would let a later `a[i] == "x"`
|
|
1094
|
+
# constant-fold.
|
|
1095
|
+
#
|
|
1096
|
+
# **The no-fill form seeds the GRADUAL element, not `nil`.** `Array.new(n)` really does put
|
|
1097
|
+
# a `nil` in every slot, but a `Nominal` seed gets DECLARED-carrier semantics downstream:
|
|
1098
|
+
# the #586 join keeps a seed arm forever, and `MutationWidening#widen_for_mutator` declines
|
|
1099
|
+
# a `Nominal` outright, so no whole-array rewrite — `[]=`, `fill`, `map!`, `replace`,
|
|
1100
|
+
# `concat` — can ever retract it. The placeholder would become a permanent nil possibility
|
|
1101
|
+
# over the allocate-then-fill idiom that is the whole point of the constructor:
|
|
1102
|
+
#
|
|
1103
|
+
# dp = Array.new(xs.size); dp[0] = 1
|
|
1104
|
+
# (1...n).each { |i| dp[i] = dp[i - 1] + xs[i] } # `undefined method '+' for nil`
|
|
1105
|
+
#
|
|
1106
|
+
# buf = Array.new(256); 256.times { |i| buf[i] = i.to_s }
|
|
1107
|
+
# buf.each { |c| c.upcase } # possible nil receiver
|
|
1108
|
+
#
|
|
1109
|
+
# Both are correct Ruby, and both went from quiet to an ERROR on the `nil` seed. The true
|
|
1110
|
+
# positive the `nil` would buy — `Array.new(n).first.upcase` on an array nothing ever wrote
|
|
1111
|
+
# — is not worth the dominant idiom, so the no-fill form stays gradual whatever the size
|
|
1112
|
+
# argument reads as. (That also happens to absorb Ruby's array-convertible COPY overload:
|
|
1113
|
+
# `Array.new([1, 2])` is `[1, 2]`, not two nils.)
|
|
1114
|
+
def array_new_seed(arg_types, block_type)
|
|
1115
|
+
fill = block_type || arg_types[1]
|
|
1116
|
+
return array_seed_of(Type::Combinator.untyped) if fill.nil?
|
|
1117
|
+
|
|
1118
|
+
element = array_new_element(fill)
|
|
1119
|
+
# `Array.new(n, nil)` is the same allocate-then-fill idiom as `Array.new(n)`, spelled with the
|
|
1120
|
+
# placeholder made explicit — concurrent-ruby writes `@Resolutions = ::Array.new(count, nil)` and
|
|
1121
|
+
# fills it by index. A nil-only element would be a PERMANENT nil arm (a Nominal carrier's element is
|
|
1122
|
+
# never widened by a later `[]=` / `fill` / `replace`), so every read of a filled slot would draw
|
|
1123
|
+
# `undefined method '…' for nil` on correct code. Same trade as the no-fill form above: the seam
|
|
1124
|
+
# needs an arm, not a precise one.
|
|
1125
|
+
element = Type::Combinator.untyped if nil_only_element?(element)
|
|
1126
|
+
array_seed_of(element)
|
|
1127
|
+
end
|
|
1043
1128
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1129
|
+
# Whether the widened fill leaves nothing but `nil` behind — see {#array_new_seed}.
|
|
1130
|
+
def nil_only_element?(element)
|
|
1131
|
+
members = element.is_a?(Type::Union) ? element.members : [element]
|
|
1132
|
+
members.all? do |member|
|
|
1133
|
+
(member.is_a?(Type::Constant) && member.value.nil?) ||
|
|
1134
|
+
(member.is_a?(Type::Nominal) && member.class_name == "NilClass")
|
|
1135
|
+
end
|
|
1136
|
+
end
|
|
1137
|
+
|
|
1138
|
+
def array_seed_of(element)
|
|
1139
|
+
Type::Combinator.nominal_of("Array", type_args: [element])
|
|
1140
|
+
end
|
|
1141
|
+
|
|
1142
|
+
# A fill / block result as an element PARAMETER. Value pinning is dropped, and a literal
|
|
1143
|
+
# container widens to its nominal: `Array.new(n) { [] }` builds n INDEPENDENT arrays that
|
|
1144
|
+
# the program then appends to, and `Array[Tuple[]]` claims every one of them stays empty —
|
|
1145
|
+
# the adjacency-list idiom would read `adj[i].first` as `nil`. Both conversions go through
|
|
1146
|
+
# {MutationWidening}'s own helpers, the single owner of "literal container → parameterised
|
|
1147
|
+
# nominal" that `MemberShapeProjection` already borrows.
|
|
1148
|
+
#
|
|
1149
|
+
# The walk is **recursive**, through `Union` members and through a container's own elements /
|
|
1150
|
+
# values alike. Every one of those positions is a fresh object per constructed slot that the
|
|
1151
|
+
# program goes on to mutate, so stopping at the outermost level only moved the wrong-precise
|
|
1152
|
+
# answer one level in: `a = Array.new(n) { [[1]] }; a[0][0] << 5` left `Array[Array[[1]]]`
|
|
1153
|
+
# and folded `a[0][0].last == 5` always-falsey on correct code. Judging a `Union` wholesale
|
|
1154
|
+
# was the same defect one axis over — `Array.new(n) { flag ? [1] : [2] }` kept both arms as
|
|
1155
|
+
# fixed-arity tuples.
|
|
1156
|
+
#
|
|
1157
|
+
# `depth` is defensive only. A `Type` carrier is an immutable value object assembled
|
|
1158
|
+
# bottom-up (`Tuple.new(elements)`, `HashShape.new(pairs)`), so it cannot contain itself and
|
|
1159
|
+
# this walk is bounded by the source literal's own nesting; the cap costs nothing on any
|
|
1160
|
+
# literal a person writes and stops a malformed carrier from recursing forever.
|
|
1161
|
+
ARRAY_NEW_FILL_DEPTH_LIMIT = 8
|
|
1162
|
+
private_constant :ARRAY_NEW_FILL_DEPTH_LIMIT
|
|
1163
|
+
|
|
1164
|
+
def array_new_element(type, depth = 0)
|
|
1165
|
+
return Type::Combinator.untyped if depth > ARRAY_NEW_FILL_DEPTH_LIMIT
|
|
1166
|
+
|
|
1167
|
+
case type
|
|
1168
|
+
when Type::Tuple then MutationWidening.widen_tuple(recursed_tuple(type, depth))
|
|
1169
|
+
when Type::HashShape then MutationWidening.widen_hash_shape(recursed_hash_shape(type, depth))
|
|
1170
|
+
when Type::Union then Type::Combinator.union(*type.members.map { |m| array_new_element(m, depth + 1) })
|
|
1171
|
+
else Type::Combinator.widen_value_pinned(type)
|
|
1172
|
+
end
|
|
1173
|
+
end
|
|
1174
|
+
|
|
1175
|
+
# The same tuple / hash shape with every element / value already widened, so the
|
|
1176
|
+
# {MutationWidening} helper that consumes it sees nominals where the literal had pins. The
|
|
1177
|
+
# rebuilt carrier is a throwaway argument — never surfaced — and the helpers read only
|
|
1178
|
+
# `elements` / `pairs`, so no policy field rides on it.
|
|
1179
|
+
def recursed_tuple(type, depth)
|
|
1180
|
+
Type::Combinator.tuple_of(*type.elements.map { |e| array_new_element(e, depth + 1) })
|
|
1181
|
+
end
|
|
1182
|
+
|
|
1183
|
+
def recursed_hash_shape(type, depth)
|
|
1184
|
+
Type::Combinator.hash_shape_of(
|
|
1185
|
+
type.pairs.transform_values { |v| array_new_element(v, depth + 1) }
|
|
1186
|
+
)
|
|
1046
1187
|
end
|
|
1047
1188
|
|
|
1048
1189
|
def array_new_size(type)
|
|
@@ -332,8 +332,7 @@ module Rigor
|
|
|
332
332
|
when :rest_positional
|
|
333
333
|
Type::Combinator.nominal_of("Array", type_args: [translated])
|
|
334
334
|
when :rest_keyword
|
|
335
|
-
|
|
336
|
-
Type::Combinator.nominal_of("Hash", type_args: [symbol_nominal, translated])
|
|
335
|
+
Type::Combinator.nominal_of("Hash", type_args: [Type::Combinator.nominal_of("Symbol"), translated])
|
|
337
336
|
else
|
|
338
337
|
translated
|
|
339
338
|
end
|
|
@@ -344,7 +343,8 @@ module Rigor
|
|
|
344
343
|
RbsTypeTranslator.translate(
|
|
345
344
|
rbs_type,
|
|
346
345
|
self_type: self_type,
|
|
347
|
-
instance_type: instance_type
|
|
346
|
+
instance_type: instance_type,
|
|
347
|
+
alias_expander: @environment&.rbs_loader
|
|
348
348
|
)
|
|
349
349
|
rescue StandardError
|
|
350
350
|
Type::Combinator.untyped
|