rigortype 0.4.2 → 0.4.3

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.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/docs/handbook/11-sig-gen.md +12 -5
  4. data/docs/manual/02-cli-reference.md +19 -1
  5. data/docs/manual/03-configuration.md +6 -4
  6. data/docs/manual/05-inspecting-types.md +50 -0
  7. data/docs/manual/12-caching.md +18 -1
  8. data/docs/manual/14-rails-quickstart.md +2 -2
  9. data/docs/manual/18-removing-dead-code.md +16 -9
  10. data/lib/rigor/analysis/check_rules/rule_ids.rb +7 -0
  11. data/lib/rigor/analysis/check_rules.rb +23 -22
  12. data/lib/rigor/analysis/effects_cache_probe.rb +1 -1
  13. data/lib/rigor/analysis/incremental_run_slot.rb +3 -3
  14. data/lib/rigor/analysis/incremental_session.rb +89 -22
  15. data/lib/rigor/analysis/path_expansion.rb +62 -6
  16. data/lib/rigor/analysis/plugin_fact_fingerprint.rb +124 -37
  17. data/lib/rigor/analysis/reachability/graph.rb +119 -15
  18. data/lib/rigor/analysis/reachability/scan.rb +332 -173
  19. data/lib/rigor/analysis/reachability/scan_cache.rb +4 -2
  20. data/lib/rigor/analysis/run_cache_probe.rb +1 -1
  21. data/lib/rigor/analysis/run_stats.rb +28 -6
  22. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +26 -1
  23. data/lib/rigor/analysis/runner/pool_coordinator.rb +28 -9
  24. data/lib/rigor/analysis/runner/project_pre_passes.rb +21 -1
  25. data/lib/rigor/analysis/runner.rb +29 -11
  26. data/lib/rigor/analysis/worker_session.rb +1 -1
  27. data/lib/rigor/cache/engine_source.rb +21 -7
  28. data/lib/rigor/cache/incremental_snapshot.rb +36 -8
  29. data/lib/rigor/cli/check_command.rb +138 -39
  30. data/lib/rigor/cli/docs_command.rb +5 -4
  31. data/lib/rigor/cli/lens_command.rb +144 -0
  32. data/lib/rigor/cli/lsp_command.rb +5 -2
  33. data/lib/rigor/cli/skill_command.rb +3 -3
  34. data/lib/rigor/cli/skill_describe.rb +1 -1
  35. data/lib/rigor/cli/type_of_command.rb +2 -1
  36. data/lib/rigor/cli/unused_command.rb +157 -24
  37. data/lib/rigor/cli.rb +30 -4
  38. data/lib/rigor/configuration.rb +15 -0
  39. data/lib/rigor/environment/bundle_sig_discovery.rb +11 -7
  40. data/lib/rigor/environment/installed_gem_set.rb +38 -11
  41. data/lib/rigor/environment/lockfile_resolver.rb +37 -6
  42. data/lib/rigor/environment/missing_gem_constant_index.rb +22 -7
  43. data/lib/rigor/environment/rbs_loader.rb +33 -0
  44. data/lib/rigor/environment/required_features.rb +4 -2
  45. data/lib/rigor/environment.rb +12 -5
  46. data/lib/rigor/inference/builtins/cruby_definers.rb +22 -5
  47. data/lib/rigor/inference/captured_locals.rb +43 -3
  48. data/lib/rigor/inference/expression_typer.rb +58 -25
  49. data/lib/rigor/inference/in_effect_refinements/scope_reads.rb +21 -17
  50. data/lib/rigor/inference/match_rebinding.rb +1 -1
  51. data/lib/rigor/inference/method_dispatcher/argument_nodes.rb +25 -0
  52. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +3 -2
  53. data/lib/rigor/inference/method_dispatcher/match_data_folding.rb +2 -2
  54. data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +3 -3
  55. data/lib/rigor/inference/method_dispatcher/optional_count.rb +63 -0
  56. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +4 -16
  57. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +4 -7
  58. data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +8 -26
  59. data/lib/rigor/inference/method_dispatcher/unit_denominator_rational.rb +67 -0
  60. data/lib/rigor/inference/method_dispatcher.rb +52 -3
  61. data/lib/rigor/inference/project_patched_definedness_guard.rb +86 -0
  62. data/lib/rigor/inference/project_patched_methods.rb +2 -2
  63. data/lib/rigor/inference/project_patched_scanner.rb +38 -24
  64. data/lib/rigor/inference/refined_dispatch/class_object.rb +350 -0
  65. data/lib/rigor/inference/refined_dispatch/own_method.rb +112 -40
  66. data/lib/rigor/inference/refined_dispatch.rb +62 -14
  67. data/lib/rigor/inference/repeated_or_writes.rb +2 -0
  68. data/lib/rigor/inference/scope_indexer.rb +140 -5
  69. data/lib/rigor/inference/statement_evaluator.rb +223 -31
  70. data/lib/rigor/inference/untyped_rejoin.rb +75 -0
  71. data/lib/rigor/language_server/project_context.rb +56 -5
  72. data/lib/rigor/lens/anchor.rb +115 -0
  73. data/lib/rigor/lens/contract_reader.rb +118 -0
  74. data/lib/rigor/lens/document.rb +113 -0
  75. data/lib/rigor/lens/file_reader.rb +433 -0
  76. data/lib/rigor/lens/members.rb +153 -0
  77. data/lib/rigor/lens/parameters.rb +91 -0
  78. data/lib/rigor/lens/plugins.rb +40 -0
  79. data/lib/rigor/lens/run.rb +280 -0
  80. data/lib/rigor/lens/slot.rb +79 -0
  81. data/lib/rigor/lens.rb +40 -0
  82. data/lib/rigor/project_environment.rb +20 -1
  83. data/lib/rigor/protection/mutation_cache.rb +2 -1
  84. data/lib/rigor/scope.rb +22 -11
  85. data/lib/rigor/sig_gen/generator.rb +45 -5
  86. data/lib/rigor/version.rb +1 -1
  87. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +29 -3
  88. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +5 -1
  89. data/sig/rigor/scope.rbs +3 -0
  90. data/sig/rigor.rbs +6 -3
  91. data/skills/rigor-project-init/references/02-configure.md +2 -2
  92. metadata +18 -1
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rigor/source/node_children"
4
+ require "rigor/source/symbol_name"
4
5
 
