rigortype 0.3.6 → 0.3.7
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 +1 -1
- data/data/builtins/ruby_core/date.yml +6 -3
- data/data/builtins/ruby_core/enumerable.yml +1 -0
- data/data/builtins/ruby_core/exception.yml +2 -1
- data/data/builtins/ruby_core/file.yml +28 -14
- data/data/builtins/ruby_core/hash.yml +5 -2
- data/data/builtins/ruby_core/io.yml +33 -16
- data/data/builtins/ruby_core/random.yml +2 -1
- data/data/builtins/ruby_core/re.yml +6 -3
- data/data/builtins/ruby_core/struct.yml +2 -0
- data/data/builtins/ruby_core/time.yml +2 -1
- data/data/gem_overlay/activesupport/core_ext.rbs +409 -11
- data/docs/manual/02-cli-reference.md +16 -7
- data/docs/manual/05-inspecting-types.md +20 -8
- data/docs/manual/07-plugins.md +4 -0
- data/docs/manual/plugins/rigor-actionpack.md +61 -0
- data/docs/manual/plugins/rigor-activerecord.md +74 -1
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +114 -12
- data/docs/manual/plugins/rigor-sidekiq.md +22 -0
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +12 -0
- data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +98 -0
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +3 -3
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +199 -0
- data/lib/rigor/analysis/check_rules/rule_walk.rb +1 -2
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +2 -2
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +6 -12
- data/lib/rigor/analysis/check_rules.rb +383 -48
- data/lib/rigor/analysis/crash_signature.rb +116 -0
- data/lib/rigor/analysis/dependency_recorder.rb +18 -3
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +2 -1
- data/lib/rigor/analysis/incremental.rb +28 -0
- data/lib/rigor/analysis/incremental_session.rb +44 -9
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +1 -2
- data/lib/rigor/analysis/reachability/graph.rb +14 -5
- data/lib/rigor/analysis/reachability/scan.rb +7 -3
- data/lib/rigor/analysis/result.rb +32 -0
- data/lib/rigor/analysis/run_cache_key.rb +41 -1
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +93 -1
- data/lib/rigor/analysis/runner/pool_coordinator.rb +34 -0
- data/lib/rigor/analysis/runner/project_pre_passes.rb +15 -11
- data/lib/rigor/analysis/runner/run_snapshots.rb +7 -2
- data/lib/rigor/analysis/runner.rb +147 -10
- data/lib/rigor/analysis/worker_session.rb +10 -1
- data/lib/rigor/bleeding_edge.rb +7 -6
- data/lib/rigor/builtins/hkt_builtins.rb +45 -6
- data/lib/rigor/builtins/predefined_constant_refinements.rb +108 -59
- data/lib/rigor/builtins/static_return_refinements.rb +20 -1
- data/lib/rigor/cache/descriptor.rb +42 -2
- data/lib/rigor/cache/incremental_snapshot.rb +40 -3
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +31 -8
- data/lib/rigor/cache/store.rb +10 -1
- data/lib/rigor/cli/coverage_command.rb +23 -18
- data/lib/rigor/cli/coverage_mutation.rb +13 -17
- data/lib/rigor/cli/coverage_scan.rb +47 -6
- data/lib/rigor/cli/fused_protection_renderer.rb +19 -2
- data/lib/rigor/cli/fused_protection_report.rb +23 -2
- data/lib/rigor/cli/measurement_integrity_warning.rb +57 -0
- data/lib/rigor/cli/mutation_protection_renderer.rb +22 -2
- data/lib/rigor/cli/mutation_protection_report.rb +31 -2
- data/lib/rigor/cli/sig_gen_command.rb +18 -0
- data/lib/rigor/cli/type_of_command.rb +188 -41
- data/lib/rigor/cli/type_of_renderer.rb +72 -3
- data/lib/rigor/cli/type_scan_command.rb +10 -1
- data/lib/rigor/cli.rb +1 -1
- data/lib/rigor/configuration.rb +7 -4
- data/lib/rigor/effects/plugin_facts.rb +1 -1
- data/lib/rigor/effects/scanner.rb +2 -3
- data/lib/rigor/environment/bundle_sig_discovery.rb +64 -13
- data/lib/rigor/environment/lockfile_resolver.rb +15 -5
- data/lib/rigor/environment/missing_gem_constant_index.rb +45 -4
- data/lib/rigor/environment/rbs_hierarchy.rb +16 -22
- data/lib/rigor/environment/rbs_loader.rb +525 -46
- data/lib/rigor/environment.rb +38 -15
- data/lib/rigor/inference/acceptance.rb +172 -20
- data/lib/rigor/inference/captured_locals.rb +62 -0
- data/lib/rigor/inference/content_join.rb +347 -0
- data/lib/rigor/inference/def_handle.rb +15 -3
- data/lib/rigor/inference/def_node_resolver.rb +62 -3
- data/lib/rigor/inference/expression_typer.rb +1020 -141
- data/lib/rigor/inference/fork_map.rb +6 -1
- data/lib/rigor/inference/hkt_reducer.rb +12 -1
- data/lib/rigor/inference/hkt_registry.rb +46 -0
- data/lib/rigor/inference/hkt_sugar_translator.rb +93 -0
- data/lib/rigor/inference/index_write_widening.rb +48 -0
- data/lib/rigor/inference/indexed_narrowing.rb +14 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +7 -5
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +10 -0
- data/lib/rigor/inference/method_dispatcher/json_folding.rb +58 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +81 -38
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +54 -17
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +63 -0
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +63 -10
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +104 -0
- data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +81 -0
- data/lib/rigor/inference/method_dispatcher.rb +155 -14
- data/lib/rigor/inference/method_parameter_binder.rb +3 -3
- data/lib/rigor/inference/mutation_widening.rb +244 -174
- data/lib/rigor/inference/narrowing.rb +331 -27
- data/lib/rigor/inference/parameter_inference_collector.rb +1 -0
- data/lib/rigor/inference/pre_eval_constants.rb +15 -0
- data/lib/rigor/inference/precision_scanner.rb +5 -2
- data/lib/rigor/inference/project_patched_scanner.rb +2 -1
- data/lib/rigor/inference/rbs_type_translator.rb +89 -39
- data/lib/rigor/inference/scope_indexer.rb +1493 -140
- data/lib/rigor/inference/statement_evaluator.rb +276 -91
- data/lib/rigor/inference/struct_fold_safety.rb +186 -27
- data/lib/rigor/inference/version_guard.rb +229 -0
- data/lib/rigor/language_server/selection_range_provider.rb +1 -1
- data/lib/rigor/plugin/base.rb +3 -2
- data/lib/rigor/plugin/inflector.rb +14 -5
- data/lib/rigor/plugin/io_boundary.rb +107 -4
- data/lib/rigor/plugin/loader.rb +14 -0
- data/lib/rigor/protection/analysis_guard.rb +60 -0
- data/lib/rigor/protection/closure_kill_oracle.rb +43 -38
- data/lib/rigor/protection/diagnostic_oracle.rb +12 -4
- data/lib/rigor/protection/discovery_seed.rb +1 -1
- data/lib/rigor/protection/measurement_integrity.rb +36 -0
- data/lib/rigor/protection/mutation_scanner.rb +61 -6
- data/lib/rigor/protection/mutator.rb +121 -18
- data/lib/rigor/reflection.rb +182 -30
- data/lib/rigor/scope/discovery_index.rb +39 -1
- data/lib/rigor/scope.rb +385 -4
- data/lib/rigor/sig_gen/classification.rb +12 -1
- data/lib/rigor/sig_gen/generator.rb +254 -5
- data/lib/rigor/sig_gen/renderer.rb +1 -6
- data/lib/rigor/sig_gen/writer.rb +3 -0
- data/lib/rigor/source/constant_path.rb +79 -2
- data/lib/rigor/triage/catalogue.rb +1 -1
- data/lib/rigor/type/combinator.rb +10 -0
- data/lib/rigor/type/maybe.rb +47 -0
- data/lib/rigor/type/refined.rb +1 -2
- data/lib/rigor/type/result.rb +53 -0
- data/lib/rigor/type.rb +2 -0
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +3 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +58 -8
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +15 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +4 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +3 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +39 -14
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +36 -3
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -2
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +21 -5
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +47 -8
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +13 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +242 -4
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +3 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +7 -3
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +62 -11
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +15 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +3 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +5 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +798 -22
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +156 -13
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +140 -40
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +2 -2
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +20 -6
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +14 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +6 -3
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +23 -6
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +264 -9
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +876 -23
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +99 -0
- data/plugins/rigor-dry-monads/lib/rigor-dry-monads.rb +5 -0
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +34 -0
- data/plugins/rigor-ethon/lib/rigor-ethon.rb +3 -0
- data/plugins/rigor-ethon/sig/ethon.rbs +27 -0
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +3 -2
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +198 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/binding_recognizer.rb +75 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/catalog.rb +64 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +173 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/target_detector.rb +46 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +161 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +121 -0
- data/plugins/rigor-ffi/lib/rigor-ffi.rb +3 -0
- data/plugins/rigor-ffi/sig/ffi.rbs +67 -0
- data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +19 -0
- data/plugins/rigor-ffi-rzmq/lib/rigor-ffi-rzmq.rb +3 -0
- data/plugins/rigor-ffi-rzmq/sig/ffi_rzmq.rbs +29 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +2 -4
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +2 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +2 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +2 -1
- data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +156 -11
- data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +30 -0
- data/plugins/rigor-rbnacl/lib/rigor-rbnacl.rb +3 -0
- data/plugins/rigor-rbnacl/sig/rbnacl.rbs +22 -0
- data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +55 -0
- data/plugins/rigor-sassc/lib/rigor-sassc.rb +3 -0
- data/plugins/rigor-sassc/sig/sassc.rbs +24 -0
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +32 -11
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +3 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +4 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +63 -10
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +15 -2
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +61 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +4 -2
- data/sig/rigor/analysis/baseline.rbs +1 -1
- data/sig/rigor/environment.rbs +3 -0
- data/sig/rigor/inference.rbs +4 -1
- data/sig/rigor/plugin/io_boundary.rbs +2 -0
- data/sig/rigor/reflection.rbs +3 -1
- data/sig/rigor/scope.rbs +25 -1
- data/sig/rigor/type.rbs +95 -1
- data/sig/rigor.rbs +16 -0
- metadata +69 -3
|
@@ -126,6 +126,10 @@ module Rigor
|
|
|
126
126
|
snapshot_project_signature_state(environment)
|
|
127
127
|
snapshot_effect_annotation_carrier(environment&.rbs_loader)
|
|
128
128
|
result = files.flat_map { |path| @analyze_file.call(path, environment) }
|
|
129
|
+
# Issue #696 — AFTER the loop, deliberately. Definition builds are lazy (ADR-54 WD1: per class, on
|
|
130
|
+
# first demand), so a snapshot taken beside the ones above — which run BEFORE `files.flat_map` —
|
|
131
|
+
# would read an empty list on every run, including the ones this diagnostic exists for.
|
|
132
|
+
record_definition_build_failures(environment&.rbs_loader&.definition_build_failures)
|
|
129
133
|
if @collect_stats
|
|
130
134
|
loader = environment.rbs_loader
|
|
131
135
|
@snapshots.class_decl_paths = loader&.class_decl_paths || {}.freeze
|
|
@@ -567,6 +571,11 @@ module Rigor
|
|
|
567
571
|
snapshot_effect_annotation_carrier(environment.rbs_loader)
|
|
568
572
|
diagnostics = files.flat_map { |path| @analyze_file.call(path, environment) }
|
|
569
573
|
loader = environment.rbs_loader
|
|
574
|
+
# Issue #696 — this path analyses on the coordinator, so its loader IS the one that reached the
|
|
575
|
+
# failing build, and it must snapshot the same slot the two real paths do. Reached whenever
|
|
576
|
+
# `fork` is unavailable (Windows) and on `--incremental` / effects runs without it: a run that
|
|
577
|
+
# degraded to sequential must not also report less than a sequential run would.
|
|
578
|
+
record_definition_build_failures(loader&.definition_build_failures)
|
|
570
579
|
@snapshots.class_decl_paths = loader&.class_decl_paths || {}.freeze
|
|
571
580
|
@snapshots.signature_paths = loader&.signature_paths || [].freeze
|
|
572
581
|
@snapshots.quarantined_signatures =
|
|
@@ -608,10 +617,35 @@ module Rigor
|
|
|
608
617
|
plugin_id: entry.plugin_id, path: entry.path, message: entry.message
|
|
609
618
|
)
|
|
610
619
|
end
|
|
620
|
+
# Issue #696. Fetched with a default so an older drain stays compatible, exactly as the line above.
|
|
621
|
+
record_definition_build_failures(drained[:definition_build_failures])
|
|
611
622
|
end
|
|
612
623
|
|
|
613
624
|
private
|
|
614
625
|
|
|
626
|
+
# Issue #696 — accumulate the per-class `RBS::DefinitionBuilder` failures this run observed, deduped
|
|
627
|
+
# by class name and kept in first-seen order.
|
|
628
|
+
#
|
|
629
|
+
# Accumulating rather than assigning is what makes a pooled run say what a sequential one says: each
|
|
630
|
+
# worker holds its OWN loader and its own memo (the `@state` that also gates the stderr banner, which
|
|
631
|
+
# is why the banner repeats per worker — #295), so a class that fails in two workers arrives twice
|
|
632
|
+
# and a class only one worker touched arrives once. The union over the workers is the set the RUN
|
|
633
|
+
# hit; neither worker alone is.
|
|
634
|
+
#
|
|
635
|
+
# NOT gated on `project_signature_paths?`, unlike {#snapshot_project_signature_state}. That gate
|
|
636
|
+
# exists because each condition there forces the otherwise-lazy env build, which would warm the cache
|
|
637
|
+
# on a project with no sig set of its own. Nothing is forced here — this reads what the analysis
|
|
638
|
+
# already demanded — and the failure is not a project-sig-only condition anyway: two BUNDLED sources
|
|
639
|
+
# declaring the same method collapse a class with no `signature_paths:` in sight (the
|
|
640
|
+
# `bigdecimal` / `BigMath` shape, #299). Gating it would suppress exactly the variant the user cannot
|
|
641
|
+
# fix by editing their own `sig/`.
|
|
642
|
+
def record_definition_build_failures(failures)
|
|
643
|
+
return if failures.nil? || failures.empty?
|
|
644
|
+
|
|
645
|
+
@snapshots.definition_build_failures =
|
|
646
|
+
(@snapshots.definition_build_failures + failures).uniq(&:first).freeze
|
|
647
|
+
end
|
|
648
|
+
|
|
615
649
|
# True when the project declares its own `signature_paths:` (the only place the
|
|
616
650
|
# qualified-name-without-namespace mistake lives).
|
|
617
651
|
def project_signature_paths?
|
|
@@ -53,17 +53,11 @@ module Rigor
|
|
|
53
53
|
# consumes them and so never pays the double parse (see {#discover} + `Runner#ensure_project_discovery`).
|
|
54
54
|
# The slot names mirror the discovery half of {Result} exactly.
|
|
55
55
|
Discovery = Data.define(
|
|
56
|
-
:discovered_classes,
|
|
57
|
-
:
|
|
58
|
-
:
|
|
59
|
-
:
|
|
60
|
-
:
|
|
61
|
-
:discovered_superclasses,
|
|
62
|
-
:discovered_includes,
|
|
63
|
-
:discovered_class_sources,
|
|
64
|
-
:discovered_method_visibilities,
|
|
65
|
-
:discovered_methods,
|
|
66
|
-
:data_member_layouts,
|
|
56
|
+
:discovered_classes, :discovered_def_nodes, :discovered_def_nestings,
|
|
57
|
+
:discovered_singleton_def_nodes, :discovered_def_sources, :discovered_singleton_def_sources,
|
|
58
|
+
:discovered_superclasses, :discovered_header_nestings, :discovered_includes,
|
|
59
|
+
:discovered_class_sources, :constant_values, :constant_sources, :constant_writes,
|
|
60
|
+
:discovered_method_visibilities, :discovered_methods, :data_member_layouts,
|
|
67
61
|
:struct_member_layouts
|
|
68
62
|
)
|
|
69
63
|
|
|
@@ -156,12 +150,22 @@ module Rigor
|
|
|
156
150
|
Discovery.new(
|
|
157
151
|
discovered_classes: index.fetch(:classes),
|
|
158
152
|
discovered_def_nodes: def_index.fetch(:def_nodes),
|
|
153
|
+
# Issue #681 — the recorded `Module.nesting` per declared `def`, so a callee's body re-walk
|
|
154
|
+
# resolves its constants the way the declaration that owns it does.
|
|
155
|
+
discovered_def_nestings: def_index.fetch(:def_nestings),
|
|
159
156
|
discovered_singleton_def_nodes: def_index.fetch(:singleton_def_nodes),
|
|
160
157
|
discovered_def_sources: def_index.fetch(:def_sources),
|
|
161
158
|
discovered_singleton_def_sources: def_index.fetch(:singleton_def_sources),
|
|
162
159
|
discovered_superclasses: def_index.fetch(:superclasses),
|
|
160
|
+
# Issue #682 — the nesting each declaration header is written in, so an ancestor name resolves in
|
|
161
|
+
# the cref Ruby resolves it in rather than by peeling the subclass's own qualified name.
|
|
162
|
+
discovered_header_nestings: def_index.fetch(:header_nestings),
|
|
163
163
|
discovered_includes: def_index.fetch(:includes),
|
|
164
164
|
discovered_class_sources: def_index.fetch(:class_sources),
|
|
165
|
+
# Issue #644 — the cross-file value-constant publication table and its write attribution.
|
|
166
|
+
constant_values: def_index.fetch(:constant_values),
|
|
167
|
+
constant_sources: def_index.fetch(:constant_sources),
|
|
168
|
+
constant_writes: def_index.fetch(:constant_writes),
|
|
165
169
|
discovered_method_visibilities: def_index.fetch(:method_visibilities),
|
|
166
170
|
discovered_methods: def_index.fetch(:methods),
|
|
167
171
|
data_member_layouts: def_index.fetch(:data_member_layouts),
|
|
@@ -17,12 +17,15 @@ module Rigor
|
|
|
17
17
|
class RunSnapshots
|
|
18
18
|
attr_accessor :class_decl_paths, :signature_paths,
|
|
19
19
|
:synthesized_namespaces, :quarantined_signatures, :conformance_results,
|
|
20
|
-
:env_build_failure, :effect_annotation_carrier
|
|
20
|
+
:env_build_failure, :definition_build_failures, :effect_annotation_carrier
|
|
21
21
|
|
|
22
22
|
# Constructor defaults match the {Runner} constructor: the pre-seed values `build_run_stats` /
|
|
23
23
|
# `pre_file_diagnostics` read before the first analysis path runs are frozen empties. The
|
|
24
24
|
# `env_build_failure` slot is nil (no failure) rather than an empty collection — it holds a single
|
|
25
|
-
# `[error_class, first_line, buffer_names]` tuple or nothing.
|
|
25
|
+
# `[error_class, first_line, buffer_names]` tuple or nothing. Its per-class sibling
|
|
26
|
+
# `definition_build_failures` (#696) holds a LIST, because a collapsed universe fails many classes,
|
|
27
|
+
# and is accumulated ACROSS pool workers rather than assigned once — see
|
|
28
|
+
# {PoolCoordinator#merge_worker_reporters}.
|
|
26
29
|
def initialize
|
|
27
30
|
@class_decl_paths = {}.freeze
|
|
28
31
|
@signature_paths = [].freeze
|
|
@@ -30,6 +33,7 @@ module Rigor
|
|
|
30
33
|
@quarantined_signatures = [].freeze
|
|
31
34
|
@conformance_results = [].freeze
|
|
32
35
|
@env_build_failure = nil
|
|
36
|
+
@definition_build_failures = [].freeze
|
|
33
37
|
@effect_annotation_carrier = [].freeze
|
|
34
38
|
end
|
|
35
39
|
|
|
@@ -42,6 +46,7 @@ module Rigor
|
|
|
42
46
|
@quarantined_signatures = []
|
|
43
47
|
@conformance_results = []
|
|
44
48
|
@env_build_failure = nil
|
|
49
|
+
@definition_build_failures = []
|
|
45
50
|
@effect_annotation_carrier = [].freeze
|
|
46
51
|
end
|
|
47
52
|
end
|
|
@@ -344,16 +344,31 @@ module Rigor
|
|
|
344
344
|
@cached_plugin_prepare_diagnostics = [].freeze
|
|
345
345
|
@project_discovered_classes = {}.freeze
|
|
346
346
|
@project_discovered_def_nodes = {}.freeze
|
|
347
|
+
# Issue #681 — the `Module.nesting` recorded per declared `def`, keyed by node identity.
|
|
348
|
+
@project_discovered_def_nestings = {}.compare_by_identity.freeze
|
|
347
349
|
@project_discovered_singleton_def_nodes = {}.freeze
|
|
348
350
|
@project_discovered_def_sources = {}.freeze
|
|
349
351
|
@project_discovered_singleton_def_sources = {}.freeze
|
|
350
352
|
@project_discovered_superclasses = {}.freeze
|
|
353
|
+
# Issue #682 — the `Module.nesting` each class / module declaration HEADER is written in.
|
|
354
|
+
@project_discovered_header_nestings = {}.freeze
|
|
351
355
|
@project_discovered_includes = {}.freeze
|
|
352
356
|
@project_discovered_class_sources = {}.freeze
|
|
357
|
+
# Issue #644 — the cross-file VALUE-constant publication table (`{qualified name => Type::Constant}`,
|
|
358
|
+
# literal writes only) and its per-name write attribution. The first seeds `in_source_constants` on
|
|
359
|
+
# every per-file scope so `FOO = :sym` in one file reads as `:sym` in another; the second seeds
|
|
360
|
+
# `constant_sources` only on an ADR-46 recording run, where it is the positive edge's attribution.
|
|
361
|
+
@project_constant_values = {}.freeze
|
|
362
|
+
@project_constant_sources = {}.freeze
|
|
363
|
+
# ...and the raw per-(name, path) publication census the incremental producer diffs.
|
|
364
|
+
@project_constant_writes = {}.freeze
|
|
365
|
+
@published_constant_name_set = nil
|
|
353
366
|
@project_discovered_method_visibilities = {}.freeze
|
|
354
367
|
@project_discovered_methods = {}.freeze
|
|
355
368
|
@project_data_member_layouts = {}.freeze
|
|
356
369
|
@project_struct_member_layouts = {}.freeze
|
|
370
|
+
# Issue #684 — set per run by {#project_discovery_expansion}; nil on every path that does not widen.
|
|
371
|
+
@project_discovery_expansion = nil
|
|
357
372
|
# ADR-67 WD6a — the call-site parameter-inference table, populated by the opt-in pre-pass in
|
|
358
373
|
# `assemble_run_diagnostics` when `parameter_inference:` is enabled, then seeded onto every per-file
|
|
359
374
|
# scope (sequential + fork-worker) through `project_scope_seed_tables`. Empty by default, so the gate-off
|
|
@@ -404,6 +419,7 @@ module Rigor
|
|
|
404
419
|
return Result.new(diagnostics: [target_ruby_error]) if target_ruby_error
|
|
405
420
|
|
|
406
421
|
expansion = expand_paths(paths)
|
|
422
|
+
@project_discovery_expansion = project_discovery_expansion(paths, expansion)
|
|
407
423
|
@snapshots.reset_for_run
|
|
408
424
|
# Per-run reset of the deferred-discovery memo (see `#ensure_project_discovery`).
|
|
409
425
|
@project_discovery_done = false
|
|
@@ -530,6 +546,22 @@ module Rigor
|
|
|
530
546
|
result.transform_values(&:freeze).freeze
|
|
531
547
|
end
|
|
532
548
|
|
|
549
|
+
# Issue #644 — per-file PUBLICATION CENSUS: `{path => {qualified constant name => descriptor}}`, the
|
|
550
|
+
# value-constant twin of {#class_declarations}. The descriptor (`[literal]` or
|
|
551
|
+
# `ScopeIndexer::CONSTANT_UNPUBLISHABLE`) is load-bearing and a bare name set is not: a name's published
|
|
552
|
+
# answer is a function of the WHOLE project's write set for it, so what a recheck has to detect is a
|
|
553
|
+
# change in the census — a value edit, a write becoming unpublishable, a second declarer appearing or
|
|
554
|
+
# going away — not merely a name appearing.
|
|
555
|
+
# {Incremental.changed_constant_publications} diffs it; the reader's `constant:` edge is recorded on
|
|
556
|
+
# every reference, resolved or not, so the diff reaches it either way.
|
|
557
|
+
def constant_declarations
|
|
558
|
+
result = Hash.new { |hash, key| hash[key] = {} }
|
|
559
|
+
@project_constant_writes.each do |name, by_path|
|
|
560
|
+
by_path.each { |path, descriptor| result[path][name] = descriptor }
|
|
561
|
+
end
|
|
562
|
+
result.transform_values(&:freeze).freeze
|
|
563
|
+
end
|
|
564
|
+
|
|
533
565
|
# ADR-67 WD6c lift — the inferred-param table this run seeded from (frozen; empty when
|
|
534
566
|
# `parameter_inference:` is off or the pre-pass failed soft). The incremental session reads it back
|
|
535
567
|
# after a baseline so the snapshot records the seeds the cached diagnostics were computed under.
|
|
@@ -637,6 +669,8 @@ module Rigor
|
|
|
637
669
|
def evaluate_return_types_setup(paths, specs)
|
|
638
670
|
expansion = expand_paths(paths || @configuration.paths)
|
|
639
671
|
@project_discovery_done = false
|
|
672
|
+
# This entry point supplies its own expansion; it never widens (see {#project_discovery_expansion}).
|
|
673
|
+
@project_discovery_expansion = nil
|
|
640
674
|
@run_generation = Object.new.freeze
|
|
641
675
|
run_project_pre_passes(expansion: expansion)
|
|
642
676
|
ensure_project_discovery(expansion)
|
|
@@ -959,6 +993,10 @@ module Rigor
|
|
|
959
993
|
close_effect_graph
|
|
960
994
|
diagnostics += @diagnostic_aggregator.rbs_quarantined_signature_diagnostics
|
|
961
995
|
diagnostics += @diagnostic_aggregator.rbs_environment_build_failed_diagnostics
|
|
996
|
+
# Issue #696 — after its env-wide twin and before the synthesized-namespace notice: the three
|
|
997
|
+
# `rbs.coverage.*` build conditions surface widest-consequence first, and their relative order is
|
|
998
|
+
# the diagnostic output contract.
|
|
999
|
+
diagnostics += @diagnostic_aggregator.rbs_definition_build_failed_diagnostics
|
|
962
1000
|
diagnostics += @diagnostic_aggregator.rbs_synthesized_namespace_diagnostics
|
|
963
1001
|
diagnostics += @diagnostic_aggregator.conforms_to_diagnostics
|
|
964
1002
|
diagnostics += @diagnostic_aggregator.rbs_extended_reporter_diagnostics
|
|
@@ -1081,15 +1119,33 @@ module Rigor
|
|
|
1081
1119
|
end
|
|
1082
1120
|
|
|
1083
1121
|
def build_run_dependency_descriptor(expansion, rbs_descriptor)
|
|
1084
|
-
entries = analyzed_file_entries(expansion) +
|
|
1122
|
+
entries = analyzed_file_entries(expansion) + discovery_file_entries(expansion) +
|
|
1123
|
+
pre_eval_file_entries + rbs_descriptor.files
|
|
1124
|
+
globs = []
|
|
1085
1125
|
@plugin_registry.plugins.each do |plugin|
|
|
1086
1126
|
# Read the boundary WITHOUT triggering its lazy `@io_boundary ||=` initializer: plugin instances
|
|
1087
1127
|
# are frozen after the run, and a plugin that never built a boundary read no files through it,
|
|
1088
1128
|
# so it contributes no dependencies.
|
|
1089
1129
|
boundary = plugin.instance_variable_get(:@io_boundary)
|
|
1090
1130
|
entries.concat(boundary.cache_descriptor.files) if boundary
|
|
1131
|
+
plugin.class.producers.each_value do |prod|
|
|
1132
|
+
globs.concat(plugin.send(:watch_glob_entries, prod[:watch])) if prod[:watch]
|
|
1133
|
+
end
|
|
1091
1134
|
end
|
|
1092
|
-
Cache::Descriptor.new(files: entries)
|
|
1135
|
+
Cache::Descriptor.new(files: entries, globs: globs.uniq)
|
|
1136
|
+
end
|
|
1137
|
+
|
|
1138
|
+
# Issue #684 — the project files this run DISCOVERED but did not analyse. A widened run's diagnostics
|
|
1139
|
+
# are a function of them (that is the entire point: the method whose absence would have been reported
|
|
1140
|
+
# is defined in one of them), so the run-result cache must invalidate when one changes. Empty on every
|
|
1141
|
+
# run whose discovery expansion is its analysis expansion, which is every whole-project run.
|
|
1142
|
+
def discovery_file_entries(expansion)
|
|
1143
|
+
return [] if @project_discovery_expansion.nil?
|
|
1144
|
+
|
|
1145
|
+
analyzed = expansion.fetch(:files).to_set
|
|
1146
|
+
analyzed_file_entries(
|
|
1147
|
+
{ files: @project_discovery_expansion.fetch(:files).reject { |path| analyzed.include?(path) } }
|
|
1148
|
+
)
|
|
1093
1149
|
end
|
|
1094
1150
|
|
|
1095
1151
|
def analyzed_file_entries(expansion)
|
|
@@ -1185,12 +1241,18 @@ module Rigor
|
|
|
1185
1241
|
def apply_discovery_result(discovery)
|
|
1186
1242
|
@project_discovered_classes = discovery.discovered_classes
|
|
1187
1243
|
@project_discovered_def_nodes = discovery.discovered_def_nodes
|
|
1244
|
+
@project_discovered_def_nestings = discovery.discovered_def_nestings
|
|
1188
1245
|
@project_discovered_singleton_def_nodes = discovery.discovered_singleton_def_nodes
|
|
1189
1246
|
@project_discovered_def_sources = discovery.discovered_def_sources
|
|
1190
1247
|
@project_discovered_singleton_def_sources = discovery.discovered_singleton_def_sources
|
|
1191
1248
|
@project_discovered_superclasses = discovery.discovered_superclasses
|
|
1249
|
+
@project_discovered_header_nestings = discovery.discovered_header_nestings
|
|
1192
1250
|
@project_discovered_includes = discovery.discovered_includes
|
|
1193
1251
|
@project_discovered_class_sources = discovery.discovered_class_sources
|
|
1252
|
+
@project_constant_values = discovery.constant_values
|
|
1253
|
+
@published_constant_name_set = nil
|
|
1254
|
+
@project_constant_sources = discovery.constant_sources
|
|
1255
|
+
@project_constant_writes = discovery.constant_writes
|
|
1194
1256
|
@project_discovered_method_visibilities = discovery.discovered_method_visibilities
|
|
1195
1257
|
@project_discovered_methods = discovery.discovered_methods
|
|
1196
1258
|
@project_data_member_layouts = discovery.data_member_layouts
|
|
@@ -1210,6 +1272,8 @@ module Rigor
|
|
|
1210
1272
|
return if @project_discovery_done
|
|
1211
1273
|
|
|
1212
1274
|
@project_discovery_done = true
|
|
1275
|
+
# Issue #684 — the cross-file index spans the PROJECT even when the analysis targets a file list.
|
|
1276
|
+
expansion = @project_discovery_expansion || expansion
|
|
1213
1277
|
if @collect_seed_bundles
|
|
1214
1278
|
# ADR-85 WD2 — rebuild discovery from the prior run's bundles (re-walking only changed files) and
|
|
1215
1279
|
# capture the refreshed bundle set for the session to persist.
|
|
@@ -1222,6 +1286,39 @@ module Rigor
|
|
|
1222
1286
|
end
|
|
1223
1287
|
end
|
|
1224
1288
|
|
|
1289
|
+
# Issue #684 — the file set the cross-file DISCOVERY pre-pass walks, when that is wider than the set
|
|
1290
|
+
# being analysed. nil means "the same one", which is every whole-project run.
|
|
1291
|
+
#
|
|
1292
|
+
# `rigor check path/to/one_file.rb` — what an editor integration, a pre-commit hook and
|
|
1293
|
+
# `git diff --name-only | xargs rigor check` all do — discovered only the files it was handed, so a
|
|
1294
|
+
# class the project declares in `sig/` WITHOUT declaring every one of its methods drew a false
|
|
1295
|
+
# `call.undefined-method` for any method whose defining file was outside the invocation. The class was
|
|
1296
|
+
# RBS-known, source discovery could not supply the method, and the rule fired on correct code. The
|
|
1297
|
+
# same check over the containing DIRECTORY reported nothing, which is also why per-directory sums
|
|
1298
|
+
# over a tree over-reported against a whole-tree run (14 diagnostics of this shape against 2 during
|
|
1299
|
+
# the #652 work).
|
|
1300
|
+
#
|
|
1301
|
+
# Discovery is a parse pass, not an analysis: 0.55s over Rigor's own 444-file `lib` cold, and under
|
|
1302
|
+
# ADR-85 seed bundles a warm run re-walks only what changed. The alternative — standing the rule down
|
|
1303
|
+
# whenever evidence might be missing — buys the same FP safety by making `rigor check one_file.rb`
|
|
1304
|
+
# answer a different question than `rigor check .`, and the diagnostics for a file should not depend
|
|
1305
|
+
# on what else was on the command line.
|
|
1306
|
+
#
|
|
1307
|
+
# Excluded, each because its expansion is already the one it means: the LSP `prebuilt:` path (which
|
|
1308
|
+
# seeds discovery from a snapshot), editor `buffer:` mode, `run_source`'s in-memory single file, and
|
|
1309
|
+
# ADR-46's `analyze_only` subset mode (which already passes the full expansion and filters later).
|
|
1310
|
+
def project_discovery_expansion(paths, expansion)
|
|
1311
|
+
return nil unless widen_discovery_to_project?(paths)
|
|
1312
|
+
|
|
1313
|
+
widened = expand_paths(@configuration.paths | paths)
|
|
1314
|
+
widened.fetch(:files).size > expansion.fetch(:files).size ? widened : nil
|
|
1315
|
+
end
|
|
1316
|
+
|
|
1317
|
+
def widen_discovery_to_project?(paths)
|
|
1318
|
+
@prebuilt.nil? && @buffer.nil? && @in_memory_sources.nil? && @analyze_only.nil? &&
|
|
1319
|
+
paths != @configuration.paths
|
|
1320
|
+
end
|
|
1321
|
+
|
|
1225
1322
|
# ADR-46 — the dependency-recording and subset-analysis modes read the discovery tables OUTSIDE the
|
|
1226
1323
|
# analysis assembly (`Runner#symbol_fingerprints` / `#class_declarations`, consumed by
|
|
1227
1324
|
# {IncrementalSession} after the run), so they force the build eagerly — matching the pre-slice-1
|
|
@@ -1232,7 +1329,8 @@ module Rigor
|
|
|
1232
1329
|
end
|
|
1233
1330
|
|
|
1234
1331
|
private :run_project_pre_passes, :adopt_prebuilt_project_scan, :apply_pre_passes_result,
|
|
1235
|
-
:apply_discovery_result, :ensure_project_discovery, :force_eager_discovery
|
|
1332
|
+
:apply_discovery_result, :ensure_project_discovery, :force_eager_discovery?,
|
|
1333
|
+
:project_discovery_expansion, :widen_discovery_to_project?
|
|
1236
1334
|
|
|
1237
1335
|
# Ruby versions probed (ascending) to discover the lowest one this Prism build accepts for
|
|
1238
1336
|
# `version:`. Prism exposes no version list, so the floor is found empirically — only when a
|
|
@@ -1363,6 +1461,7 @@ module Rigor
|
|
|
1363
1461
|
synthesized_namespaces_snapshot: -> { @snapshots.synthesized_namespaces },
|
|
1364
1462
|
quarantined_signatures_snapshot: -> { @snapshots.quarantined_signatures },
|
|
1365
1463
|
env_build_failure_snapshot: -> { @snapshots.env_build_failure },
|
|
1464
|
+
definition_build_failures_snapshot: -> { @snapshots.definition_build_failures },
|
|
1366
1465
|
conformance_results_snapshot: -> { @snapshots.conformance_results }
|
|
1367
1466
|
)
|
|
1368
1467
|
end
|
|
@@ -1581,6 +1680,9 @@ module Rigor
|
|
|
1581
1680
|
tables[:run_generation] = @run_generation if @run_generation
|
|
1582
1681
|
tables[:discovered_classes] = @project_discovered_classes unless @project_discovered_classes.empty?
|
|
1583
1682
|
tables[:discovered_def_nodes] = @project_discovered_def_nodes unless @project_discovered_def_nodes.empty?
|
|
1683
|
+
unless @project_discovered_def_nestings.empty?
|
|
1684
|
+
tables[:discovered_def_nestings] = @project_discovered_def_nestings
|
|
1685
|
+
end
|
|
1584
1686
|
unless @project_discovered_singleton_def_nodes.empty?
|
|
1585
1687
|
tables[:discovered_singleton_def_nodes] = @project_discovered_singleton_def_nodes
|
|
1586
1688
|
end
|
|
@@ -1588,6 +1690,9 @@ module Rigor
|
|
|
1588
1690
|
unless @project_discovered_superclasses.empty?
|
|
1589
1691
|
tables[:discovered_superclasses] = @project_discovered_superclasses
|
|
1590
1692
|
end
|
|
1693
|
+
unless @project_discovered_header_nestings.empty?
|
|
1694
|
+
tables[:discovered_header_nestings] = @project_discovered_header_nestings
|
|
1695
|
+
end
|
|
1591
1696
|
tables[:discovered_includes] = @project_discovered_includes unless @project_discovered_includes.empty?
|
|
1592
1697
|
unless @project_discovered_method_visibilities.empty?
|
|
1593
1698
|
tables[:discovered_method_visibilities] = @project_discovered_method_visibilities
|
|
@@ -1595,14 +1700,22 @@ module Rigor
|
|
|
1595
1700
|
tables[:discovered_methods] = @project_discovered_methods unless @project_discovered_methods.empty?
|
|
1596
1701
|
seed_opt_in_pre_pass_tables(tables)
|
|
1597
1702
|
seed_member_layout_tables(tables)
|
|
1598
|
-
|
|
1599
|
-
# dependency recording, so seed it only when recording is on; a normal run never carries it.
|
|
1600
|
-
if @record_dependencies && !@project_discovered_class_sources.empty?
|
|
1601
|
-
tables[:discovered_class_sources] = @project_discovered_class_sources
|
|
1602
|
-
end
|
|
1703
|
+
seed_dependency_attribution_tables(tables)
|
|
1603
1704
|
tables
|
|
1604
1705
|
end
|
|
1605
1706
|
|
|
1707
|
+
# ADR-46 slice 1 / issue #644 — the two SOURCE-ATTRIBUTION tables, read only by the recording accessors
|
|
1708
|
+
# (`Scope#record_class_dependency` for the class-declaration map, `Scope#record_constant_dependency`
|
|
1709
|
+
# for the constant-write map). A normal run carries neither. Extracted to keep
|
|
1710
|
+
# {#project_scope_seed_tables} under the complexity budget.
|
|
1711
|
+
def seed_dependency_attribution_tables(tables)
|
|
1712
|
+
return unless @record_dependencies
|
|
1713
|
+
|
|
1714
|
+
tables[:discovered_class_sources] = @project_discovered_class_sources unless
|
|
1715
|
+
@project_discovered_class_sources.empty?
|
|
1716
|
+
tables[:constant_sources] = @project_constant_sources unless @project_constant_sources.empty?
|
|
1717
|
+
end
|
|
1718
|
+
|
|
1606
1719
|
# Issue #260 — the mutable starting point {#project_scope_seed_tables} fills in: a copy of the opt-in
|
|
1607
1720
|
# `discovery_seed:` when one was supplied, otherwise the empty Hash every other run has always started
|
|
1608
1721
|
# from. Extracted so the seed costs {#project_scope_seed_tables} no branch of its complexity budget.
|
|
@@ -1622,9 +1735,33 @@ module Rigor
|
|
|
1622
1735
|
# Extracted to keep {#project_scope_seed_tables} under the complexity budget.
|
|
1623
1736
|
def seed_opt_in_pre_pass_tables(tables)
|
|
1624
1737
|
tables[:param_inferred_types] = @project_param_inferred_types unless @project_param_inferred_types.empty?
|
|
1625
|
-
|
|
1738
|
+
constants = merged_seed_constants
|
|
1739
|
+
return if constants.empty?
|
|
1740
|
+
|
|
1741
|
+
tables[:in_source_constants] = constants
|
|
1742
|
+
names = published_constant_name_set
|
|
1743
|
+
tables[:published_constant_names] = names unless names.empty?
|
|
1744
|
+
end
|
|
1745
|
+
|
|
1746
|
+
# Issue #644 — the seeded `in_source_constants` table, joining the two cross-file constant producers.
|
|
1747
|
+
# The whole-project literal table wins over a `pre_eval:` publication of the same name: both describe
|
|
1748
|
+
# the same assignment, the literal table pins the value the write actually carries while ADR-17's
|
|
1749
|
+
# widener erases it to the class, and listing a file under `pre_eval:` must never LOSE precision. The
|
|
1750
|
+
# per-file table still wins over both (`ScopeIndexer.index`'s merge) — same-file stays the most
|
|
1751
|
+
# specific authority.
|
|
1752
|
+
# Issue #644 — the LAST SEGMENTS of the published table, the run-wide half of
|
|
1753
|
+
# `Scope#published_constant?`. Seeded on EVERY run (unlike `constant_sources`, which only a recording
|
|
1754
|
+
# run needs) because {CheckRules::PublishedConstantGuard} asks the question on every `if` / `unless`.
|
|
1755
|
+
def published_constant_name_set
|
|
1756
|
+
@published_constant_name_set ||=
|
|
1757
|
+
@project_constant_values.keys.to_set { |name| name.split("::").last }.freeze
|
|
1758
|
+
end
|
|
1759
|
+
|
|
1760
|
+
def merged_seed_constants
|
|
1761
|
+
return @project_constant_values if @project_pre_eval_constants.empty?
|
|
1762
|
+
return @project_pre_eval_constants if @project_constant_values.empty?
|
|
1626
1763
|
|
|
1627
|
-
|
|
1764
|
+
@project_pre_eval_constants.merge(@project_constant_values).freeze
|
|
1628
1765
|
end
|
|
1629
1766
|
|
|
1630
1767
|
# ADR-46 — seed the instance + singleton `"path:line"` def-source tables (each only when non-empty).
|
|
@@ -235,6 +235,14 @@ module Rigor
|
|
|
235
235
|
# Read-once snapshot of the per-worker reporters so the caller (or the eventual Phase 4b pool
|
|
236
236
|
# aggregator) can merge into a single coordinator-side reporter. Both reporters dedupe at write time,
|
|
237
237
|
# so a post-hoc concat + de-dup at the entry-key level is sound.
|
|
238
|
+
#
|
|
239
|
+
# Issue #696 — `definition_build_failures` rides this channel rather than a coordinator-side snapshot,
|
|
240
|
+
# and it has to. Under the pool the PARENT never analyses a file, so its loader never demands a
|
|
241
|
+
# definition and never enters the failing build; a diagnostic wired off the parent's loader would
|
|
242
|
+
# appear at `--workers=0` and vanish at `--workers=N`, which is the same "reports less depending on
|
|
243
|
+
# how you ran it" defect the diagnostic exists to end. Draining it out of the workers is what makes
|
|
244
|
+
# the two paths say the same thing. The payload is `[String, String, String, Array<String>]` tuples —
|
|
245
|
+
# Marshal-clean for the fork backend and shareable for the Ractor one.
|
|
238
246
|
def drain_reporters
|
|
239
247
|
{
|
|
240
248
|
rbs_extended: {
|
|
@@ -242,7 +250,8 @@ module Rigor
|
|
|
242
250
|
lossy_projections: @rbs_extended_reporter.lossy_projections
|
|
243
251
|
},
|
|
244
252
|
boundary_cross: @boundary_cross_reporter.entries,
|
|
245
|
-
source_rbs_synthesis: @source_rbs_synthesis_reporter.entries
|
|
253
|
+
source_rbs_synthesis: @source_rbs_synthesis_reporter.entries,
|
|
254
|
+
definition_build_failures: @environment&.rbs_loader&.definition_build_failures || []
|
|
246
255
|
}
|
|
247
256
|
end
|
|
248
257
|
|
data/lib/rigor/bleeding_edge.rb
CHANGED
|
@@ -107,14 +107,15 @@ module Rigor
|
|
|
107
107
|
id: "reject-unparseable-signatures",
|
|
108
108
|
kind: :severity,
|
|
109
109
|
summary: "A broken `signature_paths:` RBS set fails the run instead of degrading it silently. An " \
|
|
110
|
-
"unparseable `.rbs` is otherwise skipped with a warning
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"
|
|
114
|
-
"already is.",
|
|
110
|
+
"unparseable `.rbs` is otherwise skipped with a warning; a duplicate-declaration conflict " \
|
|
111
|
+
"(a file that parses fine but collides on resolve — typically against Rigor's own bundled " \
|
|
112
|
+
"RBS) collapses the whole env with a warning; and a duplicate METHOD definition collapses " \
|
|
113
|
+
"one class's method surface the same way. Each leaves the run quieter rather than " \
|
|
114
|
+
"cleaner. This treats all three as a build error, the way a broken source file already is.",
|
|
115
115
|
severity_overrides: {
|
|
116
116
|
"rbs.coverage.quarantined-signature" => :error,
|
|
117
|
-
"rbs.coverage.environment-build-failed" => :error
|
|
117
|
+
"rbs.coverage.environment-build-failed" => :error,
|
|
118
|
+
"rbs.coverage.definition-build-failed" => :error
|
|
118
119
|
}.freeze
|
|
119
120
|
),
|
|
120
121
|
Feature.new(
|
|
@@ -52,6 +52,15 @@ module Rigor
|
|
|
52
52
|
Rigor::Inference::HktBodyParser.parse(CSV_PARSED_BODY, params: [:K])
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
+
# `csv::row[K]` — `Array[K | nil]` (CSV.parse_line's shape:
|
|
56
|
+
# an Array of optionally-nil cell values for a single row).
|
|
57
|
+
CSV_ROW_BODY = "Array[K | nil]"
|
|
58
|
+
private_constant :CSV_ROW_BODY
|
|
59
|
+
|
|
60
|
+
def csv_row_body_tree
|
|
61
|
+
Rigor::Inference::HktBodyParser.parse(CSV_ROW_BODY, params: [:K])
|
|
62
|
+
end
|
|
63
|
+
|
|
55
64
|
def json_value_registration
|
|
56
65
|
Rigor::Inference::HktRegistry::Registration.new(
|
|
57
66
|
uri: :"json::value",
|
|
@@ -90,6 +99,25 @@ module Rigor
|
|
|
90
99
|
)
|
|
91
100
|
end
|
|
92
101
|
|
|
102
|
+
def csv_row_registration
|
|
103
|
+
Rigor::Inference::HktRegistry::Registration.new(
|
|
104
|
+
uri: :"csv::row",
|
|
105
|
+
arity: 1,
|
|
106
|
+
variance: [:out],
|
|
107
|
+
bound: Rigor::Type::Combinator.untyped
|
|
108
|
+
)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def csv_row_definition
|
|
112
|
+
Rigor::Inference::HktRegistry.definition_with_body_tree(
|
|
113
|
+
uri: :"csv::row",
|
|
114
|
+
params: [:K],
|
|
115
|
+
body_tree: csv_row_body_tree,
|
|
116
|
+
source_path: __FILE__,
|
|
117
|
+
source_line: __LINE__ - 5
|
|
118
|
+
)
|
|
119
|
+
end
|
|
120
|
+
|
|
93
121
|
# @return [Rigor::Inference::HktRegistry] frozen registry
|
|
94
122
|
# pre-seeded with all bundled HKT registrations +
|
|
95
123
|
# bodies. Allocated fresh each call rather than
|
|
@@ -104,8 +132,8 @@ module Rigor
|
|
|
104
132
|
# once ADR-15 phase 4b.x covers the dependency graph.
|
|
105
133
|
def registry
|
|
106
134
|
Rigor::Inference::HktRegistry.new(
|
|
107
|
-
registrations: [json_value_registration, csv_parsed_registration],
|
|
108
|
-
definitions: [json_value_definition, csv_parsed_definition]
|
|
135
|
+
registrations: [json_value_registration, csv_parsed_registration, csv_row_registration],
|
|
136
|
+
definitions: [json_value_definition, csv_parsed_definition, csv_row_definition]
|
|
109
137
|
)
|
|
110
138
|
end
|
|
111
139
|
|
|
@@ -160,6 +188,14 @@ module Rigor
|
|
|
160
188
|
}.freeze
|
|
161
189
|
private_constant :CSV_PARSED_SPEC
|
|
162
190
|
|
|
191
|
+
CSV_ROW_SPEC = {
|
|
192
|
+
uri: :"csv::row",
|
|
193
|
+
args: ["String"],
|
|
194
|
+
discriminator: nil,
|
|
195
|
+
post_reduce: nil
|
|
196
|
+
}.freeze
|
|
197
|
+
private_constant :CSV_ROW_SPEC
|
|
198
|
+
|
|
163
199
|
METHOD_RETURN_OVERRIDES = {
|
|
164
200
|
# JSON — stdlib's `json` library. Upstream rbs declares
|
|
165
201
|
# `(string, ?options) -> untyped`; the HKT-builtin tier
|
|
@@ -190,17 +226,20 @@ module Rigor
|
|
|
190
226
|
["YAML", :safe_load_file, :singleton] => YAML_SAFE_VALUE_SPEC,
|
|
191
227
|
["Psych", :safe_load, :singleton] => YAML_SAFE_VALUE_SPEC,
|
|
192
228
|
["Psych", :safe_load_file, :singleton] => YAML_SAFE_VALUE_SPEC,
|
|
193
|
-
# CSV.parse / CSV.read — no-headers shape only.
|
|
229
|
+
# CSV.parse / CSV.read / CSV.readlines / CSV.parse_line — no-headers shape only.
|
|
194
230
|
# Upstream rbs declares broader return shapes but
|
|
195
231
|
# the common case is `Array[Array[String?]]` which
|
|
196
|
-
# the `csv::parsed[String]` URI matches
|
|
232
|
+
# the `csv::parsed[String]` URI matches, or
|
|
233
|
+
# `Array[String?]` which `csv::row[String]` matches. The
|
|
197
234
|
# `headers: true` shape (`CSV::Table` of `CSV::Row`)
|
|
198
235
|
# is NOT covered — calls passing the option fall
|
|
199
236
|
# through to the upstream RBS type. CSV.foreach also
|
|
200
237
|
# falls through (it yields rows rather than
|
|
201
238
|
# returning a typed structure).
|
|
202
|
-
["CSV", :parse,
|
|
203
|
-
["CSV", :read,
|
|
239
|
+
["CSV", :parse, :singleton] => CSV_PARSED_SPEC,
|
|
240
|
+
["CSV", :read, :singleton] => CSV_PARSED_SPEC,
|
|
241
|
+
["CSV", :readlines, :singleton] => CSV_PARSED_SPEC,
|
|
242
|
+
["CSV", :parse_line, :singleton] => CSV_ROW_SPEC
|
|
204
243
|
}.freeze
|
|
205
244
|
|
|
206
245
|
# @return [Rigor::Type, nil] the reduced HKT type for
|