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
data/lib/rigor/sig_gen/writer.rb
CHANGED
|
@@ -98,7 +98,8 @@ module Rigor
|
|
|
98
98
|
source_path ||= candidates.first&.path
|
|
99
99
|
unless inside_sig_root?(target)
|
|
100
100
|
return WriteResult.new(source_path: source_path, target_path: target,
|
|
101
|
-
action: :skipped_outside_sig_root
|
|
101
|
+
action: :skipped_outside_sig_root,
|
|
102
|
+
skipped: candidates.map { |c| [c, :outside_sig_root] })
|
|
102
103
|
end
|
|
103
104
|
|
|
104
105
|
target.exist? ? update_existing(source_path, target, candidates) : create_new(source_path, target, candidates)
|
|
@@ -636,7 +637,8 @@ module Rigor
|
|
|
636
637
|
end
|
|
637
638
|
|
|
638
639
|
# Returns a list of `[method_name (Symbol), kind (Symbol)]` pairs for every method-like member in the
|
|
639
|
-
# declaration ({LayoutIndex.member_keys}). ADR-14 slice 4
|
|
640
|
+
# declaration ({LayoutIndex.member_keys}); an `alias` declares its new name (#1975). ADR-14 slice 4
|
|
641
|
+
# recognises the three `attr_*` declaration kinds
|
|
640
642
|
# so a source-side `attr_reader :name` and an RBS-side `attr_reader name: T` are treated as the same member
|
|
641
643
|
# (i.e. user-authored); a `def self?.f` counts as both an instance and a singleton `f` (#1488).
|
|
642
644
|
def collect_member_pairs(decl)
|
|
@@ -754,7 +756,7 @@ module Rigor
|
|
|
754
756
|
|
|
755
757
|
def find_attr_member(decl, method_name, kind)
|
|
756
758
|
decl.members.find do |m|
|
|
757
|
-
next false if m.is_a?(RBS::AST::Members::MethodDefinition)
|
|
759
|
+
next false if m.is_a?(RBS::AST::Members::MethodDefinition) || m.is_a?(RBS::AST::Members::Alias)
|
|
758
760
|
|
|
759
761
|
LayoutIndex.member_keys(m).include?([method_name, kind])
|
|
760
762
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "rooted_glob"
|
|
4
|
+
|
|
3
5
|
module Rigor
|
|
4
6
|
# Classifies each configured `signature_paths:` entry by what it actually contributes to
|
|
5
7
|
# the RBS environment, so a caller can warn when a configured path resolves to nothing.
|
|
@@ -90,7 +92,7 @@ module Rigor
|
|
|
90
92
|
return Entry.new(path: path, status: :missing, rbs_file_count: 0) unless File.exist?(path)
|
|
91
93
|
return Entry.new(path: path, status: :not_directory, rbs_file_count: 0) unless File.directory?(path)
|
|
92
94
|
|
|
93
|
-
count =
|
|
95
|
+
count = RootedGlob.glob(path, "**/*.rbs").size
|
|
94
96
|
Entry.new(path: path, status: count.zero? ? :empty : :ok, rbs_file_count: count)
|
|
95
97
|
end
|
|
96
98
|
|
|
@@ -204,7 +206,7 @@ module Rigor
|
|
|
204
206
|
private_class_method :listed?
|
|
205
207
|
|
|
206
208
|
def self.canonical_rbs_files(dir)
|
|
207
|
-
|
|
209
|
+
RootedGlob.glob(dir, "**/*.rbs").filter_map { |file| canonical_path(file) }
|
|
208
210
|
end
|
|
209
211
|
private_class_method :canonical_rbs_files
|
|
210
212
|
|
data/lib/rigor/type/constant.rb
CHANGED
|
@@ -78,7 +78,8 @@ module Rigor
|
|
|
78
78
|
# `RbsTypeTranslator#translate_literal` already maps the parsed RBS Literal back to `Constant`.
|
|
79
79
|
# Scalar carriers without RBS Literal support (Float, Range, Rational, Complex, Regexp, Pathname)
|
|
80
80
|
# keep their pre-existing widen-to-class-name behaviour because RBS rejects their literal spellings
|
|
81
|
-
# as syntax errors.
|
|
81
|
+
# as syntax errors. `Range` is generic (`Range[out E]`), and a bare `Range` fails `rbs validate`, so
|
|
82
|
+
# it carries the class of its endpoints, or `untyped` when both are nil (#1891).
|
|
82
83
|
def erase_to_rbs
|
|
83
84
|
case value
|
|
84
85
|
when true then "true"
|
|
@@ -86,6 +87,7 @@ module Rigor
|
|
|
86
87
|
when nil then "nil"
|
|
87
88
|
when Integer then value.to_s
|
|
88
89
|
when Symbol, String then value.inspect
|
|
90
|
+
when Range then "Range[#{range_element_rbs}]"
|
|
89
91
|
else value.class.name
|
|
90
92
|
end
|
|
91
93
|
end
|
|
@@ -106,6 +108,13 @@ module Rigor
|
|
|
106
108
|
def inspect
|
|
107
109
|
"#<Rigor::Type::Constant #{describe(:short)}>"
|
|
108
110
|
end
|
|
111
|
+
|
|
112
|
+
private
|
|
113
|
+
|
|
114
|
+
def range_element_rbs
|
|
115
|
+
classes = [value.begin, value.end].compact.map { |endpoint| endpoint.class.name }.uniq.sort
|
|
116
|
+
classes.empty? ? "untyped" : classes.join(" | ")
|
|
117
|
+
end
|
|
109
118
|
end
|
|
110
119
|
end
|
|
111
120
|
end
|
data/lib/rigor/version.rb
CHANGED
|
@@ -99,7 +99,7 @@ module Rigor
|
|
|
99
99
|
# ADR-45 WD1b (#613) — boundary-probed: a root that appears later invalidates the warm run.
|
|
100
100
|
next [] unless @io_boundary.directory?(absolute)
|
|
101
101
|
|
|
102
|
-
Dir.glob(File.join(absolute,
|
|
102
|
+
Dir.glob("**/*.rb", base: absolute).map { |path| File.join(absolute, path) }
|
|
103
103
|
end
|
|
104
104
|
end
|
|
105
105
|
|
|
@@ -90,7 +90,7 @@ module Rigor
|
|
|
90
90
|
# ADR-45 WD1b (#613) — boundary-probed: a root that appears later invalidates the warm run.
|
|
91
91
|
next [] unless @io_boundary.directory?(absolute)
|
|
92
92
|
|
|
93
|
-
Dir.glob(File.join(absolute,
|
|
93
|
+
Dir.glob("**/*.rb", base: absolute).map { |path| File.join(absolute, path) }
|
|
94
94
|
end
|
|
95
95
|
end
|
|
96
96
|
|
|
@@ -58,7 +58,7 @@ module Rigor
|
|
|
58
58
|
# ADR-45 WD1b (#613) — boundary-probed: a root that appears later invalidates the warm run.
|
|
59
59
|
next [] unless @io_boundary.directory?(absolute)
|
|
60
60
|
|
|
61
|
-
Dir.glob(File.join(absolute,
|
|
61
|
+
Dir.glob("**/*.rb", base: absolute).map { |path| File.join(absolute, path) }
|
|
62
62
|
end
|
|
63
63
|
end
|
|
64
64
|
|
|
@@ -159,13 +159,18 @@ module Rigor
|
|
|
159
159
|
|
|
160
160
|
# Project-relative paths, the spelling `#template_units_for_file` is handed and the spelling the
|
|
161
161
|
# compiled cache is keyed by.
|
|
162
|
+
#
|
|
163
|
+
# #1803 — listed through the boundary's `#glob`, which records each root's membership: a helper
|
|
164
|
+
# added under `app/helpers`, or a template under a `view_search_paths:` root the template globs do
|
|
165
|
+
# not claim, is analysed by nobody, and without the row the run-result caches kept serving the
|
|
166
|
+
# index built before it appeared.
|
|
162
167
|
def self.source_files(io_boundary, roots, pattern)
|
|
163
168
|
root_prefix = "#{Dir.pwd}#{File::SEPARATOR}"
|
|
164
169
|
roots.flat_map do |root|
|
|
165
170
|
absolute = File.expand_path(root)
|
|
166
171
|
next [] unless io_boundary.directory?(absolute)
|
|
167
172
|
|
|
168
|
-
|
|
173
|
+
io_boundary.glob(absolute, "**/#{pattern}").map { |path| path.delete_prefix(root_prefix) }
|
|
169
174
|
end
|
|
170
175
|
end
|
|
171
176
|
|
|
@@ -127,7 +127,7 @@ module Rigor
|
|
|
127
127
|
# ADR-45 WD1b (#613) — boundary-probed: a root that appears later invalidates the warm run.
|
|
128
128
|
next [] unless @io_boundary.directory?(absolute)
|
|
129
129
|
|
|
130
|
-
Dir.glob(File.join(absolute,
|
|
130
|
+
Dir.glob("**/*.rb", base: absolute).map { |path| File.join(absolute, path) }
|
|
131
131
|
end
|
|
132
132
|
end
|
|
133
133
|
|
|
@@ -146,11 +146,12 @@ module Rigor
|
|
|
146
146
|
|
|
147
147
|
# ADR-45 WD1b (#613) — `config/application.rb` is probed at a fixed path, so its ABSENCE is what
|
|
148
148
|
# shapes "no adapter" on a project that has none yet; the probe goes through the boundary to record
|
|
149
|
-
# that. The
|
|
150
|
-
# (immediately superseded) presence row.
|
|
149
|
+
# that. The `config/environments/*.rb` listing goes through `#glob` (#1803), whose row records the
|
|
150
|
+
# directory's membership; each listed entry only picks up the (immediately superseded) presence row.
|
|
151
151
|
def config_files(io_boundary, root)
|
|
152
152
|
application = File.join(root, CONFIG_PATHS.first)
|
|
153
|
-
|
|
153
|
+
environments_dir = File.join(root, CONFIG_PATHS.last)
|
|
154
|
+
environments = io_boundary.glob(environments_dir, "*.rb")
|
|
154
155
|
([application] + environments).select { |path| io_boundary.file?(path) }
|
|
155
156
|
rescue StandardError
|
|
156
157
|
[]
|
|
@@ -92,7 +92,7 @@ module Rigor
|
|
|
92
92
|
# ADR-45 WD1b (#613) — boundary-probed: a root that appears later invalidates the warm run.
|
|
93
93
|
next [] unless @io_boundary.directory?(absolute)
|
|
94
94
|
|
|
95
|
-
Dir.glob(File.join(absolute,
|
|
95
|
+
Dir.glob("**/*.rb", base: absolute).map { |path| File.join(absolute, path) }
|
|
96
96
|
end
|
|
97
97
|
end
|
|
98
98
|
|
|
@@ -375,7 +375,7 @@ module Rigor
|
|
|
375
375
|
# ADR-45 WD1b (#613) — boundary-probed: a root that appears later invalidates the warm run.
|
|
376
376
|
next [] unless @io_boundary.directory?(absolute)
|
|
377
377
|
|
|
378
|
-
Dir.glob(File.join(absolute,
|
|
378
|
+
Dir.glob("**/*.rb", base: absolute).map { |path| File.join(absolute, path) }
|
|
379
379
|
end
|
|
380
380
|
end
|
|
381
381
|
|
|
@@ -69,7 +69,7 @@ module Rigor
|
|
|
69
69
|
# ADR-45 WD1b (#613) — boundary-probed: a root that appears later invalidates the warm run.
|
|
70
70
|
next [] unless @io_boundary.directory?(absolute)
|
|
71
71
|
|
|
72
|
-
Dir.glob(File.join(absolute,
|
|
72
|
+
Dir.glob("**/*.rb", base: absolute).map { |path| File.join(absolute, path) }
|
|
73
73
|
end
|
|
74
74
|
end
|
|
75
75
|
|
|
@@ -65,7 +65,7 @@ module Rigor
|
|
|
65
65
|
# ADR-45 WD1b (#613) — boundary-probed: a root that appears later invalidates the warm run.
|
|
66
66
|
next [] unless @io_boundary.directory?(absolute)
|
|
67
67
|
|
|
68
|
-
Dir.glob(File.join(absolute,
|
|
68
|
+
Dir.glob("**/*.rb", base: absolute).map { |path| File.join(absolute, path) }
|
|
69
69
|
end
|
|
70
70
|
end
|
|
71
71
|
end
|
|
@@ -109,7 +109,7 @@ module Rigor
|
|
|
109
109
|
def scannable_paths
|
|
110
110
|
@scannable_paths ||= services.configuration.paths.flat_map do |entry|
|
|
111
111
|
if io_boundary.directory?(entry)
|
|
112
|
-
Dir.glob(File.join(entry,
|
|
112
|
+
Dir.glob("**/*.rb", base: entry).map { |path| File.join(entry, path) }
|
|
113
113
|
elsif io_boundary.file?(entry) && entry.end_with?(".rb")
|
|
114
114
|
[entry]
|
|
115
115
|
else
|
|
@@ -11,6 +11,8 @@ module Rigor
|
|
|
11
11
|
# carried no row at all and was served after any edit, an analysed file's included, until `--no-cache`.
|
|
12
12
|
class Discoverer
|
|
13
13
|
RUBY_GLOB = "**/*.rb"
|
|
14
|
+
RUBY_FILE = "*.rb"
|
|
15
|
+
SKIPPED_DIRECTORIES = %w[vendor .git].freeze
|
|
14
16
|
|
|
15
17
|
def self.discover(root:, io:)
|
|
16
18
|
new(root: root, io: io).discover
|
|
@@ -67,20 +69,47 @@ module Rigor
|
|
|
67
69
|
|
|
68
70
|
private
|
|
69
71
|
|
|
72
|
+
# #1959 — one `:kinds` listing row over the root's top-level entries, then one `:names` `**/*.rb` row per
|
|
73
|
+
# real directory among them other than `vendor/`. A single `**/*.rb` row over the root re-walked an in-tree
|
|
74
|
+
# `vendor/bundle` on every warm validation and read stale on every `bundle install`. The listing carries
|
|
75
|
+
# each entry's kind and not just its name: the walk descends a directory but not a symlinked one (`**` never
|
|
76
|
+
# followed it), so a file `gen` that becomes a directory `gen/`, or a symlink that becomes a real
|
|
77
|
+
# directory, changes what it reads while every name stays put.
|
|
78
|
+
#
|
|
79
|
+
# The answer is `Dir.glob("**/*.rb")` minus the skipped paths and minus every match that is not a regular
|
|
80
|
+
# file (#2113), in its order: entry by entry, a top-level name matching `*.rb`, then that directory's
|
|
81
|
+
# matches. A directory so named is walked, never read. The boundary answers absolute paths; they are spelled
|
|
82
|
+
# back under `@root` as `Dir.glob` spells them.
|
|
70
83
|
def ruby_files
|
|
71
84
|
return [] unless @root && @io.directory?(@root.to_s)
|
|
72
85
|
|
|
73
|
-
boundary_glob.reject { |p| p.include?("/vendor/") || p.include?("/.git/") }
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# One `:names` `**/*.rb` row over the root, `vendor/` included: a per-entry walk that skipped it recorded
|
|
77
|
-
# names only, so a top-level entry that changed kind under the same name went unseen. The boundary
|
|
78
|
-
# answers absolute paths; they are spelled back under `@root` as `Dir.glob` spells them, so the filter
|
|
79
|
-
# above sees the same strings it always did.
|
|
80
|
-
def boundary_glob
|
|
81
86
|
root = @root.to_s
|
|
82
87
|
prefix = "#{File.absolute_path(root)}/"
|
|
83
|
-
@io.
|
|
88
|
+
@io.list_directory_kinds(root).flat_map do |path, kind|
|
|
89
|
+
name = path.delete_prefix(prefix)
|
|
90
|
+
own = File.fnmatch?(RUBY_FILE, name) && readable_file?(path, kind) ? [File.join(root, name)] : []
|
|
91
|
+
next own unless kind == :directory && !SKIPPED_DIRECTORIES.include?(name)
|
|
92
|
+
|
|
93
|
+
own + @io.glob(path, RUBY_GLOB).filter_map do |nested|
|
|
94
|
+
relative = nested.delete_prefix(prefix)
|
|
95
|
+
File.join(root, relative) if !skipped?(relative) && @io.file?(nested)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# #2113 — `*.rb` names a directory too, and a fifo, socket or device can carry the name. Only a regular file
|
|
101
|
+
# is read: `read_file` on a directory raises `EISDIR` and aborts the whole walk, and on a fifo it blocks
|
|
102
|
+
# until a writer appears. A top-level entry is judged by its `lstat` kind, and a symlink by what it
|
|
103
|
+
# resolves to; a nested match is judged by `file?` (which follows a symlink as `File.file?` does).
|
|
104
|
+
def readable_file?(path, kind)
|
|
105
|
+
kind == :file || (kind == :symlink && @io.file?(path))
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# #1956 — the skip reads the path relative to the root, a `vendor` or `.git` directory segment at any
|
|
109
|
+
# depth under it. Matching `/vendor/` in the whole path also dropped every file of a project that itself
|
|
110
|
+
# lives under some `…/vendor/…` directory, and the catalog came out empty.
|
|
111
|
+
def skipped?(relative)
|
|
112
|
+
relative.split("/")[0...-1].intersect?(SKIPPED_DIRECTORIES)
|
|
84
113
|
end
|
|
85
114
|
|
|
86
115
|
# The glob's rows record which files match; this read's row carries an edit.
|
|
@@ -26,7 +26,7 @@ module Rigor
|
|
|
26
26
|
def extconf_uses_ffx?(root, io = nil)
|
|
27
27
|
return false if root.nil?
|
|
28
28
|
|
|
29
|
-
extconfs = io ? io.glob(root.to_s, EXTCONF_GLOB) : Dir.glob(File.join(root.to_s,
|
|
29
|
+
extconfs = io ? io.glob(root.to_s, EXTCONF_GLOB) : Dir.glob(EXTCONF_GLOB, base: root.to_s).map { |path| File.join(root.to_s, path) }
|
|
30
30
|
extconfs.any? do |extconf|
|
|
31
31
|
read_text(extconf, io).include?("FFX.create_makefile")
|
|
32
32
|
rescue SystemCallError
|
|
@@ -72,7 +72,7 @@ module Rigor
|
|
|
72
72
|
# ADR-45 WD1b (#613) — boundary-probed: a root that appears later invalidates the warm run.
|
|
73
73
|
next [] unless @io_boundary.directory?(absolute)
|
|
74
74
|
|
|
75
|
-
Dir.glob(File.join(absolute,
|
|
75
|
+
Dir.glob("**/*.rb", base: absolute).map { |path| File.join(absolute, path) }
|
|
76
76
|
end
|
|
77
77
|
end
|
|
78
78
|
|
|
@@ -60,7 +60,7 @@ module Rigor
|
|
|
60
60
|
# ADR-45 WD1b (#613) — boundary-probed: a root that appears later invalidates the warm run.
|
|
61
61
|
next [] unless @io_boundary.directory?(absolute)
|
|
62
62
|
|
|
63
|
-
Dir.glob(File.join(absolute,
|
|
63
|
+
Dir.glob("**/*.rb", base: absolute).map { |path| File.join(absolute, path) }
|
|
64
64
|
end
|
|
65
65
|
end
|
|
66
66
|
|
|
@@ -34,12 +34,17 @@ module Rigor
|
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
attr_reader :entries, :locales
|
|
37
|
+
attr_reader :entries, :locales, :load_errors
|
|
38
38
|
|
|
39
39
|
# @param locales — all locale names that contributed at least one key.
|
|
40
|
-
|
|
40
|
+
# @param load_errors — the {LocaleLoader::LoadError}s of the files that did not parse (#1954). They
|
|
41
|
+
# ride in the index, not beside it, because the index is the cached producer value: a warm run
|
|
42
|
+
# serves it without re-reading a single locale file, so an error kept anywhere else is lost exactly
|
|
43
|
+
# when the cache hits.
|
|
44
|
+
def initialize(entries, locales:, load_errors: [])
|
|
41
45
|
@entries = entries.freeze
|
|
42
46
|
@locales = locales.dup.freeze
|
|
47
|
+
@load_errors = load_errors.map(&:freeze).freeze
|
|
43
48
|
@by_key = entries.to_h { |e| [e.dotted_key, e] }.freeze
|
|
44
49
|
freeze
|
|
45
50
|
end
|
|
@@ -15,9 +15,9 @@ module Rigor
|
|
|
15
15
|
# flattened into dotted keys (`users.welcome`, `errors.messages.blank`, …). For each leaf string,
|
|
16
16
|
# `%{var}` placeholders are extracted via a simple regex.
|
|
17
17
|
#
|
|
18
|
-
# Files that fail to parse are skipped
|
|
19
|
-
# error
|
|
20
|
-
# locale-keyed by convention.
|
|
18
|
+
# Files that fail to parse are skipped and recorded in the returned index's `load_errors`, which the
|
|
19
|
+
# plugin discloses as a run-scoped load-error row. Non-Hash YAML roots (e.g. a top-level sequence) are
|
|
20
|
+
# also skipped — the format is locale-keyed by convention.
|
|
21
21
|
class LocaleLoader
|
|
22
22
|
PLACEHOLDER_RE = /%\{(?<name>[^}]+)\}/
|
|
23
23
|
|
|
@@ -66,7 +66,7 @@ module Rigor
|
|
|
66
66
|
value_kinds: per_key_kinds[dotted_key].freeze
|
|
67
67
|
)
|
|
68
68
|
end
|
|
69
|
-
LocaleIndex.new(entries, locales: locales.to_a.sort)
|
|
69
|
+
LocaleIndex.new(entries, locales: locales.to_a.sort, load_errors: @load_errors)
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
private
|
|
@@ -90,7 +90,7 @@ module Rigor
|
|
|
90
90
|
# ADR-45 WD1b (#613) — boundary-probed: a root that appears later invalidates the warm run.
|
|
91
91
|
next [] unless @io_boundary.directory?(absolute)
|
|
92
92
|
|
|
93
|
-
Dir.glob(File.join(absolute,
|
|
93
|
+
Dir.glob("**/*.{yml,yaml}", base: absolute).map { |path| File.join(absolute, path) }
|
|
94
94
|
end.sort
|
|
95
95
|
end
|
|
96
96
|
|
|
@@ -57,7 +57,11 @@ module Rigor
|
|
|
57
57
|
target_gems: ["i18n"],
|
|
58
58
|
# Bumped 2026-06-23 — view template lazy-key scanning (`t('.key')` inside ERB / Haml / Slim under
|
|
59
59
|
# `view_search_paths`).
|
|
60
|
-
|
|
60
|
+
# Bumped 2026-10-17 (#1954) — the cached `:locale_index` value now carries the YAML load errors. A
|
|
61
|
+
# belt-and-braces key change: a released build already drops pre-change entries with the cache ABI
|
|
62
|
+
# (`Rigor::VERSION`) and a checkout with its engine-source identity, and the read below tolerates
|
|
63
|
+
# an index without errors either way.
|
|
64
|
+
version: "0.3.1",
|
|
61
65
|
description: "Validates I18n `t(key)` calls against `config/locales/*.yml`.",
|
|
62
66
|
config_schema: {
|
|
63
67
|
"locale_search_paths" => { kind: :array, default: ["config/locales"] },
|
|
@@ -72,17 +76,12 @@ module Rigor
|
|
|
72
76
|
)
|
|
73
77
|
|
|
74
78
|
# `watch:` covers every `.yml` / `.yaml` file under the locale search paths so the cache invalidates
|
|
75
|
-
# when locale files are added, removed, or edited (ADR-60 WD3).
|
|
76
|
-
#
|
|
77
|
-
#
|
|
79
|
+
# when locale files are added, removed, or edited (ADR-60 WD3). The YAML load errors travel inside the
|
|
80
|
+
# returned index (`LocaleIndex#load_errors`), not in an instance variable the block sets (#1954): a
|
|
81
|
+
# warm producer-cache hit skips the block, so a block-side capture lost the load-error row on every
|
|
82
|
+
# run whose result was not itself served from the run-result cache.
|
|
78
83
|
producer :locale_index, watch: -> { [[@locale_search_paths, "**/*.yml", "**/*.yaml"]] } do |_params|
|
|
79
|
-
|
|
80
|
-
io_boundary: io_boundary,
|
|
81
|
-
search_paths: @locale_search_paths
|
|
82
|
-
)
|
|
83
|
-
index = loader.load
|
|
84
|
-
@load_errors = loader.load_errors
|
|
85
|
-
index
|
|
84
|
+
LocaleLoader.new(io_boundary: io_boundary, search_paths: @locale_search_paths).load
|
|
86
85
|
end
|
|
87
86
|
|
|
88
87
|
# Scans view templates under `view_search_paths` for lazy `t('.key')` / `I18n.translate('.key')`
|
|
@@ -102,7 +101,6 @@ module Rigor
|
|
|
102
101
|
@locale_search_paths = Array(config.fetch("locale_search_paths")).map(&:to_s)
|
|
103
102
|
@view_search_paths = Array(config.fetch("view_search_paths")).map(&:to_s)
|
|
104
103
|
@configured_locales = Array(config.fetch("configured_locales")).map(&:to_s)
|
|
105
|
-
@load_errors = []
|
|
106
104
|
end
|
|
107
105
|
|
|
108
106
|
# Returns no rows of its own: every file-level outcome here is run-scoped. The YAML load errors and the
|
|
@@ -114,8 +112,11 @@ module Rigor
|
|
|
114
112
|
# are lazily loaded + memoised by `producer_value`, so the repeat registrations are no-ops.
|
|
115
113
|
def diagnostics_for_file(path:, scope:, root:) # rubocop:disable Lint/UnusedMethodArgument
|
|
116
114
|
index = producer_value(:locale_index)
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
if index
|
|
116
|
+
disclose_load_errors(Array(index.load_errors))
|
|
117
|
+
elsif producer_error(:locale_index)
|
|
118
|
+
disclose_locale_runtime_error
|
|
119
|
+
end
|
|
119
120
|
view_diags = producer_value(:view_diagnostics) || []
|
|
120
121
|
if (view_err = producer_error(:view_diagnostics))
|
|
121
122
|
disclose_view_runtime_error(view_err)
|
|
@@ -175,7 +176,7 @@ module Rigor
|
|
|
175
176
|
# ADR-45 WD1b (#613) — boundary-probed: a root that appears later invalidates the warm run.
|
|
176
177
|
next [] unless io_boundary.directory?(absolute)
|
|
177
178
|
|
|
178
|
-
Dir.glob(
|
|
179
|
+
Dir.glob("**/*.{erb,haml,slim}", base: absolute).map { |path| File.join(absolute, path) }
|
|
179
180
|
end.sort
|
|
180
181
|
end
|
|
181
182
|
|
|
@@ -209,8 +210,8 @@ module Rigor
|
|
|
209
210
|
# be pinned to the first analysed Ruby file — once per plugin INSTANCE, so once per fork-pool worker
|
|
210
211
|
# (#1051). They go through the run-scoped channel instead: keyed by the locale path that failed, so a
|
|
211
212
|
# project with two broken YAML files still discloses both, each exactly once, at `.rigor.yml:1:1`.
|
|
212
|
-
def disclose_load_errors
|
|
213
|
-
|
|
213
|
+
def disclose_load_errors(load_errors)
|
|
214
|
+
load_errors.each do |err|
|
|
214
215
|
disclose_once(
|
|
215
216
|
"locale-parse:#{err.path}",
|
|
216
217
|
message: "rigor-rails-i18n: failed to parse `#{err.path}`: #{err.message}",
|
|
@@ -168,7 +168,7 @@ module Rigor
|
|
|
168
168
|
# ADR-45 WD1b (#613) — boundary-probed: a root that appears later invalidates the warm run.
|
|
169
169
|
next unless io_boundary.directory?(absolute)
|
|
170
170
|
|
|
171
|
-
Dir.glob(File.join(absolute,
|
|
171
|
+
Dir.glob("**/*.rb", base: absolute).map { |path| File.join(absolute, path) }.each(&)
|
|
172
172
|
end
|
|
173
173
|
end
|
|
174
174
|
|
|
@@ -92,7 +92,7 @@ module Rigor
|
|
|
92
92
|
# ADR-45 WD1b (#613) — boundary-probed: a root that appears later invalidates the warm run.
|
|
93
93
|
next [] unless @io_boundary.directory?(absolute)
|
|
94
94
|
|
|
95
|
-
Dir.glob(File.join(absolute,
|
|
95
|
+
Dir.glob("**/*.rb", base: absolute).map { |path| File.join(absolute, path) }
|
|
96
96
|
end
|
|
97
97
|
end
|
|
98
98
|
|
|
@@ -29,7 +29,9 @@ module Rigor
|
|
|
29
29
|
# ## Reach
|
|
30
30
|
#
|
|
31
31
|
# All nine class-level HTTP verb methods Sinatra exposes: `get`, `post`, `put`, `delete`, `head`,
|
|
32
|
-
# `options`, `patch`, `link`, `unlink` (`lib/sinatra/base.rb:1531-1553`)
|
|
32
|
+
# `options`, `patch`, `link`, `unlink` (`lib/sinatra/base.rb:1531-1553`), plus the `before` / `after`
|
|
33
|
+
# filters (`add_filter`) and the `error` / `not_found` handlers, whose blocks `compile!` turns into
|
|
34
|
+
# instance methods through the same `generate_method` (issue #1768). Both modular-style subclasses
|
|
33
35
|
# of `Sinatra::Base` and `Sinatra::Application` (classic top-level style, when used via `class App <
|
|
34
36
|
# Sinatra::Application`) match because the receiver constraint accepts every subclass.
|
|
35
37
|
#
|
|
@@ -57,7 +59,7 @@ module Rigor
|
|
|
57
59
|
block_as_methods: [
|
|
58
60
|
Rigor::Plugin::Macro::BlockAsMethod.new(
|
|
59
61
|
receiver_constraint: "Sinatra::Base",
|
|
60
|
-
method_names: %i[get post put delete head options patch link unlink]
|
|
62
|
+
method_names: %i[get post put delete head options patch link unlink before after error not_found]
|
|
61
63
|
)
|
|
62
64
|
]
|
|
63
65
|
)
|
|
@@ -504,8 +504,8 @@ module Rigor
|
|
|
504
504
|
# must not vary by machine / run. `Dir.glob` sorts its results by default on Ruby 3.0+, so the fold
|
|
505
505
|
# order is already stable (the slice-1 comment's "filesystem order" caveat predates that default);
|
|
506
506
|
# this walk relies on it rather than re-sorting.
|
|
507
|
-
Dir.glob(
|
|
508
|
-
harvest_file(canonicalize(
|
|
507
|
+
Dir.glob("**/*#{ext}", base: absolute).each do |relative|
|
|
508
|
+
harvest_file(canonicalize(File.join(absolute, relative)), catalog, sigil_by_path, parse_errors_by_path)
|
|
509
509
|
end
|
|
510
510
|
end
|
|
511
511
|
elsif io_boundary.file?(absolute) && extensions.any? { |ext| absolute.end_with?(ext) }
|
|
@@ -63,7 +63,7 @@ module Rigor
|
|
|
63
63
|
@project_paths.flat_map do |entry|
|
|
64
64
|
absolute = File.expand_path(entry)
|
|
65
65
|
if @io_boundary.directory?(absolute)
|
|
66
|
-
Dir.glob(File.join(absolute,
|
|
66
|
+
Dir.glob("**/*.rb", base: absolute).map { |path| File.join(absolute, path) }
|
|
67
67
|
elsif absolute.end_with?(".rb") && @io_boundary.file?(absolute)
|
|
68
68
|
[absolute]
|
|
69
69
|
else
|
|
@@ -78,7 +78,7 @@ module Rigor
|
|
|
78
78
|
# ADR-45 WD1b (#613) — boundary-probed: a root that appears later invalidates the warm run.
|
|
79
79
|
next [] unless @io_boundary.directory?(absolute)
|
|
80
80
|
|
|
81
|
-
Dir.glob(File.join(absolute,
|
|
81
|
+
Dir.glob("**/*.rb", base: absolute).map { |path| File.join(absolute, path) }
|
|
82
82
|
end
|
|
83
83
|
end
|
|
84
84
|
end
|
|
@@ -4,11 +4,14 @@ class Rigor::Plugin::IoBoundary
|
|
|
4
4
|
def file?: (untyped) -> bool
|
|
5
5
|
def directory?: (untyped) -> bool
|
|
6
6
|
def list_directory: (untyped) -> Array[String]
|
|
7
|
+
def list_directory_kinds: (untyped) -> Hash[String, untyped]
|
|
7
8
|
# Records a `:names` glob row (membership only); a caller needing a match's content `read_file`s it. A `..`
|
|
8
9
|
# segment, a brace or a backslash in `pattern` raises `AccessDeniedError`.
|
|
9
10
|
def glob: (untyped root, untyped pattern) -> Array[String]
|
|
10
|
-
# sig-gen gap: #
|
|
11
|
-
#
|
|
11
|
+
# sig-gen gap: #1154 — the proposal leaves both string fields untyped. `nearest_root` comes from `@policy`, an
|
|
12
|
+
# ivar assigned from an `initialize` parameter (the #1154 shape); `first_path` comes from `@first_refused_path`,
|
|
13
|
+
# written from `record_refusal(path)`'s parameter outside `initialize`, a case #1154 as titled does not yet cover.
|
|
14
|
+
# The record's fields are the running count and the first refused path, nil when nothing was refused.
|
|
12
15
|
def refusal_summary: () -> { count: Integer, first_path: String?, nearest_root: String? }?
|
|
13
16
|
def open_url: (untyped url) -> String
|
|
14
17
|
# Returns a frozen `Rigor::Cache::Descriptor` (no RBS yet -> untyped).
|
data/sig/rigor/scope.rbs
CHANGED
|
@@ -61,7 +61,7 @@ module Rigor
|
|
|
61
61
|
def discovered_singleton_def_nodes: () -> Hash[String, Hash[Symbol, untyped]]
|
|
62
62
|
def discovered_def_sources: () -> Hash[String, Hash[Symbol, String]]
|
|
63
63
|
def discovered_singleton_def_sources: () -> Hash[String, Hash[Symbol, String]]
|
|
64
|
-
def discovered_method_visibilities: () -> Hash[String, Hash[Symbol, Symbol]]
|
|
64
|
+
def discovered_method_visibilities: () -> Hash[String, Hash[Symbol | [:singleton, Symbol], Symbol]]
|
|
65
65
|
# sig-gen gap: #1148 — sig-gen skips an endless-def reader (untyped return); the row is hand-written.
|
|
66
66
|
def discovered_parameter_envelopes: () -> Hash[String, Hash[untyped, untyped]]
|
|
67
67
|
# sig-gen gap: #1149 — sig-gen skips the `||` fallback reader (untyped return); the row is hand-written.
|
|
@@ -128,7 +128,7 @@ module Rigor
|
|
|
128
128
|
attr_reader discovered_singleton_def_nodes: Hash[String, Hash[Symbol, untyped]]
|
|
129
129
|
attr_reader discovered_def_sources: Hash[String, Hash[Symbol, String]]
|
|
130
130
|
attr_reader discovered_singleton_def_sources: Hash[String, Hash[Symbol, String]]
|
|
131
|
-
attr_reader discovered_method_visibilities: Hash[String, Hash[Symbol, Symbol]]
|
|
131
|
+
attr_reader discovered_method_visibilities: Hash[String, Hash[Symbol | [:singleton, Symbol], Symbol]]
|
|
132
132
|
# sig-gen gap: #1150 — `Data.define` members carry no type, as for every other row in this class.
|
|
133
133
|
attr_reader discovered_parameter_envelopes: Hash[String, Hash[untyped, untyped]]
|
|
134
134
|
attr_reader discovered_superclasses: Hash[String, String]
|
|
@@ -201,7 +201,7 @@ module Rigor
|
|
|
201
201
|
# authored: ADR-119 WD1 sibling pairing; no inferred signature exists for it (#1150).
|
|
202
202
|
def sibling_tables: () -> Hash[Symbol, untyped]
|
|
203
203
|
|
|
204
|
-
def with: (?declared_types: Hash[untyped, Type::t], ?class_ivars: Hash[String, Hash[Symbol, Type::t]], ?class_cvars: Hash[String, Hash[Symbol, Type::t]], ?program_globals: Hash[Symbol, Type::t], ?program_global_seeds: Hash[Symbol, Type::t], ?discovered_classes: Hash[String, Type::Singleton], ?in_source_constants: Hash[String, Type::t], ?discovered_methods: Hash[String, Hash[Symbol, Symbol]], ?discovered_def_nodes: Hash[String, Hash[Symbol, untyped]], ?discovered_def_nestings: Hash[untyped, Array[String]], ?discovered_singleton_def_nodes: Hash[String, Hash[Symbol, untyped]], ?discovered_def_sources: Hash[String, Hash[Symbol, String]], ?discovered_singleton_def_sources: Hash[String, Hash[Symbol, String]], ?discovered_method_visibilities: Hash[String, Hash[Symbol, Symbol]], ?discovered_parameter_envelopes: Hash[String, Hash[untyped, untyped]], ?discovered_superclasses: Hash[String, String], ?discovered_includes: Hash[String, Array[String]], ?discovered_prepends: Hash[String, Array[String]], ?discovered_extends: Hash[String, Array[String]], ?discovered_class_sources: Hash[String, Set[String]], ?constant_sources: Hash[String, Set[String]], ?constant_writers: Hash[String, Array[String]], ?constant_shadowers: Hash[String, Array[String]], ?published_constant_names: Set[String], ?local_constant_names: Set[String], ?published_constant_alias_names: Set[String], ?published_constant_ivars: Hash[String, Set[Symbol]], ?data_member_layouts: Hash[String, Array[Symbol]], ?struct_member_layouts: Hash[String, { members: Array[Symbol], keyword_init: bool }], ?param_inferred_types: Hash[[String, Symbol, Symbol], Hash[Symbol, Type::t]], ?discovered_deferred_ranges: Hash[String, Array[[Integer, Integer, Symbol?, Symbol?, String?]]], ?discovered_refinements: Hash[String, Hash[Symbol | String, Array[String]]], ?discovered_global_write_census: Set[Array[untyped]], ?run_generation: Object?, ?patched_line_readers: Set[Symbol], ?clears_last_status: bool, ?defines_case_equality: bool, ?implicit_self_evidence: untyped, ?in_effect_refinements: untyped, ?possible_discovered_methods: Hash[String, Hash[Symbol, Symbol]], ?possible_discovered_deferred_ranges: Hash[String, Array[[Integer, Integer, Symbol?, Symbol?, String?]]], ?contested_discovered_def_nodes: Set[Array[untyped]], ?contested_discovered_singleton_def_nodes: Set[Array[untyped]], ?contested_discovered_method_visibilities: Set[Array[untyped]], ?contested_discovered_parameter_envelopes: Set[Array[untyped]]) -> DiscoveryIndex
|
|
204
|
+
def with: (?declared_types: Hash[untyped, Type::t], ?class_ivars: Hash[String, Hash[Symbol, Type::t]], ?class_cvars: Hash[String, Hash[Symbol, Type::t]], ?program_globals: Hash[Symbol, Type::t], ?program_global_seeds: Hash[Symbol, Type::t], ?discovered_classes: Hash[String, Type::Singleton], ?in_source_constants: Hash[String, Type::t], ?discovered_methods: Hash[String, Hash[Symbol, Symbol]], ?discovered_def_nodes: Hash[String, Hash[Symbol, untyped]], ?discovered_def_nestings: Hash[untyped, Array[String]], ?discovered_singleton_def_nodes: Hash[String, Hash[Symbol, untyped]], ?discovered_def_sources: Hash[String, Hash[Symbol, String]], ?discovered_singleton_def_sources: Hash[String, Hash[Symbol, String]], ?discovered_method_visibilities: Hash[String, Hash[Symbol | [:singleton, Symbol], Symbol]], ?discovered_parameter_envelopes: Hash[String, Hash[untyped, untyped]], ?discovered_superclasses: Hash[String, String], ?discovered_includes: Hash[String, Array[String]], ?discovered_prepends: Hash[String, Array[String]], ?discovered_extends: Hash[String, Array[String]], ?discovered_class_sources: Hash[String, Set[String]], ?constant_sources: Hash[String, Set[String]], ?constant_writers: Hash[String, Array[String]], ?constant_shadowers: Hash[String, Array[String]], ?published_constant_names: Set[String], ?local_constant_names: Set[String], ?published_constant_alias_names: Set[String], ?published_constant_ivars: Hash[String, Set[Symbol]], ?data_member_layouts: Hash[String, Array[Symbol]], ?struct_member_layouts: Hash[String, { members: Array[Symbol], keyword_init: bool }], ?param_inferred_types: Hash[[String, Symbol, Symbol], Hash[Symbol, Type::t]], ?discovered_deferred_ranges: Hash[String, Array[[Integer, Integer, Symbol?, Symbol?, String?]]], ?discovered_refinements: Hash[String, Hash[Symbol | String, Array[String]]], ?discovered_global_write_census: Set[Array[untyped]], ?run_generation: Object?, ?patched_line_readers: Set[Symbol], ?clears_last_status: bool, ?defines_case_equality: bool, ?implicit_self_evidence: untyped, ?in_effect_refinements: untyped, ?possible_discovered_methods: Hash[String, Hash[Symbol, Symbol]], ?possible_discovered_deferred_ranges: Hash[String, Array[[Integer, Integer, Symbol?, Symbol?, String?]]], ?contested_discovered_def_nodes: Set[Array[untyped]], ?contested_discovered_singleton_def_nodes: Set[Array[untyped]], ?contested_discovered_method_visibilities: Set[Array[untyped]], ?contested_discovered_parameter_envelopes: Set[Array[untyped]]) -> DiscoveryIndex
|
|
205
205
|
end
|
|
206
206
|
|
|
207
207
|
class IndexedKey
|
|
@@ -338,6 +338,7 @@ module Rigor
|
|
|
338
338
|
# Issue #530 — public so the external-gem ancestry probe asks the same question the walk does.
|
|
339
339
|
def known_user_class?: (String | Symbol name) -> bool
|
|
340
340
|
def singleton_def_for: (String | Symbol class_name, String | Symbol method_name) -> ::Prism::DefNode?
|
|
341
|
+
def module_function_def?: (untyped, untyped) -> (false | true)
|
|
341
342
|
def singleton_def_through_ancestors: (String | Symbol class_name, String | Symbol method_name, ?name_memo: Hash[untyped, untyped]) -> ([::Prism::DefNode, String] | [nil, nil])
|
|
342
343
|
def user_def_site_for: (String | Symbol class_name, String | Symbol method_name) -> String?
|
|
343
344
|
def user_singleton_def_site_for: (String | Symbol class_name, String | Symbol method_name) -> String?
|