5
6
  require "prism"
6
7
 
@@ -81,11 +82,16 @@ module Rigor
81
82
  # fixed point rather than recursing keeps a cyclic `class A < B; class B < A` source — which Ruby
82
83
  # rejects but a static parse can still be handed — from recursing forever.
83
84
  def close_over_roots(candidates)
84
- by_name = candidates.to_h { |entry| [entry.class_name, entry] }
85
+ groups = candidates.group_by(&:class_name)
86
+ # Files that name different superclasses for one class are a TypeError in Ruby, so no ancestry
87
+ # reading of that class is correct. It is declined outright, and so is everything beneath it.
88
+ conflicted = groups.filter_map { |name, group| name if group.map(&:superclass_name).uniq.size > 1 }.to_set
89
+ by_name = groups.except(*conflicted).transform_values { |group| merge_reopened(group) }
85
90
  reached = @base_classes.to_set
86
91
  loop do
87
92
  grown = candidates.select do |entry|
88
- !reached.include?(entry.class_name) && reached.include?(entry.superclass_name)
93
+ !conflicted.include?(entry.class_name) && !reached.include?(entry.class_name) &&
94
+ reached.include?(entry.superclass_name)
89
95
  end
90
96
  break if grown.empty?
91
97
 
@@ -94,6 +100,21 @@ module Rigor
94
100
  reached.filter_map { |name| by_name[name] }
95
101
  end
96
102
 
103
+ # A class declared in several files that each name its superclass is one class to Ruby, so its names
104
+ # are the union of every file's. Keeping only the last file's entry let a sibling that declared nothing
105
+ # usable shadow the valid one. The `own_method_names` union assumes every reopening file is loaded; a
106
+ # file Zeitwerk shadows is not modelled. A superclass-less reopening is not a candidate (see visit_class).
107
+ def merge_reopened(entries)
108
+ return entries.first if entries.size == 1
109
+
110
+ first = entries.first
111
+ first.with(
112
+ declared_names: entries.flat_map(&:declared_names).uniq.sort.freeze,
113
+ object_reads: entries.flat_map(&:object_reads).uniq.sort.freeze,
114
+ own_method_names: entries.flat_map(&:own_method_names).uniq.sort.freeze
115
+ )
116
+ end
117
+
97
118
  def read_safely(path)
98
119
  @io_boundary.read_file(path)
99
120
  rescue Plugin::AccessDeniedError, Errno::ENOENT
@@ -180,9 +201,14 @@ module Rigor
180
201
  receiver.arguments.nil?
181
202
  end
182
203
 
204
+ # A name whose bytes no Symbol can hold (`attributes :"\xff"` in a file that does not parse cleanly)
205
+ # is dropped here, so it never reaches the index or the `project_defines?` lookup that would raise.
206
+ # The file's other names are kept: a recoverable syntax error elsewhere in it does not void them.
183
207
  def symbol_arguments(node)
184
208
  (node.arguments&.arguments || []).filter_map do |argument|
