rigortype 0.4.4 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +5 -5
- data/docs/handbook/11-sig-gen.md +90 -17
- data/docs/manual/02-cli-reference.md +39 -9
- data/docs/manual/03-configuration.md +10 -8
- data/docs/manual/04-diagnostics.md +16 -5
- data/docs/manual/05-inspecting-types.md +2 -2
- data/docs/manual/06-baseline.md +1 -1
- data/docs/manual/07-plugins.md +2 -1
- data/docs/manual/08-skills.md +7 -0
- data/docs/manual/10-mcp-server.md +8 -1
- data/docs/manual/18-removing-dead-code.md +18 -7
- data/docs/manual/plugins/rigor-rspec.md +101 -1
- data/lib/rigor/analysis/check_rules/clause_coverage.rb +134 -0
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +111 -7
- data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +190 -0
- data/lib/rigor/analysis/check_rules/rule_ids.rb +3 -1
- data/lib/rigor/analysis/check_rules/source_arity.rb +32 -3
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +13 -4
- data/lib/rigor/analysis/check_rules.rb +228 -15
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +2 -1
- data/lib/rigor/analysis/incremental.rb +49 -0
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +36 -18
- data/lib/rigor/analysis/incremental_session.rb +166 -24
- data/lib/rigor/analysis/path_expansion.rb +3 -1
- data/lib/rigor/analysis/reachability/graph.rb +90 -13
- data/lib/rigor/analysis/reachability/scan.rb +279 -39
- data/lib/rigor/analysis/rule_catalog.rb +17 -4
- data/lib/rigor/analysis/runner/pool_coordinator.rb +69 -16
- data/lib/rigor/analysis/runner.rb +69 -65
- data/lib/rigor/analysis/severity_stamp.rb +11 -0
- data/lib/rigor/analysis/worker_session.rb +12 -1
- data/lib/rigor/cache/descriptor.rb +40 -7
- data/lib/rigor/cache/engine_source.rb +3 -2
- data/lib/rigor/cache/incremental_snapshot.rb +26 -8
- data/lib/rigor/cache/rbs_descriptor.rb +2 -1
- data/lib/rigor/cache/store.rb +4 -3
- data/lib/rigor/cli/command.rb +3 -1
- data/lib/rigor/cli/docs_command.rb +3 -2
- data/lib/rigor/cli/doctor_command.rb +1 -1
- data/lib/rigor/cli/explain_command.rb +3 -2
- data/lib/rigor/cli/plugin_command.rb +2 -1
- data/lib/rigor/cli/plugins_command.rb +2 -1
- data/lib/rigor/cli/sig_gen_command.rb +32 -11
- data/lib/rigor/cli/skill_command.rb +2 -1
- data/lib/rigor/cli/skill_describe.rb +3 -2
- data/lib/rigor/cli/unused_command.rb +10 -7
- data/lib/rigor/cli.rb +3 -2
- data/lib/rigor/config_audit.rb +60 -15
- data/lib/rigor/configuration/severity_profile.rb +20 -2
- data/lib/rigor/configuration.rb +29 -3
- data/lib/rigor/effects/signature_sources.rb +3 -1
- data/lib/rigor/environment/bundle_sig_discovery.rb +3 -2
- data/lib/rigor/environment/gated_signature_guard.rb +2 -1
- data/lib/rigor/environment/gated_signature_plan.rb +2 -1
- data/lib/rigor/environment/installed_gem_set.rb +2 -1
- data/lib/rigor/environment/missing_gem_constant_index.rb +4 -3
- data/lib/rigor/environment/rbs_loader.rb +21 -1
- data/lib/rigor/inference/block_call_timing.rb +7 -1
- data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
- data/lib/rigor/inference/captured_locals.rb +44 -13
- data/lib/rigor/inference/clause_widening.rb +90 -0
- data/lib/rigor/inference/definer_resolution.rb +87 -20
- data/lib/rigor/inference/expression_typer.rb +35 -5
- data/lib/rigor/inference/intervening_ivar_reset.rb +303 -0
- data/lib/rigor/inference/macro_block_self_type.rb +20 -6
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +170 -35
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +50 -2
- data/lib/rigor/inference/method_dispatcher.rb +67 -3
- data/lib/rigor/inference/module_function_state.rb +137 -42
- data/lib/rigor/inference/narrowing.rb +54 -10
- data/lib/rigor/inference/parameter_inference_collector.rb +4 -26
- data/lib/rigor/inference/project_patched_methods.rb +168 -4
- data/lib/rigor/inference/project_patched_mixins.rb +162 -0
- data/lib/rigor/inference/project_patched_scanner.rb +51 -12
- data/lib/rigor/inference/project_patched_visibility.rb +214 -0
- data/lib/rigor/inference/project_seed_tables.rb +120 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +113 -0
- data/lib/rigor/inference/refined_dispatch/own_method.rb +41 -12
- data/lib/rigor/inference/reflective_ivar_write.rb +6 -6
- data/lib/rigor/inference/return_barrier.rb +16 -0
- data/lib/rigor/inference/scope_indexer.rb +675 -105
- data/lib/rigor/inference/self_call_ivar_writes.rb +11 -6
- data/lib/rigor/inference/singleton_external_ahead.rb +64 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +7 -0
- data/lib/rigor/inference/statement_evaluator.rb +446 -139
- data/lib/rigor/inference/unthreaded_rebinds.rb +3 -1
- data/lib/rigor/lens/contract_reader.rb +45 -2
- data/lib/rigor/lens/document.rb +2 -1
- data/lib/rigor/lens/file_reader.rb +12 -2
- data/lib/rigor/lens/members.rb +16 -2
- data/lib/rigor/lens/run.rb +34 -7
- data/lib/rigor/mcp/server.rb +2 -2
- data/lib/rigor/plugin/base.rb +3 -2
- data/lib/rigor/plugin/io_boundary.rb +42 -6
- data/lib/rigor/plugin/isolation.rb +4 -3
- data/lib/rigor/protection/discovery_seed.rb +9 -36
- data/lib/rigor/protection/mutation_cache.rb +8 -2
- data/lib/rigor/rooted_glob.rb +27 -0
- data/lib/rigor/scope/discovery_index.rb +5 -0
- data/lib/rigor/scope/resolution_chain/relevance.rb +14 -0
- data/lib/rigor/scope/resolution_chain.rb +40 -5
- data/lib/rigor/scope.rb +45 -2
- data/lib/rigor/sig_gen/alias_index.rb +2 -1
- data/lib/rigor/sig_gen/classification.rb +4 -1
- data/lib/rigor/sig_gen/generator.rb +167 -22
- data/lib/rigor/sig_gen/layout_index.rb +5 -2
- data/lib/rigor/sig_gen/observation_collector.rb +137 -11
- data/lib/rigor/sig_gen/renderer.rb +13 -155
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +11 -0
- data/lib/rigor/sig_gen/write_reporter.rb +203 -0
- data/lib/rigor/sig_gen/write_result.rb +3 -1
- data/lib/rigor/sig_gen/writer.rb +5 -3
- data/lib/rigor/signature_path_audit.rb +4 -2
- data/lib/rigor/type/constant.rb +10 -1
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +1 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +6 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +1 -1
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +4 -3
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +1 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +1 -1
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +1 -1
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -1
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +1 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +38 -9
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/target_detector.rb +1 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +1 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +1 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +7 -2
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -5
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +18 -17
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +1 -1
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +4 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +2 -2
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +2 -2
- data/sig/rigor/plugin/io_boundary.rbs +5 -2
- data/sig/rigor/rooted_glob.rbs +7 -0
- data/sig/rigor/scope.rbs +4 -3
- data/skills/rigor-doctor/references/01-checks.md +23 -11
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +2 -1
- metadata +13 -1
|
@@ -139,10 +139,11 @@ module Rigor
|
|
|
139
139
|
# Issue #1596 — gives up when `self`'s class, a project subclass, or a project ancestor or module on either's
|
|
140
140
|
# chain defines a {PROTOCOL_NAMES} method (a `def`, `define_method`, `attr_*` or alias), rewrites its methods
|
|
141
141
|
# beyond the names it spells, or is refined on one; or when either chain holds an external ancestor, whose
|
|
142
|
-
# own definitions the read cannot see.
|
|
142
|
+
# own definitions the read cannot see. Issue #1979 — a class built on a core constructor call
|
|
143
|
+
# (`class Fact < Data.define(:a)`) cannot be `self`'s class or a subclass of it, so the walk leaves it out.
|
|
143
144
|
def no_protocol_override
|
|
144
145
|
classes = [@class_name]
|
|
145
|
-
RefinedDispatch::OwnMethod.each_subclass(@scope, @class_name) { |subclass| classes << subclass }
|
|
146
|
+
RefinedDispatch::OwnMethod.each_subclass(@scope, @class_name, possible_only: true) { |subclass| classes << subclass }
|
|
146
147
|
classes.each do |class_name|
|
|
147
148
|
chain = Scope::ResolutionChain.for(@scope, class_name.to_s, :instance, :methods)
|
|
148
149
|
unprovable! if chain.truncated? || chain.wildcard_mixin? || !settled?(chain)
|
|
@@ -229,7 +230,7 @@ module Rigor
|
|
|
229
230
|
|
|
230
231
|
# A subclass whose superclass the tables do not know is enumerated too, and its chain ends short of the
|
|
231
232
|
# owner; one that reaches no definer of its own leaves the call to its superclass.
|
|
232
|
-
RefinedDispatch::OwnMethod.each_subclass(@scope, @class_name) do |subclass|
|
|
233
|
+
RefinedDispatch::OwnMethod.each_subclass(@scope, @class_name, possible_only: true) do |subclass|
|
|
233
234
|
unprovable! unless [owner, nil].include?(definer_ahead(subclass, name, owner))
|
|
234
235
|
end
|
|
235
236
|
def_node
|
|
@@ -258,9 +259,11 @@ module Rigor
|
|
|
258
259
|
end
|
|
259
260
|
|
|
260
261
|
def definition_count(owner, name)
|
|
261
|
-
|
|
262
|
-
|
|
262
|
+
count = 0
|
|
263
|
+
@scope.discovered_deferred_ranges.each_value do |rows|
|
|
264
|
+
count += rows.count { |row| row[2] == name && row[3] == :instance && row[4] == owner }
|
|
263
265
|
end
|
|
266
|
+
count
|
|
264
267
|
end
|
|
265
268
|
|
|
266
269
|
# Whether `chain`'s own order stands with no second world: no order mark and no fork. `ResolutionChain#settle`
|
|
@@ -326,8 +329,10 @@ module Rigor
|
|
|
326
329
|
return nil if loader.nil? || !loader.core_or_stdlib_class?(type.class_name)
|
|
327
330
|
|
|
328
331
|
segment = node.name.to_s
|
|
332
|
+
suffix = "::#{segment}"
|
|
329
333
|
project = [@scope.discovered_classes, @scope.in_source_constants]
|
|
330
|
-
|
|
334
|
+
ends_with_segment = ->(name) { (name = name.to_s) == segment || name.end_with?(suffix) }
|
|
335
|
+
return nil if project.any? { |table| table.each_key.any?(&ends_with_segment) }
|
|
331
336
|
|
|
332
337
|
type
|
|
333
338
|
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rigor
|
|
4
|
+
module Inference
|
|
5
|
+
# Issue #1572 — the singleton typing site's external-ahead decline. That site still answers from
|
|
6
|
+
# {Scope#singleton_def_through_ancestors}, which passes the chain's external entries over, so `extend M; extend
|
|
7
|
+
# Enumerable` typed `C.to_a` from `M#to_a` although Ruby calls `Enumerable#to_a` (`C.singleton_class.ancestors`
|
|
8
|
+
# starts `[#<Class:C>, Enumerable, M]`). The instance typing site reads `DefinerResolution`, which already
|
|
9
|
+
# declines there.
|
|
10
|
+
#
|
|
11
|
+
# {.withhold?} is true when the read must withhold (the call types `Dynamic`): the singleton chain holds no
|
|
12
|
+
# project entry that writes the name; the reader's `owner` has no project entry on it; or an external entry ahead
|
|
13
|
+
# of the later of the two may answer first, because RBS does not know it or declares the name in it (the test
|
|
14
|
+
# `DefinerResolution` applies, `ResolutionChain::Relevance.external_lacks_on?`). The later of the two, because
|
|
15
|
+
# where the chain does not stand the reader answers master's owner. An entry writes the name with its own `def`,
|
|
16
|
+
# never a copy the extends fold put on a class object (`RefinedDispatch::ClassObject.fold_copy?`, which also
|
|
17
|
+
# matches a copy by location where the two tables came from two parses of the file), so a folded `M#to_a`
|
|
18
|
+
# answers at `M`'s own position.
|
|
19
|
+
#
|
|
20
|
+
# ADR-46: the search files the class edge of the root and of every project entry ahead of the definer, and each
|
|
21
|
+
# tested external files the negative class edge on its spelling's last segment, as `DefinerResolution` does.
|
|
22
|
+
module SingletonExternalAhead
|
|
23
|
+
module_function
|
|
24
|
+
|
|
25
|
+
def withhold?(scope, class_name, method_name, owner)
|
|
26
|
+
chain = Scope::ResolutionChain.for(scope, class_name.to_s, :singleton, :methods)
|
|
27
|
+
definer = first_writer_index(scope, chain, method_name)
|
|
28
|
+
return true if definer.nil?
|
|
29
|
+
|
|
30
|
+
owner_index = chain.entries.index { |entry| !entry.external? && entry.name == owner.to_s }
|
|
31
|
+
return true if owner_index.nil?
|
|
32
|
+
|
|
33
|
+
external_ahead_may_answer?(scope, chain, method_name, [definer, owner_index].max)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def first_writer_index(scope, chain, method_name)
|
|
37
|
+
index = -1
|
|
38
|
+
chain.search(scope) do |entry|
|
|
39
|
+
index += 1
|
|
40
|
+
!entry.external? && writes_name?(scope, entry, method_name)
|
|
41
|
+
end && index
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def writes_name?(scope, entry, method_name)
|
|
45
|
+
return !scope.user_def_for(entry.name, method_name).nil? unless entry.side == :singleton
|
|
46
|
+
|
|
47
|
+
!scope.singleton_def_for(entry.name, method_name).nil? &&
|
|
48
|
+
!RefinedDispatch::ClassObject.fold_copy?(scope, entry.name, method_name)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def external_ahead_may_answer?(scope, chain, method_name, stop)
|
|
52
|
+
recording = Analysis::DependencyRecorder.active?
|
|
53
|
+
chain.entries.first(stop).any? do |entry|
|
|
54
|
+
next false unless entry.external?
|
|
55
|
+
|
|
56
|
+
Analysis::DependencyRecorder.read_missing(:class, entry.raw.to_s.split("::").last) if recording
|
|
57
|
+
!Scope::ResolutionChain::Relevance.external_lacks_on?(scope, entry.candidates, entry.side, method_name)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private_class_method :first_writer_index, :writes_name?, :external_ahead_may_answer?
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -109,6 +109,13 @@ module Rigor
|
|
|
109
109
|
nesting&.first
|
|
110
110
|
end
|
|
111
111
|
|
|
112
|
+
# Issue #1722 — the hook-capable test for one chain entry, asked by `call.undefined-method` of a project module
|
|
113
|
+
# mixed into a core class: a module whose `self.included(base)` hook writes `base.class_eval { def x }` gives
|
|
114
|
+
# the includer methods no table records.
|
|
115
|
+
def entry_hook_capable?(scope, entry, flavor = :methods)
|
|
116
|
+
Context.new(scope, flavor).capable?(entry)
|
|
117
|
+
end
|
|
118
|
+
|
|
112
119
|
def tested_levels(chain, from, bound)
|
|
113
120
|
starts = chain.level_starts
|
|
114
121
|
size = chain.entries.size
|