rigortype 0.3.6 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +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/handbook/11-sig-gen.md +24 -14
- data/docs/manual/02-cli-reference.md +23 -7
- data/docs/manual/04-diagnostics.md +1 -1
- 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 +188 -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 +63 -16
- 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 +233 -16
- data/lib/rigor/analysis/runner/pool_coordinator.rb +266 -47
- data/lib/rigor/analysis/runner/project_pre_passes.rb +15 -11
- data/lib/rigor/analysis/runner/run_snapshots.rb +12 -2
- data/lib/rigor/analysis/runner.rb +212 -16
- data/lib/rigor/analysis/worker_session.rb +37 -3
- data/lib/rigor/bleeding_edge.rb +7 -6
- data/lib/rigor/builtins/hkt_builtins.rb +45 -6
- data/lib/rigor/builtins/imported_refinements.rb +11 -9
- data/lib/rigor/builtins/predefined_constant_refinements.rb +108 -59
- data/lib/rigor/builtins/static_return_refinements.rb +20 -1
- data/lib/rigor/cache/annotation_location.rb +72 -0
- 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 +70 -8
- data/lib/rigor/cache/store.rb +17 -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 +47 -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/failure_slot.rb +28 -0
- 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 +743 -93
- data/lib/rigor/environment.rb +125 -25
- 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 +61 -3
- data/lib/rigor/inference/hkt_sugar_translator.rb +87 -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/plugin/registry.rb +25 -2
- 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/rbs_extended/envelope_scanner.rb +8 -7
- data/lib/rigor/rbs_extended/hkt_directives.rb +16 -1
- data/lib/rigor/rbs_extended/reporter.rb +93 -13
- data/lib/rigor/rbs_extended.rb +6 -1
- 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 +320 -85
- data/lib/rigor/sig_gen/renderer.rb +14 -12
- 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 +9 -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 +71 -3
|
@@ -29,6 +29,7 @@ require_relative "../inference/scope_indexer"
|
|
|
29
29
|
require_relative "../inference/synthetic_method_scanner"
|
|
30
30
|
require_relative "../inference/project_patched_scanner"
|
|
31
31
|
require_relative "../inference/method_dispatcher/file_folding"
|
|
32
|
+
require_relative "crash_signature"
|
|
32
33
|
require_relative "buffer_binding"
|
|
33
34
|
require_relative "check_rules"
|
|
34
35
|
require_relative "dependency_recorder"
|
|
@@ -64,7 +65,14 @@ module Rigor
|
|
|
64
65
|
|
|
65
66
|
attr_reader :cache_store, :plugin_registry, :dependency_source_index,
|
|
66
67
|
:rbs_extended_reporter, :boundary_cross_reporter,
|
|
67
|
-
:analyzed_files, :unresolved_self_calls, :seed_bundles
|
|
68
|
+
:analyzed_files, :unresolved_self_calls, :seed_bundles,
|
|
69
|
+
# #788 rounds 6–7 — the rows per-file analysis produced this run, severity-resolved exactly
|
|
70
|
+
# as the run's stream is and sliced to this run's targets (a pool backend folds `.rigor.yml`
|
|
71
|
+
# prepare / degraded rows into `analyze_files`'s return). `IncrementalSession` caches ONLY
|
|
72
|
+
# this and serves reused files from it without re-stamping: a run-level row is regenerated
|
|
73
|
+
# every run wherever it is positioned, and slicing the run's full stream by path cached the
|
|
74
|
+
# file-positioned ones (`effect.annotations-unchecked`, `source-rbs-*`).
|
|
75
|
+
:per_file_diagnostics
|
|
68
76
|
|
|
69
77
|
# ADR-46 — the per-file cross-file read records this run captured (empty unless
|
|
70
78
|
# `record_dependencies: true`). Sequential analysis records into `@file_dependencies` via
|
|
@@ -302,6 +310,7 @@ module Rigor
|
|
|
302
310
|
# See `self_undefined_rule_active?`.
|
|
303
311
|
@self_undefined_rule_active = nil
|
|
304
312
|
@analyzed_files = [].freeze
|
|
313
|
+
@per_file_diagnostics = [].freeze
|
|
305
314
|
# In-memory source map for `#run_source` — `{ logical_path => source String }`. When set,
|
|
306
315
|
# `parse_source` reads bytes from here instead of disk and `expand_paths` accepts the (possibly
|
|
307
316
|
# non-existent) logical path. nil on a normal disk-backed run.
|
|
@@ -344,16 +353,31 @@ module Rigor
|
|
|
344
353
|
@cached_plugin_prepare_diagnostics = [].freeze
|
|
345
354
|
@project_discovered_classes = {}.freeze
|
|
346
355
|
@project_discovered_def_nodes = {}.freeze
|
|
356
|
+
# Issue #681 — the `Module.nesting` recorded per declared `def`, keyed by node identity.
|
|
357
|
+
@project_discovered_def_nestings = {}.compare_by_identity.freeze
|
|
347
358
|
@project_discovered_singleton_def_nodes = {}.freeze
|
|
348
359
|
@project_discovered_def_sources = {}.freeze
|
|
349
360
|
@project_discovered_singleton_def_sources = {}.freeze
|
|
350
361
|
@project_discovered_superclasses = {}.freeze
|
|
362
|
+
# Issue #682 — the `Module.nesting` each class / module declaration HEADER is written in.
|
|
363
|
+
@project_discovered_header_nestings = {}.freeze
|
|
351
364
|
@project_discovered_includes = {}.freeze
|
|
352
365
|
@project_discovered_class_sources = {}.freeze
|
|
366
|
+
# Issue #644 — the cross-file VALUE-constant publication table (`{qualified name => Type::Constant}`,
|
|
367
|
+
# literal writes only) and its per-name write attribution. The first seeds `in_source_constants` on
|
|
368
|
+
# every per-file scope so `FOO = :sym` in one file reads as `:sym` in another; the second seeds
|
|
369
|
+
# `constant_sources` only on an ADR-46 recording run, where it is the positive edge's attribution.
|
|
370
|
+
@project_constant_values = {}.freeze
|
|
371
|
+
@project_constant_sources = {}.freeze
|
|
372
|
+
# ...and the raw per-(name, path) publication census the incremental producer diffs.
|
|
373
|
+
@project_constant_writes = {}.freeze
|
|
374
|
+
@published_constant_name_set = nil
|
|
353
375
|
@project_discovered_method_visibilities = {}.freeze
|
|
354
376
|
@project_discovered_methods = {}.freeze
|
|
355
377
|
@project_data_member_layouts = {}.freeze
|
|
356
378
|
@project_struct_member_layouts = {}.freeze
|
|
379
|
+
# Issue #684 — set per run by {#project_discovery_expansion}; nil on every path that does not widen.
|
|
380
|
+
@project_discovery_expansion = nil
|
|
357
381
|
# ADR-67 WD6a — the call-site parameter-inference table, populated by the opt-in pre-pass in
|
|
358
382
|
# `assemble_run_diagnostics` when `parameter_inference:` is enabled, then seeded onto every per-file
|
|
359
383
|
# scope (sequential + fork-worker) through `project_scope_seed_tables`. Empty by default, so the gate-off
|
|
@@ -404,12 +428,16 @@ module Rigor
|
|
|
404
428
|
return Result.new(diagnostics: [target_ruby_error]) if target_ruby_error
|
|
405
429
|
|
|
406
430
|
expansion = expand_paths(paths)
|
|
431
|
+
@project_discovery_expansion = project_discovery_expansion(paths, expansion)
|
|
407
432
|
@snapshots.reset_for_run
|
|
408
433
|
# Per-run reset of the deferred-discovery memo (see `#ensure_project_discovery`).
|
|
409
434
|
@project_discovery_done = false
|
|
410
435
|
# Per-run reset of the environment the cacheable path resolves, reused by the envelope pass so a
|
|
411
436
|
# run never builds two.
|
|
412
437
|
@run_environment = nil
|
|
438
|
+
# #788 — the per-file reader is assigned only on the analysis (miss) path; reset it here so a run the
|
|
439
|
+
# ADR-45 result cache serves does not answer with the previous run's rows.
|
|
440
|
+
@per_file_diagnostics = [].freeze
|
|
413
441
|
# ADR-84 WD2 — roll the return-memo bucket: a fresh frozen token per run makes every per-file scope
|
|
414
442
|
# of THIS run share one memo bucket while entries from any earlier run in this process (stale after
|
|
415
443
|
# an edit) become unreachable.
|
|
@@ -530,6 +558,22 @@ module Rigor
|
|
|
530
558
|
result.transform_values(&:freeze).freeze
|
|
531
559
|
end
|
|
532
560
|
|
|
561
|
+
# Issue #644 — per-file PUBLICATION CENSUS: `{path => {qualified constant name => descriptor}}`, the
|
|
562
|
+
# value-constant twin of {#class_declarations}. The descriptor (`[literal]` or
|
|
563
|
+
# `ScopeIndexer::CONSTANT_UNPUBLISHABLE`) is load-bearing and a bare name set is not: a name's published
|
|
564
|
+
# answer is a function of the WHOLE project's write set for it, so what a recheck has to detect is a
|
|
565
|
+
# change in the census — a value edit, a write becoming unpublishable, a second declarer appearing or
|
|
566
|
+
# going away — not merely a name appearing.
|
|
567
|
+
# {Incremental.changed_constant_publications} diffs it; the reader's `constant:` edge is recorded on
|
|
568
|
+
# every reference, resolved or not, so the diff reaches it either way.
|
|
569
|
+
def constant_declarations
|
|
570
|
+
result = Hash.new { |hash, key| hash[key] = {} }
|
|
571
|
+
@project_constant_writes.each do |name, by_path|
|
|
572
|
+
by_path.each { |path, descriptor| result[path][name] = descriptor }
|
|
573
|
+
end
|
|
574
|
+
result.transform_values(&:freeze).freeze
|
|
575
|
+
end
|
|
576
|
+
|
|
533
577
|
# ADR-67 WD6c lift — the inferred-param table this run seeded from (frozen; empty when
|
|
534
578
|
# `parameter_inference:` is off or the pre-pass failed soft). The incremental session reads it back
|
|
535
579
|
# after a baseline so the snapshot records the seeds the cached diagnostics were computed under.
|
|
@@ -637,10 +681,19 @@ module Rigor
|
|
|
637
681
|
def evaluate_return_types_setup(paths, specs)
|
|
638
682
|
expansion = expand_paths(paths || @configuration.paths)
|
|
639
683
|
@project_discovery_done = false
|
|
684
|
+
# This entry point supplies its own expansion; it never widens (see {#project_discovery_expansion}).
|
|
685
|
+
@project_discovery_expansion = nil
|
|
640
686
|
@run_generation = Object.new.freeze
|
|
641
687
|
run_project_pre_passes(expansion: expansion)
|
|
642
688
|
ensure_project_discovery(expansion)
|
|
643
|
-
|
|
689
|
+
# Issue #795 — `source_files:` is the WHOLE project's file list, never `target_files(expansion)`.
|
|
690
|
+
# `IncrementalSession#build_runner` threads its own `@buffer` into this Runner, so in editor-buffer
|
|
691
|
+
# mode `target_files` narrows to the buffer's single logical path; an environment built over just
|
|
692
|
+
# that path drops every OTHER file's plugin-synthesized virtual RBS, which can flip a re-evaluated
|
|
693
|
+
# return descriptor and wrongly declare a caller's return type unstable (or stable) relative to the
|
|
694
|
+
# full-project answer. This probe never analyzes a file, so it carries none of #788's per-file-cache
|
|
695
|
+
# duplication concern either.
|
|
696
|
+
environment = @pool_coordinator.resolve_sequential_environment(source_files: expansion.fetch(:files))
|
|
644
697
|
specs.to_h do |spec|
|
|
645
698
|
[[spec[:class_name], spec[:method_name], spec[:singleton]], evaluate_spec_returns(spec, environment)]
|
|
646
699
|
end
|
|
@@ -893,9 +946,19 @@ module Rigor
|
|
|
893
946
|
).diagnostics
|
|
894
947
|
end
|
|
895
948
|
|
|
949
|
+
# Issue #795 — `source_files:` is the WHOLE project's file list (`expansion.fetch(:files)`), never
|
|
950
|
+
# `target_files(expansion)`. A subset or editor-buffer run's `target_files` narrows to the
|
|
951
|
+
# `analyze_only` closure or the buffer's single logical path; building the envelope walk's
|
|
952
|
+
# environment over that subset drops every plugin-synthesized virtual RBS from an excluded file, so
|
|
953
|
+
# an envelope declared only in that file's inline annotation is invisible to the walk — the same #793
|
|
954
|
+
# gap #788 closed for per-file analysis, left open here because #788's binding sentences scoped
|
|
955
|
+
# themselves to "the environments the per-file analysis builds". This path is exempt from #788's
|
|
956
|
+
# per-file-cache duplication concern: `effect.unknown-label` (and its envelope-pass siblings) are
|
|
957
|
+
# produced by this pass, never by `Runner#per_file_diagnostics`, so `IncrementalSession` never caches
|
|
958
|
+
# them and widening this source list cannot duplicate a row on recheck.
|
|
896
959
|
def envelope_rbs_loader(expansion)
|
|
897
960
|
environment = @run_environment ||
|
|
898
|
-
@pool_coordinator.resolve_sequential_environment(source_files:
|
|
961
|
+
@pool_coordinator.resolve_sequential_environment(source_files: expansion.fetch(:files))
|
|
899
962
|
environment&.rbs_loader
|
|
900
963
|
rescue StandardError
|
|
901
964
|
nil
|
|
@@ -951,7 +1014,17 @@ module Rigor
|
|
|
951
1014
|
# per-file cache, so it needs the full analyzed set to subtract the affected closure from.
|
|
952
1015
|
targets = target_files(expansion)
|
|
953
1016
|
@analyzed_files = targets
|
|
954
|
-
|
|
1017
|
+
# Issue #784 — the whole project's file list rides along so an EMPTY `targets` (a narrowed run whose
|
|
1018
|
+
# closure is empty) can still resolve the environment a full run would — same files, same
|
|
1019
|
+
# synthesized RBS — to demand the HKT registry once, while a project with no files resolves nothing.
|
|
1020
|
+
# #788 rounds 6–7 — the per-file stream is kept apart from the run-level streams appended below,
|
|
1021
|
+
# because `IncrementalSession` must cache ONLY what per-file analysis produced (#per_file_diagnostics),
|
|
1022
|
+
# and the reader is exposed SEVERITY-RESOLVED and sliced to this run's targets. The cache serves a
|
|
1023
|
+
# reused file without re-stamping, so a raw row would resurrect a rule the profile resolves to `:off`
|
|
1024
|
+
# (`static.value-use.void` ships off on the default profile) and serve the authored severity where an
|
|
1025
|
+
# override re-stamps it; and a pool backend folds `.rigor.yml`-positioned prepare / pool-degraded rows
|
|
1026
|
+
# into the same return, which the slice drops. The run's own stream is stamped once, at the end.
|
|
1027
|
+
diagnostics += analyze_targets(targets, environment: environment, project_files: expansion.fetch(:files))
|
|
955
1028
|
# ADR-103 WD12 — the effect fixpoint, in the post-pool aggregation slot beside the conformance
|
|
956
1029
|
# results. Graph-only over a finite lattice, so it is a plain worklist to a true fixpoint; it
|
|
957
1030
|
# contributes NO diagnostics and its result leaves through `#effect_table`, never through the
|
|
@@ -959,6 +1032,14 @@ module Rigor
|
|
|
959
1032
|
close_effect_graph
|
|
960
1033
|
diagnostics += @diagnostic_aggregator.rbs_quarantined_signature_diagnostics
|
|
961
1034
|
diagnostics += @diagnostic_aggregator.rbs_environment_build_failed_diagnostics
|
|
1035
|
+
# Issue #696 — after its env-wide twin and before the synthesized-namespace notice: the four
|
|
1036
|
+
# `rbs.coverage.*` build conditions surface widest-consequence first, and their relative order is
|
|
1037
|
+
# the diagnostic output contract.
|
|
1038
|
+
diagnostics += @diagnostic_aggregator.rbs_definition_build_failed_diagnostics
|
|
1039
|
+
# Issue #784 — last of the four, deliberately: it loses only the IMPLICIT HKT registrations a
|
|
1040
|
+
# `type` alias would have contributed, never a class's own declared method surface, so it is the
|
|
1041
|
+
# narrowest-consequence rung on the same ladder.
|
|
1042
|
+
diagnostics += @diagnostic_aggregator.rbs_hkt_scan_failed_diagnostics
|
|
962
1043
|
diagnostics += @diagnostic_aggregator.rbs_synthesized_namespace_diagnostics
|
|
963
1044
|
diagnostics += @diagnostic_aggregator.conforms_to_diagnostics
|
|
964
1045
|
diagnostics += @diagnostic_aggregator.rbs_extended_reporter_diagnostics
|
|
@@ -966,6 +1047,21 @@ module Rigor
|
|
|
966
1047
|
diagnostics + @diagnostic_aggregator.source_rbs_synthesis_diagnostics
|
|
967
1048
|
end
|
|
968
1049
|
|
|
1050
|
+
# #788 round 7 — runs per-file analysis over `targets` and exposes what it produced as
|
|
1051
|
+
# `#per_file_diagnostics`: the `analyze_files` return, stamped with the same severity profile the run's
|
|
1052
|
+
# own stream gets (`SeverityStamp` drops `:off` rows and re-stamps overrides, and the per-file cache never
|
|
1053
|
+
# re-stamps), sliced to the rows positioned at the targets. Returns the RAW return for the run's stream,
|
|
1054
|
+
# which is stamped once, at the end of `#run_analysis`.
|
|
1055
|
+
def analyze_targets(targets, environment:, project_files:)
|
|
1056
|
+
raw = @pool_coordinator.analyze_files(targets, environment: environment, project_files: project_files)
|
|
1057
|
+
analysed = targets.to_set
|
|
1058
|
+
@per_file_diagnostics = @diagnostic_aggregator.apply_severity_profile(raw)
|
|
1059
|
+
.select { |diagnostic| analysed.include?(diagnostic.path) }
|
|
1060
|
+
.freeze
|
|
1061
|
+
raw
|
|
1062
|
+
end
|
|
1063
|
+
private :analyze_targets
|
|
1064
|
+
|
|
969
1065
|
# ADR-67 WD6a — the check-walk parameter-inference pre-pass. Populates `@project_param_inferred_types`
|
|
970
1066
|
# (read by `project_scope_seed_tables`) with the call-site union of every undeclared parameter, running
|
|
971
1067
|
# ONE round (a single hop of call-site → param typing; the protection scan's three-round fixpoint stays a
|
|
@@ -1081,15 +1177,33 @@ module Rigor
|
|
|
1081
1177
|
end
|
|
1082
1178
|
|
|
1083
1179
|
def build_run_dependency_descriptor(expansion, rbs_descriptor)
|
|
1084
|
-
entries = analyzed_file_entries(expansion) +
|
|
1180
|
+
entries = analyzed_file_entries(expansion) + discovery_file_entries(expansion) +
|
|
1181
|
+
pre_eval_file_entries + rbs_descriptor.files
|
|
1182
|
+
globs = []
|
|
1085
1183
|
@plugin_registry.plugins.each do |plugin|
|
|
1086
1184
|
# Read the boundary WITHOUT triggering its lazy `@io_boundary ||=` initializer: plugin instances
|
|
1087
1185
|
# are frozen after the run, and a plugin that never built a boundary read no files through it,
|
|
1088
1186
|
# so it contributes no dependencies.
|
|
1089
1187
|
boundary = plugin.instance_variable_get(:@io_boundary)
|
|
1090
1188
|
entries.concat(boundary.cache_descriptor.files) if boundary
|
|
1189
|
+
plugin.class.producers.each_value do |prod|
|
|
1190
|
+
globs.concat(plugin.send(:watch_glob_entries, prod[:watch])) if prod[:watch]
|
|
1191
|
+
end
|
|
1091
1192
|
end
|
|
1092
|
-
Cache::Descriptor.new(files: entries)
|
|
1193
|
+
Cache::Descriptor.new(files: entries, globs: globs.uniq)
|
|
1194
|
+
end
|
|
1195
|
+
|
|
1196
|
+
# Issue #684 — the project files this run DISCOVERED but did not analyse. A widened run's diagnostics
|
|
1197
|
+
# are a function of them (that is the entire point: the method whose absence would have been reported
|
|
1198
|
+
# is defined in one of them), so the run-result cache must invalidate when one changes. Empty on every
|
|
1199
|
+
# run whose discovery expansion is its analysis expansion, which is every whole-project run.
|
|
1200
|
+
def discovery_file_entries(expansion)
|
|
1201
|
+
return [] if @project_discovery_expansion.nil?
|
|
1202
|
+
|
|
1203
|
+
analyzed = expansion.fetch(:files).to_set
|
|
1204
|
+
analyzed_file_entries(
|
|
1205
|
+
{ files: @project_discovery_expansion.fetch(:files).reject { |path| analyzed.include?(path) } }
|
|
1206
|
+
)
|
|
1093
1207
|
end
|
|
1094
1208
|
|
|
1095
1209
|
def analyzed_file_entries(expansion)
|
|
@@ -1185,12 +1299,18 @@ module Rigor
|
|
|
1185
1299
|
def apply_discovery_result(discovery)
|
|
1186
1300
|
@project_discovered_classes = discovery.discovered_classes
|
|
1187
1301
|
@project_discovered_def_nodes = discovery.discovered_def_nodes
|
|
1302
|
+
@project_discovered_def_nestings = discovery.discovered_def_nestings
|
|
1188
1303
|
@project_discovered_singleton_def_nodes = discovery.discovered_singleton_def_nodes
|
|
1189
1304
|
@project_discovered_def_sources = discovery.discovered_def_sources
|
|
1190
1305
|
@project_discovered_singleton_def_sources = discovery.discovered_singleton_def_sources
|
|
1191
1306
|
@project_discovered_superclasses = discovery.discovered_superclasses
|
|
1307
|
+
@project_discovered_header_nestings = discovery.discovered_header_nestings
|
|
1192
1308
|
@project_discovered_includes = discovery.discovered_includes
|
|
1193
1309
|
@project_discovered_class_sources = discovery.discovered_class_sources
|
|
1310
|
+
@project_constant_values = discovery.constant_values
|
|
1311
|
+
@published_constant_name_set = nil
|
|
1312
|
+
@project_constant_sources = discovery.constant_sources
|
|
1313
|
+
@project_constant_writes = discovery.constant_writes
|
|
1194
1314
|
@project_discovered_method_visibilities = discovery.discovered_method_visibilities
|
|
1195
1315
|
@project_discovered_methods = discovery.discovered_methods
|
|
1196
1316
|
@project_data_member_layouts = discovery.data_member_layouts
|
|
@@ -1210,6 +1330,8 @@ module Rigor
|
|
|
1210
1330
|
return if @project_discovery_done
|
|
1211
1331
|
|
|
1212
1332
|
@project_discovery_done = true
|
|
1333
|
+
# Issue #684 — the cross-file index spans the PROJECT even when the analysis targets a file list.
|
|
1334
|
+
expansion = @project_discovery_expansion || expansion
|
|
1213
1335
|
if @collect_seed_bundles
|
|
1214
1336
|
# ADR-85 WD2 — rebuild discovery from the prior run's bundles (re-walking only changed files) and
|
|
1215
1337
|
# capture the refreshed bundle set for the session to persist.
|
|
@@ -1222,6 +1344,39 @@ module Rigor
|
|
|
1222
1344
|
end
|
|
1223
1345
|
end
|
|
1224
1346
|
|
|
1347
|
+
# Issue #684 — the file set the cross-file DISCOVERY pre-pass walks, when that is wider than the set
|
|
1348
|
+
# being analysed. nil means "the same one", which is every whole-project run.
|
|
1349
|
+
#
|
|
1350
|
+
# `rigor check path/to/one_file.rb` — what an editor integration, a pre-commit hook and
|
|
1351
|
+
# `git diff --name-only | xargs rigor check` all do — discovered only the files it was handed, so a
|
|
1352
|
+
# class the project declares in `sig/` WITHOUT declaring every one of its methods drew a false
|
|
1353
|
+
# `call.undefined-method` for any method whose defining file was outside the invocation. The class was
|
|
1354
|
+
# RBS-known, source discovery could not supply the method, and the rule fired on correct code. The
|
|
1355
|
+
# same check over the containing DIRECTORY reported nothing, which is also why per-directory sums
|
|
1356
|
+
# over a tree over-reported against a whole-tree run (14 diagnostics of this shape against 2 during
|
|
1357
|
+
# the #652 work).
|
|
1358
|
+
#
|
|
1359
|
+
# Discovery is a parse pass, not an analysis: 0.55s over Rigor's own 444-file `lib` cold, and under
|
|
1360
|
+
# ADR-85 seed bundles a warm run re-walks only what changed. The alternative — standing the rule down
|
|
1361
|
+
# whenever evidence might be missing — buys the same FP safety by making `rigor check one_file.rb`
|
|
1362
|
+
# answer a different question than `rigor check .`, and the diagnostics for a file should not depend
|
|
1363
|
+
# on what else was on the command line.
|
|
1364
|
+
#
|
|
1365
|
+
# Excluded, each because its expansion is already the one it means: the LSP `prebuilt:` path (which
|
|
1366
|
+
# seeds discovery from a snapshot), editor `buffer:` mode, `run_source`'s in-memory single file, and
|
|
1367
|
+
# ADR-46's `analyze_only` subset mode (which already passes the full expansion and filters later).
|
|
1368
|
+
def project_discovery_expansion(paths, expansion)
|
|
1369
|
+
return nil unless widen_discovery_to_project?(paths)
|
|
1370
|
+
|
|
1371
|
+
widened = expand_paths(@configuration.paths | paths)
|
|
1372
|
+
widened.fetch(:files).size > expansion.fetch(:files).size ? widened : nil
|
|
1373
|
+
end
|
|
1374
|
+
|
|
1375
|
+
def widen_discovery_to_project?(paths)
|
|
1376
|
+
@prebuilt.nil? && @buffer.nil? && @in_memory_sources.nil? && @analyze_only.nil? &&
|
|
1377
|
+
paths != @configuration.paths
|
|
1378
|
+
end
|
|
1379
|
+
|
|
1225
1380
|
# ADR-46 — the dependency-recording and subset-analysis modes read the discovery tables OUTSIDE the
|
|
1226
1381
|
# analysis assembly (`Runner#symbol_fingerprints` / `#class_declarations`, consumed by
|
|
1227
1382
|
# {IncrementalSession} after the run), so they force the build eagerly — matching the pre-slice-1
|
|
@@ -1232,7 +1387,8 @@ module Rigor
|
|
|
1232
1387
|
end
|
|
1233
1388
|
|
|
1234
1389
|
private :run_project_pre_passes, :adopt_prebuilt_project_scan, :apply_pre_passes_result,
|
|
1235
|
-
:apply_discovery_result, :ensure_project_discovery, :force_eager_discovery
|
|
1390
|
+
:apply_discovery_result, :ensure_project_discovery, :force_eager_discovery?,
|
|
1391
|
+
:project_discovery_expansion, :widen_discovery_to_project?
|
|
1236
1392
|
|
|
1237
1393
|
# Ruby versions probed (ascending) to discover the lowest one this Prism build accepts for
|
|
1238
1394
|
# `version:`. Prism exposes no version list, so the floor is found empirically — only when a
|
|
@@ -1329,7 +1485,7 @@ module Rigor
|
|
|
1329
1485
|
# indexes, prepare-diagnostic snapshot, and the four end-of-pass snapshots) is reached through reader
|
|
1330
1486
|
# procs so each collaborator observes the live ivar value at call time without a back-reference
|
|
1331
1487
|
# cycle. The reporter accumulators and the {RunSnapshots} sink are shared mutable instances.
|
|
1332
|
-
def build_collaborators # rubocop:disable Metrics/MethodLength
|
|
1488
|
+
def build_collaborators # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
|
1333
1489
|
@pre_passes = ProjectPrePasses.new(
|
|
1334
1490
|
configuration: @configuration, cache_store: @cache_store, buffer: @buffer,
|
|
1335
1491
|
plugin_requirer: @plugin_requirer, pool_mode: -> { pool_mode? }
|
|
@@ -1363,6 +1519,8 @@ module Rigor
|
|
|
1363
1519
|
synthesized_namespaces_snapshot: -> { @snapshots.synthesized_namespaces },
|
|
1364
1520
|
quarantined_signatures_snapshot: -> { @snapshots.quarantined_signatures },
|
|
1365
1521
|
env_build_failure_snapshot: -> { @snapshots.env_build_failure },
|
|
1522
|
+
definition_build_failures_snapshot: -> { @snapshots.definition_build_failures },
|
|
1523
|
+
hkt_scan_failure_snapshot: -> { @snapshots.hkt_scan_failure },
|
|
1366
1524
|
conformance_results_snapshot: -> { @snapshots.conformance_results }
|
|
1367
1525
|
)
|
|
1368
1526
|
end
|
|
@@ -1581,6 +1739,9 @@ module Rigor
|
|
|
1581
1739
|
tables[:run_generation] = @run_generation if @run_generation
|
|
1582
1740
|
tables[:discovered_classes] = @project_discovered_classes unless @project_discovered_classes.empty?
|
|
1583
1741
|
tables[:discovered_def_nodes] = @project_discovered_def_nodes unless @project_discovered_def_nodes.empty?
|
|
1742
|
+
unless @project_discovered_def_nestings.empty?
|
|
1743
|
+
tables[:discovered_def_nestings] = @project_discovered_def_nestings
|
|
1744
|
+
end
|
|
1584
1745
|
unless @project_discovered_singleton_def_nodes.empty?
|
|
1585
1746
|
tables[:discovered_singleton_def_nodes] = @project_discovered_singleton_def_nodes
|
|
1586
1747
|
end
|
|
@@ -1588,6 +1749,9 @@ module Rigor
|
|
|
1588
1749
|
unless @project_discovered_superclasses.empty?
|
|
1589
1750
|
tables[:discovered_superclasses] = @project_discovered_superclasses
|
|
1590
1751
|
end
|
|
1752
|
+
unless @project_discovered_header_nestings.empty?
|
|
1753
|
+
tables[:discovered_header_nestings] = @project_discovered_header_nestings
|
|
1754
|
+
end
|
|
1591
1755
|
tables[:discovered_includes] = @project_discovered_includes unless @project_discovered_includes.empty?
|
|
1592
1756
|
unless @project_discovered_method_visibilities.empty?
|
|
1593
1757
|
tables[:discovered_method_visibilities] = @project_discovered_method_visibilities
|
|
@@ -1595,14 +1759,22 @@ module Rigor
|
|
|
1595
1759
|
tables[:discovered_methods] = @project_discovered_methods unless @project_discovered_methods.empty?
|
|
1596
1760
|
seed_opt_in_pre_pass_tables(tables)
|
|
1597
1761
|
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
|
|
1762
|
+
seed_dependency_attribution_tables(tables)
|
|
1603
1763
|
tables
|
|
1604
1764
|
end
|
|
1605
1765
|
|
|
1766
|
+
# ADR-46 slice 1 / issue #644 — the two SOURCE-ATTRIBUTION tables, read only by the recording accessors
|
|
1767
|
+
# (`Scope#record_class_dependency` for the class-declaration map, `Scope#record_constant_dependency`
|
|
1768
|
+
# for the constant-write map). A normal run carries neither. Extracted to keep
|
|
1769
|
+
# {#project_scope_seed_tables} under the complexity budget.
|
|
1770
|
+
def seed_dependency_attribution_tables(tables)
|
|
1771
|
+
return unless @record_dependencies
|
|
1772
|
+
|
|
1773
|
+
tables[:discovered_class_sources] = @project_discovered_class_sources unless
|
|
1774
|
+
@project_discovered_class_sources.empty?
|
|
1775
|
+
tables[:constant_sources] = @project_constant_sources unless @project_constant_sources.empty?
|
|
1776
|
+
end
|
|
1777
|
+
|
|
1606
1778
|
# Issue #260 — the mutable starting point {#project_scope_seed_tables} fills in: a copy of the opt-in
|
|
1607
1779
|
# `discovery_seed:` when one was supplied, otherwise the empty Hash every other run has always started
|
|
1608
1780
|
# from. Extracted so the seed costs {#project_scope_seed_tables} no branch of its complexity budget.
|
|
@@ -1622,9 +1794,33 @@ module Rigor
|
|
|
1622
1794
|
# Extracted to keep {#project_scope_seed_tables} under the complexity budget.
|
|
1623
1795
|
def seed_opt_in_pre_pass_tables(tables)
|
|
1624
1796
|
tables[:param_inferred_types] = @project_param_inferred_types unless @project_param_inferred_types.empty?
|
|
1625
|
-
|
|
1797
|
+
constants = merged_seed_constants
|
|
1798
|
+
return if constants.empty?
|
|
1799
|
+
|
|
1800
|
+
tables[:in_source_constants] = constants
|
|
1801
|
+
names = published_constant_name_set
|
|
1802
|
+
tables[:published_constant_names] = names unless names.empty?
|
|
1803
|
+
end
|
|
1804
|
+
|
|
1805
|
+
# Issue #644 — the seeded `in_source_constants` table, joining the two cross-file constant producers.
|
|
1806
|
+
# The whole-project literal table wins over a `pre_eval:` publication of the same name: both describe
|
|
1807
|
+
# the same assignment, the literal table pins the value the write actually carries while ADR-17's
|
|
1808
|
+
# widener erases it to the class, and listing a file under `pre_eval:` must never LOSE precision. The
|
|
1809
|
+
# per-file table still wins over both (`ScopeIndexer.index`'s merge) — same-file stays the most
|
|
1810
|
+
# specific authority.
|
|
1811
|
+
# Issue #644 — the LAST SEGMENTS of the published table, the run-wide half of
|
|
1812
|
+
# `Scope#published_constant?`. Seeded on EVERY run (unlike `constant_sources`, which only a recording
|
|
1813
|
+
# run needs) because {CheckRules::PublishedConstantGuard} asks the question on every `if` / `unless`.
|
|
1814
|
+
def published_constant_name_set
|
|
1815
|
+
@published_constant_name_set ||=
|
|
1816
|
+
@project_constant_values.keys.to_set { |name| name.split("::").last }.freeze
|
|
1817
|
+
end
|
|
1818
|
+
|
|
1819
|
+
def merged_seed_constants
|
|
1820
|
+
return @project_constant_values if @project_pre_eval_constants.empty?
|
|
1821
|
+
return @project_pre_eval_constants if @project_constant_values.empty?
|
|
1626
1822
|
|
|
1627
|
-
|
|
1823
|
+
@project_pre_eval_constants.merge(@project_constant_values).freeze
|
|
1628
1824
|
end
|
|
1629
1825
|
|
|
1630
1826
|
# ADR-46 — seed the instance + singleton `"path:line"` def-source tables (each only when non-empty).
|
|
@@ -1739,7 +1935,7 @@ module Rigor
|
|
|
1739
1935
|
path: path,
|
|
1740
1936
|
line: 1,
|
|
1741
1937
|
column: 1,
|
|
1742
|
-
message:
|
|
1938
|
+
message: CrashSignature.check_rule_message(e),
|
|
1743
1939
|
severity: :error
|
|
1744
1940
|
)
|
|
1745
1941
|
]
|
|
@@ -16,6 +16,7 @@ require_relative "../effects/envelope_index"
|
|
|
16
16
|
require_relative "../inference/scope_indexer"
|
|
17
17
|
require_relative "../inference/method_dispatcher/file_folding"
|
|
18
18
|
require_relative "check_rules"
|
|
19
|
+
require_relative "crash_signature"
|
|
19
20
|
require_relative "dependency_recorder"
|
|
20
21
|
require_relative "dependency_source_inference"
|
|
21
22
|
require_relative "diagnostic"
|
|
@@ -228,21 +229,54 @@ module Rigor
|
|
|
228
229
|
rescue Errno::ENOENT => e
|
|
229
230
|
[analyzer_error(path, e.message)]
|
|
230
231
|
rescue StandardError => e
|
|
231
|
-
[analyzer_error(path,
|
|
232
|
+
[analyzer_error(path, CrashSignature.check_rule_message(e))]
|
|
232
233
|
end
|
|
233
234
|
private :analyze_body
|
|
234
235
|
|
|
235
236
|
# Read-once snapshot of the per-worker reporters so the caller (or the eventual Phase 4b pool
|
|
236
237
|
# aggregator) can merge into a single coordinator-side reporter. Both reporters dedupe at write time,
|
|
237
238
|
# so a post-hoc concat + de-dup at the entry-key level is sound.
|
|
239
|
+
#
|
|
240
|
+
# Issue #696 — `definition_build_failures` rides this channel rather than a coordinator-side snapshot,
|
|
241
|
+
# and it has to. Under the pool the PARENT never analyses a file, so its loader never demands a
|
|
242
|
+
# definition and never enters the failing build; a diagnostic wired off the parent's loader would
|
|
243
|
+
# appear at `--workers=0` and vanish at `--workers=N`, which is the same "reports less depending on
|
|
244
|
+
# how you ran it" defect the diagnostic exists to end. Draining it out of the workers is what makes
|
|
245
|
+
# the two paths say the same thing. The payload is `[String, String, String, Array<String>]` tuples —
|
|
246
|
+
# Marshal-clean for the fork backend and shareable for the Ractor one.
|
|
247
|
+
#
|
|
248
|
+
# Issue #784 — `hkt_scan_failure` rides the same channel for the same reason: the PARENT's own
|
|
249
|
+
# Environment never demands `#hkt_registry` under the pool, so its slot is always nil, and draining it
|
|
250
|
+
# out of the workers is the only way `--workers=N` says what `--workers=0` says.
|
|
251
|
+
#
|
|
252
|
+
# Issue #805 — the Marshal-clean requirement binds every stream here, not just the ones written for it.
|
|
253
|
+
# The `RbsExtended::Reporter`'s unresolved / lossy-projection entries used to carry the `RBS::Location`
|
|
254
|
+
# itself, and `Marshal.dump` on one raises `TypeError` (a C-extension object with no `_dump`), so a
|
|
255
|
+
# project whose `sig/` produced a single such event killed each worker HERE — after its files were
|
|
256
|
+
# analysed — and the run degraded to in-process re-analysis with a `pool-degraded` row. All three
|
|
257
|
+
# streams now carry `(path, line, column)` primitives.
|
|
238
258
|
def drain_reporters
|
|
259
|
+
# Issue #784 — demand the registry once per worker before reading the slot, so a worker whose share
|
|
260
|
+
# of files happened to contain no `Klass.method` call still reports the run's outcome (the same
|
|
261
|
+
# reason `PoolCoordinator#hkt_scan_outcome` demands it on the sequential path). The Environment
|
|
262
|
+
# object is built in the constructor, but its RBS env is lazy: on a worker that analysed a file this
|
|
263
|
+
# is a memoised read; on one that demanded nothing it is the same cache-served env load that file
|
|
264
|
+
# would have paid. The fork backend never hands a worker an empty slice, so no idle worker pays it.
|
|
265
|
+
@environment&.hkt_registry
|
|
239
266
|
{
|
|
240
267
|
rbs_extended: {
|
|
241
268
|
unresolved_payloads: @rbs_extended_reporter.unresolved_payloads,
|
|
242
|
-
lossy_projections: @rbs_extended_reporter.lossy_projections
|
|
269
|
+
lossy_projections: @rbs_extended_reporter.lossy_projections,
|
|
270
|
+
# Issue #785 — the HKT directive stream rides the same channel for the same reason
|
|
271
|
+
# `hkt_scan_failure` does: the directives are read by the registry scan, and under the pool the
|
|
272
|
+
# PARENT never demands that scan, so a diagnostic wired off the parent's reporter would appear
|
|
273
|
+
# at `--workers=0` and vanish at `--workers=N`. The demand above is what fills this.
|
|
274
|
+
hkt_directive_errors: @rbs_extended_reporter.hkt_directive_errors
|
|
243
275
|
},
|
|
244
276
|
boundary_cross: @boundary_cross_reporter.entries,
|
|
245
|
-
source_rbs_synthesis: @source_rbs_synthesis_reporter.entries
|
|
277
|
+
source_rbs_synthesis: @source_rbs_synthesis_reporter.entries,
|
|
278
|
+
definition_build_failures: @environment&.rbs_loader&.definition_build_failures || [],
|
|
279
|
+
hkt_scan_failure: @environment&.hkt_scan_failure
|
|
246
280
|
}
|
|
247
281
|
end
|
|
248
282
|
|
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
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "strscan"
|
|
4
4
|
|
|
5
|
+
require_relative "../rbs_extended/reporter"
|
|
5
6
|
require_relative "../type"
|
|
6
7
|
require_relative "../type_node"
|
|
7
8
|
|
|
@@ -558,11 +559,14 @@ module Rigor
|
|
|
558
559
|
result
|
|
559
560
|
end
|
|
560
561
|
|
|
561
|
-
# ADR-13 slice 3b — record one `dynamic.shape.lossy-projection` event per (head,
|
|
562
|
-
#
|
|
563
|
-
#
|
|
564
|
-
# `
|
|
565
|
-
#
|
|
562
|
+
# ADR-13 slice 3b — record one `dynamic.shape.lossy-projection` event per (head, position) pair when
|
|
563
|
+
# the projection actually degraded. The builders return the source carrier unchanged on
|
|
564
|
+
# non-HashShape / non-Tuple receivers (see `Type::Combinator.pick_of` / `omit_of` and the
|
|
565
|
+
# HashShape-only `partial_of` / `required_of` / `readonly_of`), so detection is "first arg was lossy".
|
|
566
|
+
#
|
|
567
|
+
# Issue #805 — the `RBS::Location` is flattened to `(path, line, column)` here, at record time: the
|
|
568
|
+
# entry is drained out of every fork-pool worker, and a location survives neither the drain's
|
|
569
|
+
# `Marshal.dump` nor its cross-worker dedup (see {Rigor::RbsExtended::Reporter}).
|
|
566
570
|
def record_lossy_projection_if_applicable(node, args, result)
|
|
567
571
|
return if @reporter.nil?
|
|
568
572
|
return if result.nil?
|
|
@@ -570,10 +574,8 @@ module Rigor
|
|
|
570
574
|
return if args.empty?
|
|
571
575
|
return unless Type::Combinator.shape_projection_lossy?(args.first)
|
|
572
576
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
source_location: @source_location
|
|
576
|
-
)
|
|
577
|
+
path, line, column = RbsExtended::Reporter.position_of(@source_location)
|
|
578
|
+
@reporter.record_lossy_projection(head: node.head, path: path, line: line, column: column)
|
|
577
579
|
end
|
|
578
580
|
|
|
579
581
|
def try_parametric_int_builder(node)
|