185
- argument.unescaped if argument.is_a?(Prism::SymbolNode)
209
+ next unless argument.is_a?(Prism::SymbolNode)
210
+
211
+ Rigor::Source::SymbolName.of(argument.unescaped)&.to_s
186
212
  end
187
213
  end
188
214
 
@@ -239,7 +239,11 @@ module Rigor
239
239
  def project_defines?(class_name, method_name, scope)
240
240
  return false unless scope.respond_to?(:user_def_through_ancestors)
241
241
 
242
- !scope.user_def_through_ancestors(class_name, method_name.to_sym).first.nil?
242
+ # A name no Symbol can hold cannot be a method the project defines (#1831): decline, do not raise.
243
+ method = Rigor::Source::SymbolName.of(method_name.to_s)
244
+ return false if method.nil?
245
+
246
+ !scope.user_def_through_ancestors(class_name, method).first.nil?
243
247
  end
244
248
 
245
249
  # `REST::AccountSerializer` offers two readings — the namespaced `REST::Account` and the
data/sig/rigor/scope.rbs CHANGED
@@ -247,6 +247,7 @@ module Rigor
247
247
  def optimistic_local_miss: (String | Symbol name) -> Object?
248
248
  def optimistic_ivar_miss: (String | Symbol name) -> Object?
249
249
  def with_local: (String | Symbol name, Type::t type) -> Scope
250
+ def with_rejoined_locals: (untyped) -> Rigor::Scope
250
251
  def with_ivar: (String | Symbol name, Type::t type) -> Scope
251
252
  def with_local_origin: (String | Symbol name, Symbol? cause) -> Scope
252
253
  def with_optimistic_local: (String | Symbol name, Symbol? cause, ?miss: Object?) -> Scope
@@ -299,6 +300,8 @@ module Rigor
299
300
  def inferred_param?: (String | Symbol name) -> bool
300
301
  def forget_match_globals: () -> Scope
301
302
  def untyped_match_globals: () -> Scope
303
+ # sig-gen gap: #1154 — the reader over the `@globals` ivar `initialize` assigns from a parameter reaches
304
+ # `untyped`, so the `bool` is hand-written.
302
305
  def match_globals_bound?: () -> bool
303
306
  def with_match_frame: (untyped body, ?untyped parameters) -> Scope
304
307
  # sig-gen gap: #1154 — the frame reader over an ivar assigned in `initialize` from a parameter reaches
data/sig/rigor.rbs CHANGED
@@ -83,7 +83,6 @@ module Rigor
83
83
 
84
84
  class RunStats
85
85
  attr_reader wall_seconds: Float
86
- attr_reader peak_rss_bytes: Integer?
87
86
  attr_reader target_files: Integer
88
87
  attr_reader rbs_classes_total: Integer
89
88
  attr_reader rbs_classes_project_sig: Integer
@@ -93,21 +92,25 @@ module Rigor
93
92
  attr_reader rbs_attribution_available: bool
94
93
 
95
94
  CACHED_SENTINEL: String
95
+ CURRENT_PROCESS_PEAK_RSS: ^() -> Integer?
96
96
 
97
+ # authored: #1529 memoised lazy read; its type enters only through the deliberate `peak_rss_probe:` initialize parameter.
98
+ def peak_rss_bytes: () -> Integer?
97
99
  def self.peak_rss_bytes: () -> Integer?
98
100
  def self.partition_classes: (class_decl_paths: Hash[String, String], signature_paths: Array[untyped]) -> [Integer, Integer]
99
101
  def self.attribution_available?: (class_decl_paths: Hash[String, String]) -> bool
100
102
 
101
103
  def initialize: (
102
104
  wall_seconds: Float,
103
- peak_rss_bytes: Integer?,
104
105
  target_files: Integer,
105
106
  rbs_classes_total: Integer,
106
107
  rbs_classes_project_sig: Integer,
107
108
  rbs_classes_bundled: Integer,
108
109
  gem_walk_classes: Integer,
109
110
  gem_walk_gems: Integer,
110
- ?rbs_attribution_available: bool
111
+ ?rbs_attribution_available: bool,
112
+ ?peak_rss_bytes: Integer?,
113
+ ?peak_rss_probe: ^() -> Integer?
111
114
  ) -> void
112
115
  def format: (untyped out, ?prefix: String) -> void
113
116
  def to_h: () -> Hash[Symbol, untyped]
@@ -97,8 +97,8 @@ test_paths:
97
97
  - spec
98
98
 
99
99
  exclude:
100
- - vendor
101
- - tmp
100
+ - "vendor/**"
101
+ - "tmp/**"
102
102
 
103
103
  plugins:
104
104
  - rigor-actionpack
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rigortype
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rigor contributors
@@ -564,6 +564,7 @@ files:
564
564
  - lib/rigor/cli/explain_command.rb
