rigortype 0.4.7 → 0.4.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 +3 -1
- data/data/core_overlay/bigdecimal.rbs +33 -0
- data/data/core_overlay/resolv.rbs +30 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +29 -0
- data/data/gem_overlay/addressable/addressable.rbs +40 -0
- data/docs/handbook/01-getting-started.md +1 -1
- data/docs/handbook/11-sig-gen.md +35 -0
- data/docs/install.md +20 -0
- data/docs/manual/01-installation.md +67 -26
- data/docs/manual/02-cli-reference.md +307 -91
- data/docs/manual/03-configuration.md +41 -3
- data/docs/manual/04-diagnostics.md +5 -4
- data/docs/manual/08-skills.md +11 -0
- data/docs/manual/09-editor-integration.md +8 -0
- data/docs/manual/14-rails-quickstart.md +1 -1
- data/docs/manual/19-effect-labels.md +3 -4
- data/docs/manual/plugins/rigor-actionpack.md +15 -6
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +3 -0
- data/docs/manual/plugins/rigor-sorbet.md +17 -3
- data/lib/rigor/analysis/check_rules/always_raises_shield.rb +147 -0
- data/lib/rigor/analysis/check_rules/call_facts.rb +58 -0
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +8 -0
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +13 -2
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +6 -0
- data/lib/rigor/analysis/check_rules/plugin_api_surface.rb +195 -0
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +40 -11
- data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +27 -1
- data/lib/rigor/analysis/check_rules/replaced_singleton_defs.rb +96 -0
- data/lib/rigor/analysis/check_rules/rule_ids.rb +8 -1
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules.rb +598 -99
- data/lib/rigor/analysis/crash_signature.rb +43 -1
- data/lib/rigor/analysis/dependency_recorder.rb +233 -76
- data/lib/rigor/analysis/incremental.rb +11 -1
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +2 -1
- data/lib/rigor/analysis/incremental_session.rb +279 -37
- data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -3
- data/lib/rigor/analysis/rule_catalog.rb +37 -1
- data/lib/rigor/analysis/run_cache_key.rb +27 -3
- data/lib/rigor/analysis/run_disclosure_credit.rb +26 -6
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +4 -0
- data/lib/rigor/analysis/runner/declaration_position.rb +7 -7
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +72 -20
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +3 -8
- data/lib/rigor/analysis/runner/pool_coordinator.rb +26 -5
- data/lib/rigor/analysis/runner/project_pre_passes.rb +4 -37
- data/lib/rigor/analysis/runner.rb +222 -39
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +61 -22
- data/lib/rigor/analysis/worker_session.rb +42 -41
- data/lib/rigor/bleeding_edge.rb +19 -4
- data/lib/rigor/cache/file_digest.rb +39 -31
- data/lib/rigor/cache/incremental_snapshot.rb +66 -7
- data/lib/rigor/cache/rbs_descriptor.rb +37 -2
- data/lib/rigor/cache/rbs_environment.rb +5 -1
- data/lib/rigor/cli/baseline_command.rb +15 -2
- data/lib/rigor/cli/check_command.rb +4 -2
- data/lib/rigor/cli/effects_snapshot_command.rb +8 -1
- data/lib/rigor/cli/lens_command.rb +17 -2
- data/lib/rigor/cli/sig_gen_command.rb +8 -0
- data/lib/rigor/cli/skill_describe.rb +1 -0
- data/lib/rigor/cli.rb +5 -2
- data/lib/rigor/config_audit.rb +32 -18
- data/lib/rigor/configuration/severity_profile.rb +9 -0
- data/lib/rigor/configuration.rb +32 -5
- data/lib/rigor/effects/collector.rb +53 -21
- data/lib/rigor/environment/bundle_sig_discovery.rb +63 -28
- data/lib/rigor/environment/constant_type_cache_holder.rb +5 -0
- data/lib/rigor/environment/default_libraries.rb +17 -0
- data/lib/rigor/environment/deferred_signature_plan.rb +104 -6
- data/lib/rigor/environment/hkt_registry_holder.rb +6 -0
- data/lib/rigor/environment/rbs_collection_discovery.rb +123 -8
- data/lib/rigor/environment/rbs_coverage_report.rb +12 -11
- data/lib/rigor/environment/rbs_hierarchy.rb +6 -0
- data/lib/rigor/environment/rbs_loader.rb +312 -63
- data/lib/rigor/environment.rb +131 -35
- data/lib/rigor/inference/acceptance.rb +50 -15
- data/lib/rigor/inference/alias_mutation_floor/foreign_locals.rb +117 -0
- data/lib/rigor/inference/alias_mutation_floor/memos.rb +85 -0
- data/lib/rigor/inference/alias_mutation_floor/reach.rb +119 -0
- data/lib/rigor/inference/alias_mutation_floor/site_collector.rb +252 -0
- data/lib/rigor/inference/alias_mutation_floor.rb +339 -0
- data/lib/rigor/inference/anonymous_meta_class.rb +18 -0
- data/lib/rigor/inference/block_auto_splat.rb +1 -1
- data/lib/rigor/inference/block_run_count.rb +9 -2
- data/lib/rigor/inference/builtins/array_catalog.rb +2 -2
- data/lib/rigor/inference/callee_mutation_chain.rb +1 -1
- data/lib/rigor/inference/captured_locals.rb +2 -0
- data/lib/rigor/inference/census_foreign_block.rb +354 -0
- data/lib/rigor/inference/clause_widening.rb +1 -1
- data/lib/rigor/inference/closure_escape_analyzer.rb +11 -4
- data/lib/rigor/inference/construction_escape.rb +56 -15
- data/lib/rigor/inference/declaration_walk.rb +4 -0
- data/lib/rigor/inference/def_node_resolver.rb +32 -14
- data/lib/rigor/inference/def_return_typer.rb +1 -1
- data/lib/rigor/inference/definer_resolution.rb +15 -7
- data/lib/rigor/inference/definers_index.rb +72 -16
- data/lib/rigor/inference/error_info.rb +16 -0
- data/lib/rigor/inference/expression_typer.rb +1459 -287
- data/lib/rigor/inference/flow_tracer.rb +31 -14
- data/lib/rigor/inference/fork_map.rb +3 -0
- data/lib/rigor/inference/fresh_frame_blocks.rb +52 -1
- data/lib/rigor/inference/global_write_census.rb +8 -6
- data/lib/rigor/inference/in_effect_refinements.rb +7 -0
- data/lib/rigor/inference/indexed_narrowing.rb +57 -8
- data/lib/rigor/inference/ivar_writer_census.rb +106 -11
- data/lib/rigor/inference/key_presence_guard.rb +51 -25
- data/lib/rigor/inference/last_line/self_evidence.rb +30 -0
- data/lib/rigor/inference/last_line.rb +5 -2
- data/lib/rigor/inference/loop_value_record.rb +63 -15
- data/lib/rigor/inference/match_rebinding/frame.rb +11 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +74 -18
- data/lib/rigor/inference/match_rebinding.rb +21 -8
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +43 -29
- data/lib/rigor/inference/method_dispatcher/class_keyed_block_params.rb +65 -0
- data/lib/rigor/inference/method_dispatcher/core_enumerable_receiver.rb +44 -0
- data/lib/rigor/inference/method_dispatcher/declared_constructor.rb +151 -0
- data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/interface_shape.rb +181 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +12 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +409 -67
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +384 -64
- data/lib/rigor/inference/method_dispatcher/singleton_type_params.rb +104 -0
- data/lib/rigor/inference/method_dispatcher.rb +66 -18
- data/lib/rigor/inference/method_parameter_binder.rb +136 -56
- data/lib/rigor/inference/module_function_state/spellings.rb +2 -2
- data/lib/rigor/inference/module_function_state.rb +13 -2
- data/lib/rigor/inference/multi_target_binder.rb +1 -1
- data/lib/rigor/inference/mutation_widening.rb +44 -4
- data/lib/rigor/inference/narrowing.rb +201 -27
- data/lib/rigor/inference/operand_effects.rb +43 -2
- data/lib/rigor/inference/optimistic_origin.rb +133 -8
- data/lib/rigor/inference/origin_lookup.rb +1 -1
- data/lib/rigor/inference/precision_scanner.rb +1 -1
- data/lib/rigor/inference/project_method_ownership.rb +28 -10
- data/lib/rigor/inference/project_patched_methods.rb +65 -13
- data/lib/rigor/inference/project_patched_scanner.rb +120 -25
- data/lib/rigor/inference/project_patched_visibility.rb +39 -3
- data/lib/rigor/inference/rbs_type_translator.rb +68 -15
- data/lib/rigor/inference/receiver_writer_call.rb +67 -0
- data/lib/rigor/inference/recursion_rebinding.rb +157 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +56 -18
- data/lib/rigor/inference/refined_dispatch/own_method.rb +51 -17
- data/lib/rigor/inference/refined_dispatch.rb +13 -0
- data/lib/rigor/inference/reflective_ivar_write.rb +19 -11
- data/lib/rigor/inference/remover_holder_scan.rb +76 -22
- data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +13 -6
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +12 -11
- data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +188 -28
- data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +136 -9
- data/lib/rigor/inference/scope_indexer.rb +2079 -183
- data/lib/rigor/inference/self_call_ivar_writes.rb +26 -7
- data/lib/rigor/inference/self_preserving_block.rb +87 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +41 -6
- data/lib/rigor/inference/singleton_object_constant.rb +32 -2
- data/lib/rigor/inference/statement_evaluator.rb +970 -250
- data/lib/rigor/inference/string_mutation.rb +14 -0
- data/lib/rigor/inference/synthetic_method_scanner.rb +35 -11
- data/lib/rigor/inference/toplevel_def_census.rb +14 -6
- data/lib/rigor/inference/unknown_store_widening.rb +1 -1
- data/lib/rigor/inference/untyped_rejoin.rb +1 -1
- data/lib/rigor/inference/void_tail_summary.rb +29 -10
- data/lib/rigor/lens/claims/comparison.rb +78 -0
- data/lib/rigor/lens/claims/request.rb +131 -0
- data/lib/rigor/lens/claims/type_reader.rb +216 -0
- data/lib/rigor/lens/claims.rb +208 -0
- data/lib/rigor/lens/contract_reader.rb +5 -0
- data/lib/rigor/lens/document.rb +1 -1
- data/lib/rigor/lens/file_reader.rb +21 -8
- data/lib/rigor/lens/run.rb +8 -0
- data/lib/rigor/lens.rb +1 -1
- data/lib/rigor/library_probe.rb +68 -0
- data/lib/rigor/memo_audit.rb +282 -0
- data/lib/rigor/plugin/base.rb +15 -2
- data/lib/rigor/plugin/io_boundary.rb +25 -3
- data/lib/rigor/plugin/manifest.rb +7 -3
- data/lib/rigor/plugin/registry.rb +13 -2
- data/lib/rigor/plugin/trust_policy.rb +46 -0
- data/lib/rigor/protection/mutation_cache.rb +4 -1
- data/lib/rigor/reflection/constant_ancestors.rb +79 -22
- data/lib/rigor/reflection/declaration_collectors.rb +55 -0
- data/lib/rigor/reflection/declarations.rb +58 -0
- data/lib/rigor/reflection/project_method.rb +177 -0
- data/lib/rigor/reflection.rb +2 -0
- data/lib/rigor/rooted_glob.rb +18 -1
- data/lib/rigor/run_scoped_slots.rb +20 -20
- data/lib/rigor/scope/discovery_index.rb +149 -2
- data/lib/rigor/scope/resolution_chain/relevance.rb +14 -1
- data/lib/rigor/scope/resolution_chain.rb +900 -55
- data/lib/rigor/scope.rb +307 -34
- data/lib/rigor/sig_gen/bot_proposal_guard.rb +306 -0
- data/lib/rigor/sig_gen/classification.rb +14 -1
- data/lib/rigor/sig_gen/generator.rb +222 -13
- data/lib/rigor/sig_gen/method_candidate.rb +14 -0
- data/lib/rigor/sig_gen/observation_collector.rb +11 -2
- data/lib/rigor/sig_gen/platform_guarded_files.rb +184 -0
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +47 -0
- data/lib/rigor/sig_gen/writer.rb +21 -0
- data/lib/rigor/sig_gen.rb +1 -0
- data/lib/rigor/source/forwardable_delegators.rb +122 -0
- data/lib/rigor/source/node_children.rb +13 -0
- data/lib/rigor/type/combinator.rb +164 -8
- data/lib/rigor/type/interface.rb +60 -0
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_of_memo/serving.rb +99 -0
- data/lib/rigor/type_of_memo/stats.rb +43 -0
- data/lib/rigor/type_of_memo.rb +337 -0
- data/lib/rigor/typing_state.rb +1094 -0
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +2 -2
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +57 -25
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +3 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +25 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +16 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +4 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +130 -30
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +20 -5
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +7 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +49 -13
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +19 -5
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +4 -2
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +13 -5
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +29 -0
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +15 -21
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -6
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +4 -2
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +12 -2
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +6 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +199 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +3 -2
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +4 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +209 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +146 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +52 -34
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +80 -3
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +16 -16
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +1 -6
- data/sig/rigor/analysis/check_rules/rule_walk.rbs +6 -0
- data/sig/rigor/analysis/fact_store.rbs +2 -3
- data/sig/rigor/cache/descriptor.rbs +14 -0
- data/sig/rigor/environment.rbs +4 -1
- data/sig/rigor/plugin/base.rbs +53 -19
- data/sig/rigor/plugin/interfaces.rbs +51 -0
- data/sig/rigor/plugin/io_boundary.rbs +2 -3
- data/sig/rigor/plugin/node_context.rbs +21 -0
- data/sig/rigor/reflection.rbs +37 -0
- data/sig/rigor/scope.rbs +22 -3
- data/sig/rigor/trinary.rbs +1 -0
- data/sig/rigor/type.rbs +9 -3
- data/sig/rigor.rbs +3 -2
- data/skills/rigor-ask/SKILL.md +1 -0
- data/skills/rigor-project-init/references/01-detect.md +1 -0
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +5 -1
- data/skills/rigor-project-init/references/06-agent-contract.md +4 -4
- data/skills/rigor-rbs-contract/SKILL.md +147 -0
- data/skills/rigor-rbs-contract/references/01-finding-candidates.md +212 -0
- data/skills/rigor-rbs-contract/references/02-contract-rules.md +157 -0
- data/skills/rigor-rbs-contract/references/03-target-own-code.md +90 -0
- data/skills/rigor-rbs-contract/references/04-target-gem-rbs-collection.md +210 -0
- data/skills/rigor-rbs-contract/references/05-declaration-mutants.md +182 -0
- data/skills/rigor-rbs-setup/SKILL.md +3 -1
- data/skills/rigor-type-oracle/SKILL.md +26 -1
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +4 -3
- metadata +48 -2
- data/lib/rigor/inference/memo_audit.rb +0 -55
|
@@ -9,7 +9,8 @@ require_relative "../type"
|
|
|
9
9
|
require_relative "guard_rebinding"
|
|
10
10
|
require_relative "project_method_ownership"
|
|
11
11
|
require_relative "refined_dispatch/own_method"
|
|
12
|
-
require_relative "../
|
|
12
|
+
require_relative "../typing_state"
|
|
13
|
+
require_relative "../memo_audit"
|
|
13
14
|
|
|
14
15
|
module Rigor
|
|
15
16
|
module Inference
|
|
@@ -116,19 +117,38 @@ module Rigor
|
|
|
116
117
|
nil
|
|
117
118
|
end
|
|
118
119
|
|
|
119
|
-
|
|
120
|
-
|
|
120
|
+
# ADR-129 WD1 — a run-scoped Memo: the `[deferred_ranges, counts]` pair is one table and the counts computed
|
|
121
|
+
# from it alone, so the counts are a function of the table identity the pair is keyed on.
|
|
122
|
+
DEFINITION_COUNTS = TypingState.memo(
|
|
123
|
+
:self_call_ivar_writes_definition_counts,
|
|
124
|
+
run_scoped: true, audit: "self-call ivar definition counts",
|
|
125
|
+
reason: "the instance-def counts of one deferred-ranges table; keyed on its identity"
|
|
126
|
+
)
|
|
127
|
+
private_constant :DEFINITION_COUNTS
|
|
121
128
|
|
|
122
129
|
# `{[owner, name] => the number of instance `def` rows of name the project records for owner}`, over every
|
|
123
130
|
# file of `deferred_ranges` (a `discovered_deferred_ranges` table). Built once per table and kept in ONE
|
|
124
131
|
# thread slot keyed by the table's identity (#2300): the table is never written in place, a file whose rows
|
|
125
132
|
# equal the seed's keeps the seed's table, and a scan of every file's rows per query was the cost once the
|
|
126
133
|
# table held the whole project. The slot holds one table and its counts, the next table replaces them, and the
|
|
127
|
-
# run that filled it releases it when it ends (
|
|
134
|
+
# run that filled it releases it when it ends (run-scoped, #2399).
|
|
128
135
|
def definition_counts(deferred_ranges)
|
|
129
|
-
|
|
130
|
-
return slot[1] if slot && slot[0].equal?(deferred_ranges)
|
|
136
|
+
return count_definitions(deferred_ranges) if MemoAudit.enabled? && MemoAudit.suspended?
|
|
131
137
|
|
|
138
|
+
state = TypingState.current
|
|
139
|
+
slot = state[DEFINITION_COUNTS]
|
|
140
|
+
if slot && slot[0].equal?(deferred_ranges)
|
|
141
|
+
return MemoAudit.check("self-call ivar definition counts", slot[1], deferred_ranges.size) do
|
|
142
|
+
count_definitions(deferred_ranges)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
counts = MemoAudit.key_audit("self-call ivar definition counts") { count_definitions(deferred_ranges) }
|
|
147
|
+
state[DEFINITION_COUNTS] = [deferred_ranges, counts]
|
|
148
|
+
counts
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def count_definitions(deferred_ranges)
|
|
132
152
|
counts = {}
|
|
133
153
|
deferred_ranges.each_value do |rows|
|
|
134
154
|
rows.each do |row|
|
|
@@ -138,7 +158,6 @@ module Rigor
|
|
|
138
158
|
counts[key] = counts.fetch(key, 0) + 1
|
|
139
159
|
end
|
|
140
160
|
end
|
|
141
|
-
Thread.current[DEFINITION_COUNTS_KEY] = [deferred_ranges, counts]
|
|
142
161
|
counts
|
|
143
162
|
end
|
|
144
163
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
|
|
5
|
+
require_relative "closure_escape_analyzer"
|
|
6
|
+
require_relative "project_method_ownership"
|
|
7
|
+
require_relative "refined_dispatch"
|
|
8
|
+
|
|
9
|
+
module Rigor
|
|
10
|
+
module Inference
|
|
11
|
+
# Issue #2861 — whether a block-carrying call provably runs its literal block under the CALLER's `self`, so the
|
|
12
|
+
# block body is no more opaque than the scope around it (`Scope#entering_self_preserving_block`).
|
|
13
|
+
#
|
|
14
|
+
# Issue #316 enters every block with `self` unmodelled, because a DSL call (`RSpec.describe`, `Class.new`, a
|
|
15
|
+
# project `def run(&b) = instance_exec(&b)`) may run the block on another object, and the call cannot be told
|
|
16
|
+
# apart from `Array#each` without knowing the callee. Here the callee is known: the explicit receiver's type and
|
|
17
|
+
# method are one {ClosureEscapeAnalyzer} classifies as `:non_escaping` — a catalogued core iterator, `tap` /
|
|
18
|
+
# `then`, or a project class's inherited `Enumerable` method — each of which `yield`s, which runs the block
|
|
19
|
+
# with its own `self`. The project must not define the method anywhere the receiver looks it up
|
|
20
|
+
# ({ProjectMethodOwnership.defines?}): a project reopening of `Array#each` could `instance_exec` the block.
|
|
21
|
+
# A `def` inside `refine X do … end` is no such answer, so where the call site's in-effect refinements may
|
|
22
|
+
# define the name the call declines too (issue #2909) — a refined `Integer#times` or `Object#tap` could
|
|
23
|
+
# `instance_exec` the block just the same.
|
|
24
|
+
#
|
|
25
|
+
# The project must not define it on `Object`, `Kernel` or another owner every receiver reaches either
|
|
26
|
+
# ({.universally_defined?}). A receiverless call is the DSL shape and never answers true, nor does a receiver Rigor
|
|
27
|
+
# cannot see. A `self.` receiver is read like any other explicit one, through its type.
|
|
28
|
+
module SelfPreservingBlock
|
|
29
|
+
module_function
|
|
30
|
+
|
|
31
|
+
# `classification` is the receiver's {ClosureEscapeAnalyzer.classify} answer when the caller already holds it.
|
|
32
|
+
def call?(call_node, receiver_type, scope, classification: nil)
|
|
33
|
+
return false unless call_node.is_a?(Prism::CallNode) && call_node.block.is_a?(Prism::BlockNode)
|
|
34
|
+
|
|
35
|
+
return false if call_node.receiver.nil? || receiver_type.nil?
|
|
36
|
+
|
|
37
|
+
method_name = call_node.name
|
|
38
|
+
classification ||= ClosureEscapeAnalyzer.classify(receiver_type: receiver_type, method_name: method_name,
|
|
39
|
+
scope: scope)
|
|
40
|
+
return false unless classification == :non_escaping
|
|
41
|
+
|
|
42
|
+
project_shadow_free?(call_node, receiver_type, method_name, scope)
|
|
43
|
+
rescue StandardError
|
|
44
|
+
false
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# The project-side half of {.call?}: nothing the project defines — a plain `def` on a lookup target or a
|
|
48
|
+
# {UNIVERSAL_OWNERS} entry, nor a refinement in effect at the call site — may answer the yielding method
|
|
49
|
+
# instead of the catalogued non-escaping one.
|
|
50
|
+
def project_shadow_free?(call_node, receiver_type, method_name, scope)
|
|
51
|
+
targets = ProjectMethodOwnership.targets(receiver_type)
|
|
52
|
+
return false if targets.nil? || targets.empty?
|
|
53
|
+
return false if universally_defined?(method_name, scope)
|
|
54
|
+
|
|
55
|
+
targets.none? { |class_name, kind| ProjectMethodOwnership.defines?(class_name, method_name, kind, scope) } &&
|
|
56
|
+
!refined_in_effect?(call_node, method_name, scope)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# The classes every receiver's lookup reaches: an instance of any class is an `Object` (a class object is also
|
|
60
|
+
# a `Module` and a `Class`), so a project method of the name on one of them, or on `Kernel`, which
|
|
61
|
+
# {ProjectMethodOwnership.defines?} reaches through `Object`'s ancestry, may be the one the call runs, whatever
|
|
62
|
+
# class the receiver names. `ClosureEscapeAnalyzer` answers `tap` / `then` / `yield_self` for every class
|
|
63
|
+
# without asking who defines them.
|
|
64
|
+
UNIVERSAL_OWNERS = %w[Object Module Class].freeze
|
|
65
|
+
private_constant :UNIVERSAL_OWNERS
|
|
66
|
+
|
|
67
|
+
# Whether the project defines `method_name` on a {UNIVERSAL_OWNERS} entry. A top-level `def` of the name is
|
|
68
|
+
# private, so an explicit-receiver call raises NoMethodError before any block runs, and is not counted.
|
|
69
|
+
def universally_defined?(method_name, scope)
|
|
70
|
+
UNIVERSAL_OWNERS.any? { |owner| ProjectMethodOwnership.defines?(owner, method_name, :instance, scope) }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Issue #2909 — whether a refinement in effect at `call_node` may define `method_name`. The refined-class
|
|
74
|
+
# walk is not consulted: any in-effect row for the name declines, the direction ADR-5 prefers, and it covers
|
|
75
|
+
# `refine Object` / `refine Kernel` without a universal-owner path of its own. An empty list — no `using`
|
|
76
|
+
# reaches the node — is the common case and costs the one lookup. The answer reads the refinement table by
|
|
77
|
+
# method name, so `refinement:<name>` is recorded as {ExpressionTyper}'s refined dispatch records it.
|
|
78
|
+
def refined_in_effect?(call_node, method_name, scope)
|
|
79
|
+
list = scope.in_effect_refinements(call_node)
|
|
80
|
+
return false if list.empty?
|
|
81
|
+
|
|
82
|
+
InEffectRefinements.record_refinement_names(method_name)
|
|
83
|
+
RefinedDispatch.refined_anywhere?(scope, method_name, list)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -37,7 +37,8 @@ module Rigor
|
|
|
37
37
|
# Known remainders, pinned in `spec/integration/definer_resolution_witness_spec.rb`: `class << self; prepend
|
|
38
38
|
# P` is recorded as an `extend` (the chain places `P` after the singleton, so an own `def self.x` is trusted
|
|
39
39
|
# against it); a concern is capable for every name; an own `def self.x` is trusted against its own level's
|
|
40
|
-
# U2 hooks and same-class redefinitions
|
|
40
|
+
# U2 hooks and same-class redefinitions, except a concern block's `def self.x` of the same name (#3005, which
|
|
41
|
+
# the read tests); a `class Class; def inherited` monkeypatch is unseen; and a
|
|
41
42
|
# superclass that only `extend`s adds two forks, so every read on its subclasses declines.
|
|
42
43
|
module SingletonHookDecline
|
|
43
44
|
WILDCARD = "*"
|
|
@@ -65,7 +66,8 @@ module Rigor
|
|
|
65
66
|
entry = chain.entries[index]
|
|
66
67
|
# A singleton project entry on a singleton chain is always a level's class entry.
|
|
67
68
|
own = !entry.external? && entry.side == :singleton && def_head(scope, entry.name, method_name) == entry.name
|
|
68
|
-
own
|
|
69
|
+
# Issue #3005 — an own `def self.x` that a concern block's `def self.x` on its level may replace is tested.
|
|
70
|
+
own && !chain.hook_block_rival?(scope, index, method_name) ? index : index + 1
|
|
69
71
|
end
|
|
70
72
|
|
|
71
73
|
# What a class entry's answer is when its `def` was written in another module's body: `:copy` when that
|
|
@@ -92,14 +94,22 @@ module Rigor
|
|
|
92
94
|
# Whether the class's singleton `def` is the very node `head`'s instance table holds, the one the fold
|
|
93
95
|
# copied: a receiverless `def`. A `def C.x` or `class << C; def x; end` written inside `head`'s body has the
|
|
94
96
|
# same nesting head and is the class's own, not a copy. (The walk records a `def C.x` written in `module X`
|
|
95
|
-
# as `X#x` too, so identity alone does not tell it from a copy; its receiver does.)
|
|
97
|
+
# as `X#x` too, so identity alone does not tell it from a copy; its receiver does.) Issue #3155 — the walk no
|
|
98
|
+
# longer files that `def C.x` anywhere, so the copy is all the table holds; the definer's mark on the class
|
|
99
|
+
# (`ENVELOPE_OBJECT_SINGLETON_KIND`, {ScopeIndexer#record_object_singleton}) still says the class has its own.
|
|
96
100
|
def copied_from?(scope, head, method_name, owner)
|
|
97
101
|
node = scope.singleton_def_for(owner, method_name)
|
|
98
102
|
return false if node.nil? || !node.receiver.nil?
|
|
103
|
+
return false if foreign_singleton_definer?(scope, owner, method_name)
|
|
99
104
|
|
|
100
105
|
scope.user_def_for(head, method_name).equal?(node)
|
|
101
106
|
end
|
|
102
107
|
|
|
108
|
+
def foreign_singleton_definer?(scope, owner, method_name)
|
|
109
|
+
key = [Scope::DiscoveryIndex::ENVELOPE_OBJECT_SINGLETON_KIND, method_name.to_sym]
|
|
110
|
+
scope.parameter_envelopes_of(owner).key?(key)
|
|
111
|
+
end
|
|
112
|
+
|
|
103
113
|
# The innermost `Module.nesting` entry `owner`'s singleton `def` of the name was written in, or nil.
|
|
104
114
|
def def_head(scope, owner, method_name)
|
|
105
115
|
node = scope.singleton_def_for(owner, method_name)
|
|
@@ -170,15 +180,40 @@ module Rigor
|
|
|
170
180
|
|
|
171
181
|
# The memoised `[verdict, edges]` of one test, its edges replayed while a recording is active.
|
|
172
182
|
def verdict(kind, key)
|
|
173
|
-
|
|
183
|
+
memo_key = [kind, key]
|
|
184
|
+
entry = @memo[memo_key]
|
|
185
|
+
if entry.nil?
|
|
174
186
|
edges = []
|
|
175
|
-
value = yield(edges)
|
|
176
|
-
[value ? true : false, edges.concat(own_edges(kind, key)).uniq.freeze].freeze
|
|
187
|
+
value = MemoAudit.key_audit("singleton hook verdict") { yield(edges) }
|
|
188
|
+
entry = @memo[memo_key] = [value ? true : false, edges.concat(own_edges(kind, key)).uniq.freeze].freeze
|
|
189
|
+
else
|
|
190
|
+
entry = MemoAudit.check("singleton hook verdict", entry, kind, key) { fresh_verdict(kind, key) }
|
|
177
191
|
end
|
|
192
|
+
verdict, edges = entry
|
|
178
193
|
replay(edges) if @recording
|
|
179
194
|
verdict
|
|
180
195
|
end
|
|
181
196
|
|
|
197
|
+
# ADR-129 G2 — a memoised verdict rebuilt for the audit by a new Context, whose memo is a fresh bucket's
|
|
198
|
+
# while the recomputation runs ({Scope::ResolutionChain.hook_memo}), its dependency reads withheld.
|
|
199
|
+
def fresh_verdict(kind, key)
|
|
200
|
+
Analysis::DependencyRecorder.withhold { Context.new(@scope, @flavor).verdict_entry(kind, key) }.first
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
protected
|
|
204
|
+
|
|
205
|
+
# The `[verdict, edges]` entry of one test, computed through {#verdict} on this Context's memo.
|
|
206
|
+
def verdict_entry(kind, key)
|
|
207
|
+
case kind
|
|
208
|
+
when :project then verdict(:project, key) { project_capable?(key) }
|
|
209
|
+
when :external then verdict(:external, key) { external_capable?(key) }
|
|
210
|
+
else instance_level_capable?(key)
|
|
211
|
+
end
|
|
212
|
+
@memo[[kind, key]]
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
private
|
|
216
|
+
|
|
182
217
|
def own_edges(kind, key)
|
|
183
218
|
return entry_edges(key) if kind == :external
|
|
184
219
|
return EMPTY_EDGES if kind == :instance_level
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../source/constant_path"
|
|
4
|
+
require_relative "../source/parameter_envelope"
|
|
4
5
|
|
|
5
6
|
module Rigor
|
|
6
7
|
module Inference
|
|
@@ -45,18 +46,47 @@ module Rigor
|
|
|
45
46
|
name = receiver_constant_name(call_node, receiver_type)
|
|
46
47
|
return false if name.nil?
|
|
47
48
|
|
|
48
|
-
scope.discovered_method?(name, method_name, :singleton)
|
|
49
|
+
scope.discovered_method?(name, method_name, :singleton) || foreign_singleton?(scope, name, method_name)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Issue #3016 — `def Const.x` written outside `Const`'s own body, and `Const.define_singleton_method(:x)`, record
|
|
53
|
+
# no `:singleton` row: {ScopeIndexer} files them as an opaque `[:singleton, x]` envelope on every name the
|
|
54
|
+
# constant can denote from the def site. That envelope is the evidence the method exists on the constant's
|
|
55
|
+
# object. The read records the constant's class edge, and that edge's file set holds every file writing such a
|
|
56
|
+
# def or call, so adding or removing one re-checks the call.
|
|
57
|
+
#
|
|
58
|
+
# A computed `Const.define_singleton_method(name)` may define any name, so the singleton side's open-names mark it
|
|
59
|
+
# files declines every call through the constant.
|
|
60
|
+
def foreign_singleton?(scope, name, method_name)
|
|
61
|
+
bucket = scope.parameter_envelopes_of(name)
|
|
62
|
+
bucket.key?([:singleton, method_name.to_sym]) ||
|
|
63
|
+
bucket.key?(Scope::DiscoveryIndex::ENVELOPE_OPEN_SINGLETON_NAMES_MARK)
|
|
49
64
|
end
|
|
50
65
|
|
|
51
66
|
# The `Prism::DefNode` for `method_name` in the constant's singleton body, or nil. The inference tier's
|
|
52
67
|
# entry point.
|
|
68
|
+
#
|
|
69
|
+
# Issue #3018 — nil too when another definer of the name on the constant may replace that def: a `def Const.x`,
|
|
70
|
+
# a `Const.define_singleton_method(:x)` or a `singleton_class.class_eval` def joins an opaque envelope into the
|
|
71
|
+
# name's `[:singleton, x]` slot, and a computed `define_singleton_method` files the open-names mark. The body
|
|
72
|
+
# def's own contribution is its parameter envelope, never opaque, so a lone body def keeps its typed answer.
|
|
73
|
+
# Declined whatever the source order, since a two-file project has none.
|
|
53
74
|
def def_node_for(call_node, receiver_type, method_name, scope)
|
|
54
75
|
return nil if scope.nil?
|
|
55
76
|
|
|
56
77
|
name = receiver_constant_name(call_node, receiver_type)
|
|
57
78
|
return nil if name.nil?
|
|
58
79
|
|
|
59
|
-
scope.singleton_def_for(name, method_name)
|
|
80
|
+
def_node = scope.singleton_def_for(name, method_name)
|
|
81
|
+
return nil if def_node.nil? || replaceable?(scope, name, method_name)
|
|
82
|
+
|
|
83
|
+
def_node
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def replaceable?(scope, name, method_name)
|
|
87
|
+
bucket = scope.parameter_envelopes_of(name)
|
|
88
|
+
Source::ParameterEnvelope.opaque?(bucket[[:singleton, method_name.to_sym]]) ||
|
|
89
|
+
bucket.key?(Scope::DiscoveryIndex::ENVELOPE_OPEN_SINGLETON_NAMES_MARK)
|
|
60
90
|
end
|
|
61
91
|
end
|
|
62
92
|
end
|