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
|
@@ -25,6 +25,18 @@ module Rigor
|
|
|
25
25
|
# leaves the edges the tables did record looking positioned. So it taints the whole owner side with the
|
|
26
26
|
# {WILDCARD} name instead: an owner side that lists {WILDCARD} has no known order at all.
|
|
27
27
|
#
|
|
28
|
+
# Issue #2991 — {WILDCARD} says only that the ORDER is unknown: `include M; prepend M`, an edge recorded where its
|
|
29
|
+
# position is not a fact, and an opaque eval whose block mixes nothing into its own `self` all set it while every
|
|
30
|
+
# module is recorded. Where the walk recorded no module under any name, the instance side also lists
|
|
31
|
+
# {UNNAMED}, always beside {WILDCARD}, because its SET of modules is unknown too. That covers an instance-side
|
|
32
|
+
# mixin call it could not record whose argument is not a constant (`include helper`, `include(*MODS)`; a
|
|
33
|
+
# `Module.new { … }` argument keeps its #2975 reading), a name beside one it cannot name (`include M, h`), and
|
|
34
|
+
# an opaque eval whose block mixes into its own `self`, and a hook's `base.include M`. `self.include M` and a
|
|
35
|
+
# receiverless `send(:include, M)` are recorded as a bare `include M` is (#2995). Issue #2994 — the singleton side
|
|
36
|
+
# lists {UNNAMED} the same way for an `extend` (or a `class << self` `include` / `prepend`) it could not record
|
|
37
|
+
# whose arguments include no constant (`extend(*EMODS)`, `extend helper`), and for a name beside one it cannot
|
|
38
|
+
# name (`extend M, h`).
|
|
39
|
+
#
|
|
28
40
|
# The value is `{owner => {include: [names], extend: [names]}}`. `:include` is the instance side
|
|
29
41
|
# (`include` and `prepend`), `:extend` the singleton side (`extend`, and `include` / `prepend` inside
|
|
30
42
|
# `class << self`), so an unknown `extend` never taints a class's instance ancestry.
|
|
@@ -33,6 +45,11 @@ module Rigor
|
|
|
33
45
|
class MixinAccumulator < Hash
|
|
34
46
|
# The name an owner side lists when a mixin call the walk could not record was written on it.
|
|
35
47
|
WILDCARD = "*"
|
|
48
|
+
# The name an owner side also lists when such a call mixes in a module the walk cannot name (#2991).
|
|
49
|
+
UNNAMED = "?"
|
|
50
|
+
UNNAMED_LIST = [WILDCARD, UNNAMED].freeze
|
|
51
|
+
WILDCARD_LIST = [WILDCARD].freeze
|
|
52
|
+
private_constant :UNNAMED_LIST, :WILDCARD_LIST
|
|
36
53
|
|
|
37
54
|
# What {#unpositioned} answers when nothing was noted: one shared frozen table, so the common file
|
|
38
55
|
# allocates none.
|
|
@@ -47,7 +64,11 @@ module Rigor
|
|
|
47
64
|
@unpositioned = nil
|
|
48
65
|
@hook_params = nil
|
|
49
66
|
@body_blocks = nil
|
|
67
|
+
@program_body = nil
|
|
50
68
|
@program = nil
|
|
69
|
+
@class_bodies = nil
|
|
70
|
+
@class_body_calls = nil
|
|
71
|
+
@class_bodies_read = 0
|
|
51
72
|
end
|
|
52
73
|
|
|
53
74
|
# Marks the file's own top-level statements: a receiverless `include` among them is `main.include`,
|
|
@@ -55,11 +76,38 @@ module Rigor
|
|
|
55
76
|
def program_body(body)
|
|
56
77
|
return unless body.is_a?(Prism::StatementsNode)
|
|
57
78
|
|
|
58
|
-
@
|
|
79
|
+
@program_body = body
|
|
59
80
|
end
|
|
60
81
|
|
|
82
|
+
# Whether `node` is a call whose `self` is `main`'s as far as the walk can tell: a top-level statement, or
|
|
83
|
+
# a call reached from one through control flow alone (issue #3007), so a receiverless `include` there is
|
|
84
|
+
# `main.include`. The set is built on the first question, which only a top-level mixin call asks.
|
|
61
85
|
def program_statement?(node)
|
|
62
|
-
|
|
86
|
+
return false if @program_body.nil?
|
|
87
|
+
|
|
88
|
+
@program ||= MainSelfCalls.collect(@program_body)
|
|
89
|
+
@program.include?(node)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Issue #3006 — registers a class or module body (or its `class << self` body), whose calls
|
|
93
|
+
# {#class_body_scope} answers for.
|
|
94
|
+
def class_body(body)
|
|
95
|
+
(@class_bodies ||= []) << body if body
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# The node whose locals a call written in a registered class body reads — the body itself, or the `def self.`
|
|
99
|
+
# it sits in — or nil when the call is not one whose `self` is that body's class as far as {MainSelfCalls}
|
|
100
|
+
# can tell (an instance `def`, a block that may rebind `self`, a nested declaration). Built on the first
|
|
101
|
+
# question, which only a computed-name `send` asks, and extended with the bodies registered since.
|
|
102
|
+
def class_body_scope(node)
|
|
103
|
+
return nil if @class_bodies.nil?
|
|
104
|
+
|
|
105
|
+
@class_body_calls ||= {}.compare_by_identity
|
|
106
|
+
while @class_bodies_read < @class_bodies.size
|
|
107
|
+
@class_body_calls.merge!(MainSelfCalls.collect(@class_bodies[@class_bodies_read], instance_defs: false))
|
|
108
|
+
@class_bodies_read += 1
|
|
109
|
+
end
|
|
110
|
+
@class_body_calls[node]
|
|
63
111
|
end
|
|
64
112
|
|
|
65
113
|
# Marks the statements of a body whose own position is a fact — the calls and declarations in them
|
|
@@ -110,17 +158,18 @@ module Rigor
|
|
|
110
158
|
end
|
|
111
159
|
|
|
112
160
|
# Notes the edges `node` recorded for `owner` on `side`. `complete` is false when an argument named
|
|
113
|
-
# nothing the walk could record, which leaves the recorded names' neighbours unknown.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return
|
|
161
|
+
# nothing the walk could record, which leaves the recorded names' neighbours unknown. `positioned` says a
|
|
162
|
+
# receiver-form call runs where it is written, as a receiverless one does (`singleton_class.prepend P`, #2998).
|
|
163
|
+
def note(node, owner, side, targets, complete:, positioned: false)
|
|
164
|
+
return taint(owner, side, unnamed: true) unless complete
|
|
165
|
+
return if (positioned || node.receiver.nil? || node.receiver.is_a?(Prism::SelfNode)) && direct?(node)
|
|
117
166
|
|
|
118
167
|
add(owner, side, targets)
|
|
119
168
|
end
|
|
120
169
|
|
|
121
|
-
# Names `owner`'s `side` as having no known order.
|
|
122
|
-
def taint(owner, side)
|
|
123
|
-
add(owner, side,
|
|
170
|
+
# Names `owner`'s `side` as having no known order; `unnamed` also as holding a module no name spells.
|
|
171
|
+
def taint(owner, side, unnamed: false)
|
|
172
|
+
add(owner, side, unnamed ? UNNAMED_LIST : WILDCARD_LIST)
|
|
124
173
|
end
|
|
125
174
|
|
|
126
175
|
# `{owner => {side => [names]}}`, frozen, each list de-duplicated.
|
|
@@ -138,6 +187,84 @@ module Rigor
|
|
|
138
187
|
((@unpositioned ||= {})[owner] ||= {})[side] = ((@unpositioned[owner][side] || []) + names)
|
|
139
188
|
end
|
|
140
189
|
end
|
|
190
|
+
|
|
191
|
+
# Issue #3007 — the calls whose `self` is `main` as far as the mixin walk can tell: the file's top-level
|
|
192
|
+
# statements, and the calls reached from them through control flow (`if`, `unless`, `case`, `begin`, `rescue`, a
|
|
193
|
+
# loop, `&&` / `||`) or a value position (an assignment's value, a call's receiver or argument, an array literal),
|
|
194
|
+
# or through the body of a top-level `def` or `def self.`, whose `include` the walk reads as ADR-119 WD7 reads one
|
|
195
|
+
# in a method body: a member of the set with no position. A block is entered only when it is a core iterator's
|
|
196
|
+
# that keeps `self` ({SELF_KEEPING_ITERATORS}) on a literal receiver, or a receiverless `loop` / `tap` / `then`.
|
|
197
|
+
# Any other block or lambda is not: its `self` may not be `main` (a `describe do … end` or `class_exec` block
|
|
198
|
+
# rebinds it), and neither is a declaration, `class << self` or a `def` on another receiver, whose `self` is not
|
|
199
|
+
# `main`. Recording more here can only silence: these edges feed no typing (`Inference::ObjectMixins`).
|
|
200
|
+
module MainSelfCalls
|
|
201
|
+
CONTROL_FLOW = [
|
|
202
|
+
Prism::StatementsNode, Prism::BeginNode, Prism::RescueNode, Prism::ElseNode, Prism::EnsureNode,
|
|
203
|
+
Prism::IfNode, Prism::UnlessNode, Prism::CaseNode, Prism::CaseMatchNode, Prism::WhenNode, Prism::InNode,
|
|
204
|
+
Prism::WhileNode, Prism::UntilNode, Prism::ForNode, Prism::ParenthesesNode, Prism::AndNode, Prism::OrNode,
|
|
205
|
+
Prism::RescueModifierNode,
|
|
206
|
+
Prism::LocalVariableWriteNode, Prism::InstanceVariableWriteNode, Prism::GlobalVariableWriteNode,
|
|
207
|
+
Prism::ConstantWriteNode, Prism::MultiWriteNode, Prism::ArgumentsNode, Prism::ArrayNode
|
|
208
|
+
].freeze
|
|
209
|
+
# Core iterators whose block runs with the caller's `self`, entered on a literal receiver.
|
|
210
|
+
SELF_KEEPING_ITERATORS = %i[each each_with_index each_with_object map times upto downto tap then loop].freeze
|
|
211
|
+
# The iterators entered without a receiver: `Kernel#loop`, and `tap` / `then` on `main` itself.
|
|
212
|
+
RECEIVERLESS_ITERATORS = %i[tap then loop].freeze
|
|
213
|
+
LITERAL_RECEIVERS = [Prism::ArrayNode, Prism::RangeNode, Prism::IntegerNode].freeze
|
|
214
|
+
private_constant :CONTROL_FLOW, :SELF_KEEPING_ITERATORS, :RECEIVERLESS_ITERATORS, :LITERAL_RECEIVERS
|
|
215
|
+
|
|
216
|
+
module_function
|
|
217
|
+
|
|
218
|
+
# `{call => scope}`: each call and the node whose locals it reads, `body` or the `def` it sits in. Issue #3006 —
|
|
219
|
+
# a class body's calls are collected the same way with `instance_defs: false`: an instance `def`'s `self` is
|
|
220
|
+
# an instance, not the class, while a `def self.`'s is the class, and every block is entered.
|
|
221
|
+
def collect(body, instance_defs: true)
|
|
222
|
+
calls = {}.compare_by_identity
|
|
223
|
+
visit(body, calls, body, instance_defs)
|
|
224
|
+
calls
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def visit(node, calls, scope, instance_defs)
|
|
228
|
+
case node
|
|
229
|
+
when Prism::CallNode
|
|
230
|
+
calls[node] = scope
|
|
231
|
+
visit(node.receiver, calls, scope, instance_defs)
|
|
232
|
+
visit(node.arguments, calls, scope, instance_defs)
|
|
233
|
+
entered = instance_defs ? self_keeping_block?(node) : any_block?(node)
|
|
234
|
+
visit(node.block.body, calls, scope, instance_defs) if entered
|
|
235
|
+
when Prism::DefNode
|
|
236
|
+
visit(node.body, calls, node, instance_defs) if (instance_defs && node.receiver.nil?) ||
|
|
237
|
+
node.receiver.is_a?(Prism::SelfNode)
|
|
238
|
+
when *CONTROL_FLOW
|
|
239
|
+
node.compact_child_nodes.each { |child| visit(child, calls, scope, instance_defs) }
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
private_class_method :visit
|
|
243
|
+
|
|
244
|
+
# Issue #3006 — a class body's calls include those in any block written there (`KINDS.each { … }`, a concern's
|
|
245
|
+
# `included do … end`): a computed `send` in one may mix into the class, or into what the block's `self` is.
|
|
246
|
+
def any_block?(call)
|
|
247
|
+
call.block.is_a?(Prism::BlockNode)
|
|
248
|
+
end
|
|
249
|
+
private_class_method :any_block?
|
|
250
|
+
|
|
251
|
+
def self_keeping_block?(call)
|
|
252
|
+
return false unless call.block.is_a?(Prism::BlockNode) && SELF_KEEPING_ITERATORS.include?(call.name)
|
|
253
|
+
return RECEIVERLESS_ITERATORS.include?(call.name) if call.receiver.nil?
|
|
254
|
+
|
|
255
|
+
LITERAL_RECEIVERS.any? { |kind| literal(call.receiver).is_a?(kind) }
|
|
256
|
+
end
|
|
257
|
+
private_class_method :self_keeping_block?
|
|
258
|
+
|
|
259
|
+
# `(1..2)` is a range literal in parentheses.
|
|
260
|
+
def literal(receiver)
|
|
261
|
+
return receiver unless receiver.is_a?(Prism::ParenthesesNode) && receiver.body.is_a?(Prism::StatementsNode)
|
|
262
|
+
|
|
263
|
+
body = receiver.body.body
|
|
264
|
+
body.size == 1 ? body.first : receiver
|
|
265
|
+
end
|
|
266
|
+
private_class_method :literal
|
|
267
|
+
end
|
|
141
268
|
end
|
|
142
269
|
end
|
|
143
270
|
end
|