565
565
  - lib/rigor/cli/fused_protection_renderer.rb
566
566
  - lib/rigor/cli/fused_protection_report.rb
567
+ - lib/rigor/cli/lens_command.rb
567
568
  - lib/rigor/cli/lsp_command.rb
568
569
  - lib/rigor/cli/mcp_command.rb
569
570
  - lib/rigor/cli/measurement_integrity_warning.rb
@@ -756,6 +757,7 @@ files:
756
757
  - lib/rigor/inference/match_rebinding/self_calls.rb
757
758
  - lib/rigor/inference/method_dispatcher.rb
758
759
  - lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb
760
+ - lib/rigor/inference/method_dispatcher/argument_nodes.rb
759
761
  - lib/rigor/inference/method_dispatcher/arithmetic_sequence_elements.rb
760
762
  - lib/rigor/inference/method_dispatcher/array_to_h_folding.rb
761
763
  - lib/rigor/inference/method_dispatcher/block_folding.rb
@@ -777,6 +779,7 @@ files:
777
779
  - lib/rigor/inference/method_dispatcher/math_folding.rb
778
780
  - lib/rigor/inference/method_dispatcher/member_shape_projection.rb
779
781
  - lib/rigor/inference/method_dispatcher/method_folding.rb
782
+ - lib/rigor/inference/method_dispatcher/optional_count.rb
780
783
  - lib/rigor/inference/method_dispatcher/overload_selector.rb
781
784
  - lib/rigor/inference/method_dispatcher/process_folding.rb
782
785
  - lib/rigor/inference/method_dispatcher/proven_overload.rb
@@ -795,6 +798,7 @@ files:
795
798
  - lib/rigor/inference/method_dispatcher/struct_folding.rb
796
799
  - lib/rigor/inference/method_dispatcher/struct_materialization.rb
797
800
  - lib/rigor/inference/method_dispatcher/time_folding.rb
801
+ - lib/rigor/inference/method_dispatcher/unit_denominator_rational.rb
798
802
  - lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb
799
803
  - lib/rigor/inference/method_dispatcher/uri_folding.rb
800
804
  - lib/rigor/inference/method_parameter_binder.rb
@@ -812,6 +816,7 @@ files:
812
816
  - lib/rigor/inference/pre_eval_constants.rb
813
817
  - lib/rigor/inference/precision_scanner.rb
814
818
  - lib/rigor/inference/project_method_ownership.rb
819
+ - lib/rigor/inference/project_patched_definedness_guard.rb
815
820
  - lib/rigor/inference/project_patched_methods.rb
816
821
  - lib/rigor/inference/project_patched_scanner.rb
817
822
  - lib/rigor/inference/protection_scanner.rb
@@ -822,6 +827,7 @@ files:
822
827
  - lib/rigor/inference/refine_census.rb
823
828
  - lib/rigor/inference/refine_self.rb
824
829
  - lib/rigor/inference/refined_dispatch.rb
830
+ - lib/rigor/inference/refined_dispatch/class_object.rb
825
831
  - lib/rigor/inference/refined_dispatch/own_method.rb
826
832
  - lib/rigor/inference/refinement_mutation.rb
827
833
  - lib/rigor/inference/repeated_or_writes.rb
@@ -846,6 +852,7 @@ files:
846
852
  - lib/rigor/inference/synthetic_method_scanner.rb
847
853
  - lib/rigor/inference/unknown_store_widening.rb
848
854
  - lib/rigor/inference/unthreaded_rebinds.rb
855
+ - lib/rigor/inference/untyped_rejoin.rb
849
856
  - lib/rigor/inference/version_guard.rb
850
857
  - lib/rigor/inference/void_origin.rb
851
858
  - lib/rigor/inference/void_tail_summary.rb
@@ -868,6 +875,16 @@ files:
868
875
  - lib/rigor/language_server/signature_help_provider.rb
869
876
  - lib/rigor/language_server/synchronized_writer.rb
870
877
  - lib/rigor/language_server/uri.rb
878
+ - lib/rigor/lens.rb
879
+ - lib/rigor/lens/anchor.rb
880
+ - lib/rigor/lens/contract_reader.rb
881
+ - lib/rigor/lens/document.rb
882
+ - lib/rigor/lens/file_reader.rb
883
+ - lib/rigor/lens/members.rb
884
+ - lib/rigor/lens/parameters.rb
885
+ - lib/rigor/lens/plugins.rb
886
+ - lib/rigor/lens/run.rb
887
+ - lib/rigor/lens/slot.rb
871
888
  - lib/rigor/mcp.rb
872
889
  - lib/rigor/mcp/loop.rb
873
890
  - lib/rigor/mcp/server.rb