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
|
@@ -22,7 +22,12 @@ module Rigor
|
|
|
22
22
|
# whose kind (class or module) contradicts another source's, stands down on its own, where adding it would
|
|
23
23
|
# have raised `RBS::DuplicatedDeclarationError` and dropped the plugin's whole file;
|
|
24
24
|
# - a class header's superclass clause another source already gives the class one stands down (#2439), where two
|
|
25
|
-
# different clauses would have raised `RBS::SuperclassMismatchError`; the plugin's declaration reopens the class
|
|
25
|
+
# different clauses would have raised `RBS::SuperclassMismatchError`; the plugin's declaration reopens the class;
|
|
26
|
+
# - an `include`, `extend` or `prepend` of a module this file declares but that stood down whole, or that another
|
|
27
|
+
# source declares as a class, stands down too (#2974), where keeping it would have raised `NoMixinFoundError` or
|
|
28
|
+
# `MixinClassError` for the class that mixes it in (`Integer`, for the overlay's `ActiveSupport::NumericWithFormat`);
|
|
29
|
+
# - an instance, class-instance or class variable another source declares on the type stands down (#2978), where
|
|
30
|
+
# two declarations of it would have raised `InstanceVariableDuplicationError` or its twins for the class.
|
|
26
31
|
#
|
|
27
32
|
# A declaration with nothing taken comes back as the same object, so a file with no collision loads as it
|
|
28
33
|
# stands. {.standdown_rows} reads what stood down back off the built environment, so a warm run off the env
|
|
@@ -33,27 +38,30 @@ module Rigor
|
|
|
33
38
|
# @param own — the buffer names that are the project's own sources: its `signature_paths:` files (not the
|
|
34
39
|
# collection or gem directories also on that list) and its inline RBS.
|
|
35
40
|
def plan(decls, env, own, namespace = nil)
|
|
36
|
-
|
|
41
|
+
mixins = MixinGate.new(env, decls)
|
|
42
|
+
planned = decls.filter_map { |decl| plan_declaration(decl, env, own, namespace, mixins) }
|
|
37
43
|
same?(planned, decls) ? decls : planned
|
|
38
44
|
end
|
|
39
45
|
|
|
40
|
-
def plan_declaration(decl, env, own, namespace)
|
|
46
|
+
def plan_declaration(decl, env, own, namespace, mixins)
|
|
41
47
|
name = GatedSignaturePlan.qualify(namespace, declared_name(decl))
|
|
42
48
|
if GatedSignaturePlan.type_declaration?(decl)
|
|
43
49
|
return nil if kind_clash?(env, decl, name)
|
|
44
50
|
|
|
45
|
-
return plan_type(decl, env, own, name)
|
|
51
|
+
return plan_type(decl, env, own, name, mixins)
|
|
46
52
|
end
|
|
47
53
|
declared_elsewhere(env, decl, name) ? nil : decl
|
|
48
54
|
end
|
|
49
55
|
|
|
50
|
-
def plan_type(decl, env, own, name)
|
|
56
|
+
def plan_type(decl, env, own, name, mixins)
|
|
51
57
|
others = GatedSignaturePlan.other_declarations(env, name)
|
|
52
58
|
taken = GatedSignaturePlan.declared_keys(others)
|
|
59
|
+
variables = variable_keys(others)
|
|
53
60
|
external = others.reject { |other| own.include?(buffer_of(other)) }
|
|
54
61
|
defined = GatedSignaturePlan.declared_keys(external) | definition_keys(external)
|
|
55
62
|
members = decl.members.filter_map do |member|
|
|
56
|
-
next plan_declaration(member, env, own, name) if declaration?(member)
|
|
63
|
+
next plan_declaration(member, env, own, name, mixins) if declaration?(member)
|
|
64
|
+
next nil if mixins.drop?(member, name) || variables.include?(variable_key(member))
|
|
57
65
|
next member if taken.empty?
|
|
58
66
|
|
|
59
67
|
composed(member, defined) || GatedSignaturePlan.untaken(member, taken)
|
|
@@ -64,6 +72,40 @@ module Rigor
|
|
|
64
72
|
GatedSignaturePlan.rebuild(decl, members: members, drop_super_class: drop)
|
|
65
73
|
end
|
|
66
74
|
|
|
75
|
+
# Issue #2978 — an instance, class-instance or class variable another source declares on the type stands down:
|
|
76
|
+
# RBS raises `InstanceVariableDuplicationError` (or its class-instance and class-variable twins) for two
|
|
77
|
+
# declarations of one, and the class reads `Dynamic[top]`. Keyed by name and kind, not type: the other source's
|
|
78
|
+
# declaration binds either way.
|
|
79
|
+
VARIABLE_MEMBERS = {
|
|
80
|
+
::RBS::AST::Members::InstanceVariable => :instance_variable,
|
|
81
|
+
::RBS::AST::Members::ClassInstanceVariable => :class_instance_variable,
|
|
82
|
+
::RBS::AST::Members::ClassVariable => :class_variable
|
|
83
|
+
}.freeze
|
|
84
|
+
|
|
85
|
+
# `[name, kind]` for a variable member, else nil.
|
|
86
|
+
def variable_key(member)
|
|
87
|
+
kind = VARIABLE_MEMBERS[member.class]
|
|
88
|
+
kind && [member.name, kind]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# The variable keys `decls` declare, an attribute's implied instance variable included (`attr_reader x: String`
|
|
92
|
+
# declares `@x` unless written `attr_reader x(): String`).
|
|
93
|
+
def variable_keys(decls)
|
|
94
|
+
decls.each_with_object(Set.new) do |decl, keys|
|
|
95
|
+
decl.members.each do |member|
|
|
96
|
+
key = variable_key(member) || attribute_variable_key(member)
|
|
97
|
+
keys << key if key
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def attribute_variable_key(member)
|
|
103
|
+
return nil unless member.is_a?(::RBS::AST::Members::Attribute) && member.ivar_name != false
|
|
104
|
+
|
|
105
|
+
[member.ivar_name || :"@#{member.name}",
|
|
106
|
+
member.kind == :singleton ? :class_instance_variable : :instance_variable]
|
|
107
|
+
end
|
|
108
|
+
|
|
67
109
|
# Issue #2439 — true when `decl` is a class with a superclass clause and another source (`others`) already
|
|
68
110
|
# gives the class one. Two different clauses raise `RBS::SuperclassMismatchError` and the class and every
|
|
69
111
|
# descendant read `Dynamic[top]`, so the plugin's header stands down and reopens the class without one. The
|
|
@@ -140,6 +182,62 @@ module Rigor
|
|
|
140
182
|
RbsLoader.declaration_buffer_name(decl)
|
|
141
183
|
end
|
|
142
184
|
|
|
185
|
+
# Issue #2974 — which of a deferred file's mixins name a module that will not be there as a module: one the file
|
|
186
|
+
# itself declares, inside a type that stood down whole on a kind clash (the project's `class ActiveSupport` takes
|
|
187
|
+
# the overlay's `module ActiveSupport` and the `NumericWithFormat` in it), or one another source declares as a
|
|
188
|
+
# class. A mixin of a module the file does not declare, which some other source may supply, is left alone.
|
|
189
|
+
class MixinGate
|
|
190
|
+
MIXINS = [::RBS::AST::Members::Include, ::RBS::AST::Members::Extend, ::RBS::AST::Members::Prepend].freeze
|
|
191
|
+
|
|
192
|
+
def initialize(env, decls)
|
|
193
|
+
@env = env
|
|
194
|
+
@declared = Set.new
|
|
195
|
+
@dropped = Set.new
|
|
196
|
+
decls.each { |decl| collect(decl, nil, false) }
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# `namespace` is the absolute name, without the leading `::`, of the type `member` is written in.
|
|
200
|
+
def drop?(member, namespace)
|
|
201
|
+
return false unless MIXINS.any? { |klass| member.is_a?(klass) } && member.name.class?
|
|
202
|
+
|
|
203
|
+
target = resolve(member.name, namespace)
|
|
204
|
+
return false if target.nil?
|
|
205
|
+
|
|
206
|
+
first = GatedSignaturePlan.other_declarations(@env, target).first
|
|
207
|
+
return first.is_a?(::RBS::AST::Declarations::Class) unless first.nil?
|
|
208
|
+
|
|
209
|
+
@dropped.include?(target)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
private
|
|
213
|
+
|
|
214
|
+
def collect(decl, namespace, inside_dropped)
|
|
215
|
+
return unless GatedSignaturePlan.type_declaration?(decl)
|
|
216
|
+
|
|
217
|
+
name = GatedSignaturePlan.qualify(namespace, decl.name)
|
|
218
|
+
dropped = inside_dropped || DeferredSignaturePlan.kind_clash?(@env, decl, name)
|
|
219
|
+
@declared << name
|
|
220
|
+
@dropped << name if dropped
|
|
221
|
+
decl.members.each { |member| collect(member, name, dropped) }
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# The first lexical candidate for `type_name` written inside `namespace` that this file or the env declares,
|
|
225
|
+
# as RBS resolves a relative name outward from the innermost scope.
|
|
226
|
+
def resolve(type_name, namespace)
|
|
227
|
+
candidates(type_name, namespace).find do |name|
|
|
228
|
+
@declared.include?(name) || !GatedSignaturePlan.other_declarations(@env, name).empty?
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def candidates(type_name, namespace)
|
|
233
|
+
text = type_name.to_s
|
|
234
|
+
return [text.delete_prefix("::")] if type_name.absolute?
|
|
235
|
+
|
|
236
|
+
scopes = namespace.to_s.split("::")
|
|
237
|
+
scopes.size.downto(0).map { |depth| [*scopes.first(depth), text].join("::") }
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
143
241
|
# One {.standdown_rows} walk over a deferred file: the built env, the file's buffer name, the rows so far, and
|
|
144
242
|
# the env's type-name resolver, built on first use (only a dropped superclass clause needs it).
|
|
145
243
|
class Walk
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "../typing_state"
|
|
4
|
+
|
|
3
5
|
module Rigor
|
|
4
6
|
class Environment
|
|
5
7
|
# ADR-20 slice 2e — mutable single-slot memoization container for the per-Environment HKT registry. Held
|
|
@@ -10,6 +12,10 @@ module Rigor
|
|
|
10
12
|
# prevent cross-thread races. If a future caller introduces a multi-threaded reader path against a
|
|
11
13
|
# shared Environment, the synchronisation belongs at that caller's seam, not here.
|
|
12
14
|
class HktRegistryHolder
|
|
15
|
+
TypingState.memo_field(self, :@value, :@loaded,
|
|
16
|
+
reason: "the one value its owner's block builds from the environment's fixed inputs, " \
|
|
17
|
+
"kept once built")
|
|
18
|
+
|
|
13
19
|
def initialize
|
|
14
20
|
@loaded = false
|
|
15
21
|
@value = nil
|
|
@@ -19,12 +19,16 @@ module Rigor
|
|
|
19
19
|
# access. Failure modes (missing lockfile, malformed YAML, missing collection directory) silently
|
|
20
20
|
# degrade to an empty list.
|
|
21
21
|
module RbsCollectionDiscovery
|
|
22
|
-
# `stdlib`-typed entries
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
22
|
+
# `stdlib`-typed entries (`source: { type: stdlib }`) name a library rbs ships under its own `stdlib/`, and
|
|
23
|
+
# produce no directory under the collection root, so {.discover} never returns one. Issue #2946 — they are
|
|
24
|
+
# loaded all the same, as the `rbs` CLI's `EnvironmentLoader#add_collection` loads them: as RBS LIBRARIES.
|
|
25
|
+
# {.stdlib_library_names} lists them, and `Environment::BaseLibraries.resolve` appends them to the library list,
|
|
26
|
+
# which is de-duplicated, so an entry `DEFAULT_LIBRARIES` already names is not loaded twice. A collection
|
|
27
|
+
# gem that subclasses one (actionview's `NodeParser < ::Ripper`) then builds. An unknown name is dropped by
|
|
28
|
+
# the loader's `has_library?` filter, as a `libraries:` name is, and `ConfigAudit` reports it. The other
|
|
29
|
+
# documented source types — `git` (the gem_rbs_collection repo), `rubygems` (sigs lifted from a gem's
|
|
30
|
+
# bundled `sig/`), and `local` (a user-managed RBS dir) — all produce a directory under the collection root
|
|
31
|
+
# and are admitted by {.discover}.
|
|
28
32
|
#
|
|
29
33
|
# The `source.type` filter alone is NOT sufficient: gems that were extracted from Ruby's stdlib into
|
|
30
34
|
# standalone default gems (e.g. `cgi`, `logger`, `base64`, `csv`, `bigdecimal`) are published in
|
|
@@ -77,6 +81,110 @@ module Rigor
|
|
|
77
81
|
Rigor::ConfigPath.absolute(lockfile_path.to_s, project_root)
|
|
78
82
|
end
|
|
79
83
|
|
|
84
|
+
# Issue #2946 — the `name` of every `source: { type: stdlib }` entry in the lockfile, in lockfile order and
|
|
85
|
+
# de-duplicated, whether or not rbs ships that library: the caller loads them as RBS libraries, and the
|
|
86
|
+
# loader's `has_library?` filter drops an unknown one. Unlike {.discover}, the collection directory need not
|
|
87
|
+
# exist, since a stdlib entry installs nothing there. Returns `[]` when no lockfile resolves or its YAML is
|
|
88
|
+
# unreadable. A pure YAML read, so the ADR-87 boot-slimming probe can rebuild the library list from it.
|
|
89
|
+
def self.stdlib_library_names(lockfile_path:, project_root: Dir.pwd, auto_detect: true)
|
|
90
|
+
resolved = resolve_lockfile_path(lockfile_path: lockfile_path, project_root: project_root,
|
|
91
|
+
auto_detect: auto_detect)
|
|
92
|
+
return [] if resolved.nil?
|
|
93
|
+
|
|
94
|
+
data = read_lockfile_yaml(resolved)
|
|
95
|
+
return [] if data.nil?
|
|
96
|
+
|
|
97
|
+
Array(data["gems"]).filter_map do |entry|
|
|
98
|
+
next unless entry.is_a?(Hash) && SKIPPED_SOURCE_TYPES.include?(source_type(entry))
|
|
99
|
+
|
|
100
|
+
name = entry["name"]
|
|
101
|
+
name.to_s if name.is_a?(String) && !name.empty?
|
|
102
|
+
end.uniq
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Issue #2971 — the `name` of every lockfile entry that is NOT a `stdlib` source and whose
|
|
106
|
+
# `<root>/<name>/<version>/` directory exists: the gems whose signatures come from the collection ({.discover}).
|
|
107
|
+
# The `libraries:` audit uses it so a name the collection provides is not reported as an unknown RBS library.
|
|
108
|
+
# A pure file read, like {.stdlib_library_names}.
|
|
109
|
+
def self.collection_gem_names(lockfile_path:, project_root: Dir.pwd, auto_detect: true)
|
|
110
|
+
resolved = resolve_lockfile_path(lockfile_path: lockfile_path, project_root: project_root,
|
|
111
|
+
auto_detect: auto_detect)
|
|
112
|
+
return [] if resolved.nil?
|
|
113
|
+
|
|
114
|
+
data = read_lockfile_yaml(resolved)
|
|
115
|
+
return [] if data.nil?
|
|
116
|
+
|
|
117
|
+
collection_root = resolve_collection_root(resolved, data)
|
|
118
|
+
gem_paths_from(collection_root, data, Set.new).map { |dir| dir.parent.basename.to_s }.uniq
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Issue #3030 — whether `file`, found under the collection gem directory `root`, sits below a `_`-prefixed
|
|
122
|
+
# directory (`_test/`, `_scripts/`). Such a file is not part of the gem's signatures: rbs's
|
|
123
|
+
# `EnvironmentLoader` loads a collection gem as a library, and `RBS::FileFinder.each_file(skip_hidden: true)`
|
|
124
|
+
# drops every file with a `_`-prefixed directory between it and the library root. A `git` source never
|
|
125
|
+
# copies them into the collection (`Sources::Git#cp_r` prunes them), but a `local` source symlinks the whole
|
|
126
|
+
# `<name>/<version>/` directory, so gem_rbs_collection's own `bin/test` setup brings each gem's `_test/`
|
|
127
|
+
# fixtures along. Only the directories count, as in rbs: a `_`-prefixed file name is loaded.
|
|
128
|
+
def self.hidden_file?(root, file)
|
|
129
|
+
relative = Pathname(file.to_s).relative_path_from(Pathname(root.to_s))
|
|
130
|
+
relative.ascend.drop(1).any? { |part| part.basename.to_s.start_with?("_") }
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Issue #3030 — `collection_paths` ({.discover}) without each gem directory a `signature_paths:` root already
|
|
134
|
+
# loads a copy of. A root is a copy of the entry `<collection>/<name>/<version>` when its real path is the
|
|
135
|
+
# entry's real path (a `local` source symlinks the working tree in), or when it is a gem_rbs_collection working
|
|
136
|
+
# tree for the same gem and version, `.../gems/<name>/<version>` (a `git` source installs a copy of it). That is
|
|
137
|
+
# what an author editing the tree points `signature_paths:` at from its `_test/` directory while the lockfile
|
|
138
|
+
# installs the same gem, and loading both declares every member twice. The `signature_paths:` copy is kept, as
|
|
139
|
+
# it is the one being edited. Any other root, such as a partial override `sig/overrides/foo/1.0`, leaves the
|
|
140
|
+
# collection copy loaded. Real paths are read so `..` and a symlinked spelling answer the same; a root that
|
|
141
|
+
# does not exist matches nothing.
|
|
142
|
+
def self.without_signature_path_copies(collection_paths, signature_roots)
|
|
143
|
+
reals = signature_roots.filter_map { |root| real_path(root) }
|
|
144
|
+
return collection_paths if reals.empty?
|
|
145
|
+
|
|
146
|
+
trees = reals.filter_map do |real|
|
|
147
|
+
real = Pathname(real)
|
|
148
|
+
[real.parent.basename.to_s, real.basename.to_s] if real.parent.parent.basename.to_s == "gems"
|
|
149
|
+
end.to_set
|
|
150
|
+
reals = reals.to_set
|
|
151
|
+
collection_paths.reject do |path|
|
|
152
|
+
path = Pathname(path.to_s)
|
|
153
|
+
trees.include?([path.parent.basename.to_s, path.basename.to_s]) || reals.include?(real_path(path))
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def self.real_path(path)
|
|
158
|
+
File.realpath(path.to_s)
|
|
159
|
+
rescue SystemCallError
|
|
160
|
+
nil
|
|
161
|
+
end
|
|
162
|
+
private_class_method :real_path
|
|
163
|
+
|
|
164
|
+
# Issue #3030 — why a run with no collection lockfile looks like it was meant to have one: `:collection_test_dir`
|
|
165
|
+
# when `project_root` is a gem_rbs_collection test directory (`gems/<name>/<version>/_test/`, whose lockfile
|
|
166
|
+
# gem_rbs_collection's `bin/test` setup writes), `:collection_config` when it holds an `rbs_collection.yaml`
|
|
167
|
+
# (`rbs collection install` has not run), and nil otherwise, or when a lockfile resolves or the lookup is off.
|
|
168
|
+
# Rigor resolves no `manifest.yaml` or gemspec dependency itself, so without the lockfile the dependency RBS
|
|
169
|
+
# is absent. Two `stat`s and no read.
|
|
170
|
+
def self.missing_lockfile_hint(lockfile_path:, project_root: Dir.pwd, auto_detect: true)
|
|
171
|
+
return nil if lockfile_path.nil? && !auto_detect
|
|
172
|
+
return nil if resolve_lockfile_path(lockfile_path: lockfile_path, project_root: project_root,
|
|
173
|
+
auto_detect: auto_detect)
|
|
174
|
+
|
|
175
|
+
root = Pathname(File.expand_path(project_root.to_s))
|
|
176
|
+
return :collection_test_dir if collection_test_dir?(root)
|
|
177
|
+
return :collection_config if root.join("rbs_collection.yaml").file?
|
|
178
|
+
|
|
179
|
+
nil
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# `gems/<name>/<version>/_test/`, the layout of a gem_rbs_collection working tree.
|
|
183
|
+
def self.collection_test_dir?(root)
|
|
184
|
+
root.basename.to_s == "_test" && root.parent.parent.parent.basename.to_s == "gems"
|
|
185
|
+
end
|
|
186
|
+
private_class_method :collection_test_dir?
|
|
187
|
+
|
|
80
188
|
# Returns the resolved lockfile path (`Pathname`) or `nil` when neither explicit nor auto-detect
|
|
81
189
|
# produces one. Public so the stats banner can surface what rigor found.
|
|
82
190
|
def self.resolve_lockfile_path(lockfile_path:, project_root: Dir.pwd, auto_detect: true)
|
|
@@ -110,12 +218,19 @@ module Rigor
|
|
|
110
218
|
end
|
|
111
219
|
private_class_method :resolve_collection_root
|
|
112
220
|
|
|
221
|
+
# An entry's `source.type`, or "" when `source:` is absent or not a mapping (`source: stdlib`): a malformed
|
|
222
|
+
# entry fails soft (ADR-5) instead of raising out of the run.
|
|
223
|
+
def self.source_type(entry)
|
|
224
|
+
source = entry["source"]
|
|
225
|
+
source.is_a?(Hash) ? source["type"].to_s : ""
|
|
226
|
+
end
|
|
227
|
+
private_class_method :source_type
|
|
228
|
+
|
|
113
229
|
def self.gem_paths_from(collection_root, data, skip_gem_names)
|
|
114
230
|
Array(data["gems"]).filter_map do |entry|
|
|
115
231
|
next unless entry.is_a?(Hash)
|
|
116
232
|
|
|
117
|
-
|
|
118
|
-
next if SKIPPED_SOURCE_TYPES.include?(source_type)
|
|
233
|
+
next if SKIPPED_SOURCE_TYPES.include?(source_type(entry))
|
|
119
234
|
|
|
120
235
|
name = entry["name"]
|
|
121
236
|
version = entry["version"]
|
|
@@ -5,8 +5,9 @@ module Rigor
|
|
|
5
5
|
# Open item O4 Layer 3 slice 3 — graceful-degradation coverage report.
|
|
6
6
|
#
|
|
7
7
|
# When the user has a `Gemfile.lock` (via slice 1) and rigor has resolved its target-project RBS sources
|
|
8
|
-
# (
|
|
9
|
-
# `rbs_collection.lock.yaml` paths), this module classifies each
|
|
8
|
+
# (the resolved library list — `Environment::BaseLibraries.resolve`, `data/vendored_gem_sigs/`,
|
|
9
|
+
# slice-1 bundle-shipped `sig/`, slice-2 `rbs_collection.lock.yaml` paths), this module classifies each
|
|
10
|
+
# locked gem by RBS provenance and
|
|
10
11
|
# surfaces the "no RBS available" set so the run-start diagnostic in {Rigor::Analysis::Runner} can
|
|
11
12
|
# suggest `rbs collection install` or `dependencies.source_inference:` for the uncovered gems.
|
|
12
13
|
#
|
|
@@ -37,9 +38,9 @@ module Rigor
|
|
|
37
38
|
|
|
38
39
|
# @param locked_gems — The lockfile-resolved gem set. Empty
|
|
39
40
|
# hash → no coverage analysis to do.
|
|
40
|
-
# @param default_libraries —
|
|
41
|
-
# `
|
|
42
|
-
# running in a project context.
|
|
41
|
+
# @param default_libraries — the resolved list of names rigor loads as RBS libraries
|
|
42
|
+
# (`Environment::BaseLibraries.resolve`: `DEFAULT_LIBRARIES` + `libraries:` + the collection
|
|
43
|
+
# lockfile's stdlib entries). Pass it from callers running in a project context.
|
|
43
44
|
# @param bundle_sig_paths — the discovered `<bundle>/.../gems/<name>-<ver>/sig`
|
|
44
45
|
# paths from {BundleSigDiscovery.discover}.
|
|
45
46
|
# @param rbs_collection_paths — the discovered `<collection>/<name>/<version>/`
|
|
@@ -48,7 +49,7 @@ module Rigor
|
|
|
48
49
|
def self.classify(locked_gems:, default_libraries:,
|
|
49
50
|
bundle_sig_paths:, rbs_collection_paths:)
|
|
50
51
|
default_set = default_libraries.to_set
|
|
51
|
-
bundle_names = extract_gem_names_from_bundle_paths(bundle_sig_paths)
|
|
52
|
+
bundle_names = extract_gem_names_from_bundle_paths(bundle_sig_paths, locked_gems)
|
|
52
53
|
collection_names = extract_gem_names_from_collection_paths(rbs_collection_paths)
|
|
53
54
|
|
|
54
55
|
locked_gems.each_value.map do |locked|
|
|
@@ -73,11 +74,11 @@ module Rigor
|
|
|
73
74
|
coverage_rows.select { |row| row.source == :missing }
|
|
74
75
|
end
|
|
75
76
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
# Issue #2978 — each path's gem name as the locked entry it matches ({BundleSigDiscovery.locked_names_of}), not
|
|
78
|
+
# a parse of its directory name, which reads `foo-2fa-1.0` as `foo` and reported `foo-2fa` as missing RBS.
|
|
79
|
+
def self.extract_gem_names_from_bundle_paths(paths, locked_gems)
|
|
80
|
+
pathnames = paths.map { |path| path.is_a?(Pathname) ? path : Pathname.new(path) }
|
|
81
|
+
BundleSigDiscovery.locked_names_of(pathnames, locked_gems).to_set
|
|
81
82
|
end
|
|
82
83
|
private_class_method :extract_gem_names_from_bundle_paths
|
|
83
84
|
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "../typing_state"
|
|
4
|
+
|
|
3
5
|
module Rigor
|
|
4
6
|
class Environment
|
|
5
7
|
# Small hierarchy oracle backed by RBS instance definitions.
|
|
6
8
|
class RbsHierarchy
|
|
9
|
+
TypingState.memo_field(self, :@ancestor_names_cache, :@class_ordering_cache,
|
|
10
|
+
reason: "keyed on the class names; answered from the loader's RBS environment, fixed " \
|
|
11
|
+
"for the loader's lifetime")
|
|
12
|
+
|
|
7
13
|
def initialize(loader)
|
|
8
14
|
@loader = loader
|
|
9
15
|
@ancestor_names_cache = {}
|