rigortype 0.4.1 → 0.4.2

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 (182) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/data/builtins/ruby_core/io.yml +1 -0
  4. data/data/builtins/ruby_core/proc.yml +4 -0
  5. data/data/builtins/ruby_core/random.yml +4 -0
  6. data/data/builtins/ruby_core/struct.yml +3 -0
  7. data/data/core_overlay/env.rbs +34 -0
  8. data/data/core_overlay/gc.rbs +50 -0
  9. data/data/core_overlay/hash.rbs +45 -0
  10. data/data/core_overlay/integer.rbs +31 -0
  11. data/data/core_overlay/io_buffer.rbs +27 -0
  12. data/data/core_overlay/kernel.rbs +40 -0
  13. data/data/core_overlay/match_data.rbs +28 -0
  14. data/data/core_overlay/module.rbs +51 -0
  15. data/data/core_overlay/object_space.rbs +29 -0
  16. data/data/core_overlay/proc.rbs +45 -0
  17. data/data/core_overlay/range.rbs +40 -0
  18. data/data/core_overlay/source_range.rbs +78 -0
  19. data/data/core_overlay/string.rbs +86 -0
  20. data/data/vendored_gem_sigs/prime/integer-extension.rbs +41 -0
  21. data/data/vendored_gem_sigs/prime/prime.rbs +372 -0
  22. data/docs/handbook/01-getting-started.md +3 -3
  23. data/docs/handbook/06-classes.md +3 -1
  24. data/docs/handbook/11-sig-gen.md +6 -0
  25. data/docs/handbook/appendix-liskov.md +5 -0
  26. data/docs/manual/03-configuration.md +1 -1
  27. data/docs/manual/04-diagnostics.md +2 -1
  28. data/docs/manual/06-baseline.md +2 -0
  29. data/docs/manual/07-plugins.md +1 -1
  30. data/docs/manual/18-removing-dead-code.md +32 -2
  31. data/docs/manual/plugins/README.md +6 -1
  32. data/docs/manual/plugins/rigor-activerecord.md +21 -0
  33. data/docs/manual/plugins/rigor-alba.md +60 -0
  34. data/docs/manual/plugins/rigor-minitest.md +22 -4
  35. data/docs/manual/plugins/rigor-typelizer.md +71 -0
  36. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +27 -3
  37. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +21 -92
  38. data/lib/rigor/analysis/check_rules/ruby_deprecations.rb +275 -0
  39. data/lib/rigor/analysis/check_rules/rule_ids.rb +4 -0
  40. data/lib/rigor/analysis/check_rules/source_arity.rb +21 -2
  41. data/lib/rigor/analysis/check_rules.rb +289 -57
  42. data/lib/rigor/analysis/dependency_recorder.rb +15 -0
  43. data/lib/rigor/analysis/dependency_source_inference/builder.rb +8 -1
  44. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +21 -0
  45. data/lib/rigor/analysis/dependency_source_inference/index.rb +36 -11
  46. data/lib/rigor/analysis/dependency_source_inference/walker.rb +196 -27
  47. data/lib/rigor/analysis/erb_template_detector.rb +2 -1
  48. data/lib/rigor/analysis/incremental.rb +29 -9
  49. data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +30 -5
  50. data/lib/rigor/analysis/incremental_run_slot.rb +3 -1
  51. data/lib/rigor/analysis/incremental_session.rb +14 -1
  52. data/lib/rigor/analysis/reachability/graph.rb +123 -23
  53. data/lib/rigor/analysis/reachability/scan.rb +290 -36
  54. data/lib/rigor/analysis/rule_catalog.rb +43 -0
  55. data/lib/rigor/analysis/run_cache_key.rb +16 -1
  56. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +87 -5
  57. data/lib/rigor/analysis/runner/pool_coordinator.rb +50 -11
  58. data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
  59. data/lib/rigor/analysis/runner.rb +34 -3
  60. data/lib/rigor/analysis/worker_session.rb +7 -4
  61. data/lib/rigor/cache/incremental_snapshot.rb +22 -0
  62. data/lib/rigor/cli/annotate_command.rb +5 -0
  63. data/lib/rigor/cli/baseline_command.rb +7 -18
  64. data/lib/rigor/cli/check_command.rb +2 -1
  65. data/lib/rigor/cli/trace_command.rb +5 -0
  66. data/lib/rigor/cli/type_of_command.rb +5 -3
  67. data/lib/rigor/cli/unused_command.rb +13 -7
  68. data/lib/rigor/cli.rb +10 -2
  69. data/lib/rigor/configuration/severity_profile.rb +6 -0
  70. data/lib/rigor/configuration.rb +46 -2
  71. data/lib/rigor/environment/gated_signature_guard.rb +43 -0
  72. data/lib/rigor/environment/gated_signature_plan.rb +339 -0
  73. data/lib/rigor/environment/rbs_coverage_report.rb +1 -1
  74. data/lib/rigor/environment/rbs_loader.rb +283 -14
  75. data/lib/rigor/environment/required_features.rb +206 -0
  76. data/lib/rigor/environment.rb +21 -3
  77. data/lib/rigor/inference/acceptance.rb +28 -3
  78. data/lib/rigor/inference/builtins/cruby_definers.rb +61 -0
  79. data/lib/rigor/inference/builtins/method_catalog.rb +3 -0
  80. data/lib/rigor/inference/def_node_resolver.rb +23 -1
  81. data/lib/rigor/inference/expression_typer.rb +215 -16
  82. data/lib/rigor/inference/global_write_census.rb +7 -4
  83. data/lib/rigor/inference/guard_rebinding.rb +1 -1
  84. data/lib/rigor/inference/in_effect_refinements/proc_literals.rb +83 -0
  85. data/lib/rigor/inference/in_effect_refinements/refine_defs.rb +54 -0
  86. data/lib/rigor/inference/in_effect_refinements/scope_reads.rb +282 -0
  87. data/lib/rigor/inference/in_effect_refinements/walk.rb +276 -0
  88. data/lib/rigor/inference/in_effect_refinements.rb +256 -0
  89. data/lib/rigor/inference/key_presence_guard/scan.rb +281 -0
  90. data/lib/rigor/inference/key_presence_guard.rb +303 -0
  91. data/lib/rigor/inference/macro_block_self_type.rb +59 -12
  92. data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
  93. data/lib/rigor/inference/method_dispatcher/arithmetic_sequence_elements.rb +73 -0
  94. data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +68 -0
  95. data/lib/rigor/inference/method_dispatcher/integer_step_block_params.rb +81 -0
  96. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +6 -0
  97. data/lib/rigor/inference/method_dispatcher/keyword_arguments.rb +267 -0
  98. data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -1
  99. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +122 -105
  100. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +259 -28
  101. data/lib/rigor/inference/method_dispatcher/splat_arity.rb +71 -0
  102. data/lib/rigor/inference/method_dispatcher.rb +44 -11
  103. data/lib/rigor/inference/module_function_state.rb +2 -1
  104. data/lib/rigor/inference/narrowing.rb +20 -6
  105. data/lib/rigor/inference/object_mixins.rb +180 -0
  106. data/lib/rigor/inference/project_patched_methods.rb +10 -5
  107. data/lib/rigor/inference/project_patched_scanner.rb +71 -3
  108. data/lib/rigor/inference/refine_census.rb +239 -0
  109. data/lib/rigor/inference/refine_self.rb +317 -0
  110. data/lib/rigor/inference/refined_dispatch/own_method.rb +403 -0
  111. data/lib/rigor/inference/refined_dispatch.rb +272 -0
  112. data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +13 -0
  113. data/lib/rigor/inference/scope_indexer.rb +687 -60
  114. data/lib/rigor/inference/statement_evaluator.rb +71 -20
  115. data/lib/rigor/inference/string_mutation.rb +9 -3
  116. data/lib/rigor/inference/synthetic_method_scanner.rb +2 -1
  117. data/lib/rigor/inference/version_guard.rb +19 -10
  118. data/lib/rigor/language_server/project_context.rb +31 -0
  119. data/lib/rigor/language_server/server.rb +18 -0
  120. data/lib/rigor/plugin/inflector.rb +1 -1
  121. data/lib/rigor/plugin/io_boundary.rb +42 -3
  122. data/lib/rigor/plugin/macro/block_as_method.rb +51 -8
  123. data/lib/rigor/project_environment.rb +29 -6
  124. data/lib/rigor/protection/mutation_cache.rb +1 -1
  125. data/lib/rigor/protection/mutator.rb +1 -1
  126. data/lib/rigor/reflection/constant_ancestors.rb +127 -11
  127. data/lib/rigor/reflection/constant_path.rb +13 -2
  128. data/lib/rigor/reflection.rb +40 -8
  129. data/lib/rigor/scope/discovery_index.rb +40 -1
  130. data/lib/rigor/scope/resolution_chain.rb +159 -11
  131. data/lib/rigor/scope.rb +189 -10
  132. data/lib/rigor/sig_gen/classification.rb +4 -1
  133. data/lib/rigor/sig_gen/generator.rb +55 -5
  134. data/lib/rigor/sig_gen/method_candidate.rb +2 -2
  135. data/lib/rigor/sig_gen/skip_reason_catalog.rb +12 -0
  136. data/lib/rigor/source/alias_names.rb +52 -0
  137. data/lib/rigor/source/literals.rb +3 -2
  138. data/lib/rigor/source/symbol_name.rb +20 -0
  139. data/lib/rigor/type/combinator.rb +34 -2
  140. data/lib/rigor/type/float_range.rb +4 -1
  141. data/lib/rigor/type/nominal.rb +6 -1
  142. data/lib/rigor/version.rb +1 -1
  143. data/plugins/rigor-ac-library-rb/lib/rigor/plugin/ac_library_rb.rb +28 -0
  144. data/plugins/rigor-ac-library-rb/lib/rigor-ac-library-rb.rb +3 -0
  145. data/plugins/rigor-ac-library-rb/sig/ac_library_rb/convolution.rbs +7 -0
  146. data/plugins/rigor-ac-library-rb/sig/ac_library_rb/data_structures.rbs +160 -0
  147. data/plugins/rigor-ac-library-rb/sig/ac_library_rb/graph.rbs +94 -0
  148. data/plugins/rigor-ac-library-rb/sig/ac_library_rb/math.rbs +42 -0
  149. data/plugins/rigor-ac-library-rb/sig/ac_library_rb/modint.rbs +71 -0
  150. data/plugins/rigor-ac-library-rb/sig/ac_library_rb/object.rbs +37 -0
  151. data/plugins/rigor-ac-library-rb/sig/ac_library_rb/segtree.rbs +83 -0
  152. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +2 -1
  153. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +100 -0
  154. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +94 -10
  155. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +43 -2
  156. data/plugins/rigor-activerecord/sig/active_record/relation.rbs +3 -1
  157. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +149 -0
  158. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +74 -0
  159. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +104 -0
  160. data/plugins/rigor-alba/lib/rigor/plugin/alba.rb +85 -0
  161. data/plugins/rigor-alba/lib/rigor-alba.rb +3 -0
  162. data/plugins/rigor-alba/sig/alba.rbs +4 -0
  163. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +2 -2
  164. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/target_detector.rb +29 -12
  165. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +7 -6
  166. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +130 -21
  167. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +6 -4
  168. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +4 -4
  169. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +1 -1
  170. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +1 -1
  171. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +31 -0
  172. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +102 -0
  173. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +87 -0
  174. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +76 -0
  175. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer.rb +73 -0
  176. data/plugins/rigor-typelizer/lib/rigor-typelizer.rb +3 -0
  177. data/sig/rigor/analysis/dependency_source_inference/index.rbs +1 -1
  178. data/sig/rigor/plugin/io_boundary.rbs +3 -0
  179. data/sig/rigor/scope.rbs +27 -4
  180. data/sig/rigor/source.rbs +4 -0
  181. data/sig/rigor.rbs +3 -1
  182. metadata +66 -1
@@ -0,0 +1,86 @@
1
+ # Rigor core overlay — supplemental core signatures.
2
+ #
3
+ # --- String bit operations and String#tr with a Hash (Ruby 4.1) ---
4
+ #
5
+ # Issue #1691. Ruby 4.1 adds bit access and bitwise operations on a
6
+ # string's bytes (`string.c`), and lets `tr` / `tr!` take a single Hash of
7
+ # replacement pairs (Feature #22238). No rbs release through 4.2 declares
8
+ # any of them, so correct 4.1 code reported `call.undefined-method`, or
9
+ # `call.wrong-arity` for `"hello".tr("e" => "er")`. Signatures follow the
10
+ # CRuby call-seqs:
11
+ #
12
+ # bit_get(offset, lsb_first: true) -> 0, 1, or nil
13
+ # bit_set?(offset, lsb_first: true) -> true, false, or nil
14
+ # bit_set / bit_clear / bit_flip(offset, lsb_first: true) -> self
15
+ # (offset, length, lsb_first: true) -> self
16
+ # (range, lsb_first: true) -> self
17
+ # bit_count -> integer
18
+ # bit_count(offset, length, lsb_first: true) -> integer
19
+ # bit_count(range, lsb_first: true) -> integer
20
+ # bitwise_not -> string bitwise_not! -> self
21
+ # bitwise_and / _or / _xor(other) -> string, and their `!` forms -> self
22
+ # tr(pairs) -> new_string tr!(pairs) -> self or nil
23
+ #
24
+ # `lsb_first` raises unless it is `true` or `false`, hence `bool`. `other`
25
+ # is converted with `to_str`, hence `string`.
26
+ #
27
+ # TEMPORARY: delete each entry once the rbs release Rigor bundles declares
28
+ # it upstream.
29
+ #
30
+ # Declared unconditionally, not gated on `target_ruby` (ADR-121 § Rejected
31
+ # alternatives; false positives outrank). The new methods sit on a module
32
+ # `String` includes so that a later direct declaration from rbs or a
33
+ # project `sig/` overrides them instead of raising
34
+ # `DuplicatedMethodDefinitionError` for the whole class (see
35
+ # `process.rbs`). The `tr` / `tr!` Hash form is an `| ...` continuation of
36
+ # the upstream declaration, which every supported rbs release carries; a
37
+ # later release that declares the same overload only repeats it.
38
+ module RigorCoreOverlay
39
+ module Ruby41StringBits
40
+ def bit_get: (int offset, ?lsb_first: bool) -> (0 | 1)?
41
+
42
+ def bit_set?: (int offset, ?lsb_first: bool) -> bool?
43
+
44
+ def bit_set: (int offset, ?lsb_first: bool) -> self
45
+ | (int offset, int length, ?lsb_first: bool) -> self
46
+ | (Range[Integer?] range, ?lsb_first: bool) -> self
47
+
48
+ def bit_clear: (int offset, ?lsb_first: bool) -> self
49
+ | (int offset, int length, ?lsb_first: bool) -> self
50
+ | (Range[Integer?] range, ?lsb_first: bool) -> self
51
+
52
+ def bit_flip: (int offset, ?lsb_first: bool) -> self
53
+ | (int offset, int length, ?lsb_first: bool) -> self
54
+ | (Range[Integer?] range, ?lsb_first: bool) -> self
55
+
56
+ def bit_count: () -> Integer
57
+ | (int offset, int length, ?lsb_first: bool) -> Integer
58
+ | (Range[Integer?] range, ?lsb_first: bool) -> Integer
59
+
60
+ def bitwise_not: () -> String
61
+
62
+ def bitwise_not!: () -> self
63
+
64
+ def bitwise_and: (string other) -> String
65
+
66
+ def bitwise_and!: (string other) -> self
67
+
68
+ def bitwise_or: (string other) -> String
69
+
70
+ def bitwise_or!: (string other) -> self
71
+
72
+ def bitwise_xor: (string other) -> String
73
+
74
+ def bitwise_xor!: (string other) -> self
75
+ end
76
+ end
77
+
78
+ class String
79
+ include RigorCoreOverlay::Ruby41StringBits
80
+
81
+ def tr: (Hash[string, string] pairs) -> String
82
+ | ...
83
+
84
+ def tr!: (Hash[string, string] pairs) -> self?
85
+ | ...
86
+ end
@@ -0,0 +1,41 @@
1
+ %a{annotate:rdoc:skip}
2
+ class Integer
3
+ # <!--
4
+ # rdoc-file=lib/prime.rb
5
+ # - each_prime(ubound) { |prime| ... }
6
+ # -->
7
+ # Iterates the given block over all prime numbers.
8
+ #
9
+ # See `Prime`#each for more details.
10
+ #
11
+ def self.each_prime: (Integer) { (Integer) -> void } -> void
12
+
13
+ # <!--
14
+ # rdoc-file=lib/prime.rb
15
+ # - from_prime_division(pd)
16
+ # -->
17
+ # Re-composes a prime factorization and returns the product.
18
+ #
19
+ # See Prime#int_from_prime_division for more details.
20
+ #
21
+ def self.from_prime_division: (Array[[ Integer, Integer ]]) -> Integer
22
+
23
+ # <!--
24
+ # rdoc-file=lib/prime.rb
25
+ # - prime_division(generator = Prime::Generator23.new)
26
+ # -->
27
+ # Returns the factorization of `self`.
28
+ #
29
+ # See Prime#prime_division for more details.
30
+ #
31
+ def prime_division: (?Prime::PseudoPrimeGenerator) -> Array[[ Integer, Integer ]]
32
+
33
+ # <!--
34
+ # rdoc-file=lib/prime.rb
35
+ # - prime?()
36
+ # -->
37
+ # Returns true if `self` is a prime number, else returns false. Not recommended
38
+ # for very big integers (> 10**23).
39
+ #
40
+ def prime?: () -> bool
41
+ end
@@ -0,0 +1,372 @@
1
+ # <!-- rdoc-file=lib/prime.rb -->
2
+ # The set of all prime numbers.
3
+ #
4
+ # ## Example
5
+ #
6
+ # Prime.each(100) do |prime|
7
+ # p prime #=> 2, 3, 5, 7, 11, ...., 97
8
+ # end
9
+ #
10
+ # Prime is Enumerable:
11
+ #
12
+ # Prime.first 5 # => [2, 3, 5, 7, 11]
13
+ #
14
+ # ## Retrieving the instance
15
+ #
16
+ # For convenience, each instance method of `Prime`.instance can be accessed as a
17
+ # class method of `Prime`.
18
+ #
19
+ # e.g.
20
+ # Prime.instance.prime?(2) #=> true
21
+ # Prime.prime?(2) #=> true
22
+ #
23
+ # ## Generators
24
+ #
25
+ # A "generator" provides an implementation of enumerating pseudo-prime numbers
26
+ # and it remembers the position of enumeration and upper bound. Furthermore, it
27
+ # is an external iterator of prime enumeration which is compatible with an
28
+ # Enumerator.
29
+ #
30
+ # `Prime`::`PseudoPrimeGenerator` is the base class for generators. There are
31
+ # few implementations of generator.
32
+ #
33
+ # `Prime`::`EratosthenesGenerator`
34
+ # : Uses Eratosthenes' sieve.
35
+ # `Prime`::`TrialDivisionGenerator`
36
+ # : Uses the trial division method.
37
+ # `Prime`::`Generator23`
38
+ # : Generates all positive integers which are not divisible by either 2 or 3.
39
+ # This sequence is very bad as a pseudo-prime sequence. But this is faster
40
+ # and uses much less memory than the other generators. So, it is suitable
41
+ # for factorizing an integer which is not large but has many prime factors.
42
+ # e.g. for Prime#prime? .
43
+ #
44
+ class Prime
45
+ include Singleton
46
+
47
+ include Enumerable[Integer]
48
+
49
+ extend Enumerable[Integer]
50
+
51
+ # <!--
52
+ # rdoc-file=lib/prime.rb
53
+ # - each(ubound = nil, generator = EratosthenesGenerator.new, &block)
54
+ # -->
55
+ # Iterates the given block over all prime numbers.
56
+ #
57
+ # ## Parameters
58
+ #
59
+ # `ubound`
60
+ # : Optional. An arbitrary positive number. The upper bound of enumeration.
61
+ # The method enumerates prime numbers infinitely if `ubound` is nil.
62
+ # `generator`
63
+ # : Optional. An implementation of pseudo-prime generator.
64
+ #
65
+ #
66
+ # ## Return value
67
+ #
68
+ # An evaluated value of the given block at the last time. Or an enumerator which
69
+ # is compatible to an `Enumerator` if no block given.
70
+ #
71
+ # ## Description
72
+ #
73
+ # Calls `block` once for each prime number, passing the prime as a parameter.
74
+ #
75
+ # `ubound`
76
+ # : Upper bound of prime numbers. The iterator stops after it yields all prime
77
+ # numbers p <= `ubound`.
78
+ #
79
+ def self.each: (?Integer? ubound, ?PseudoPrimeGenerator generator) { (Integer) -> void } -> void
80
+ | (?Integer? ubound, ?PseudoPrimeGenerator generator) -> PseudoPrimeGenerator
81
+
82
+ # <!--
83
+ # rdoc-file=lib/prime.rb
84
+ # - each(ubound = nil, generator = EratosthenesGenerator.new, &block)
85
+ # -->
86
+ # Iterates the given block over all prime numbers.
87
+ #
88
+ # ## Parameters
89
+ #
90
+ # `ubound`
91
+ # : Optional. An arbitrary positive number. The upper bound of enumeration.
92
+ # The method enumerates prime numbers infinitely if `ubound` is nil.
93
+ # `generator`
94
+ # : Optional. An implementation of pseudo-prime generator.
95
+ #
96
+ #
97
+ # ## Return value
98
+ #
99
+ # An evaluated value of the given block at the last time. Or an enumerator which
100
+ # is compatible to an `Enumerator` if no block given.
101
+ #
102
+ # ## Description
103
+ #
104
+ # Calls `block` once for each prime number, passing the prime as a parameter.
105
+ #
106
+ # `ubound`
107
+ # : Upper bound of prime numbers. The iterator stops after it yields all prime
108
+ # numbers p <= `ubound`.
109
+ #
110
+ def each: (?Integer? ubound, ?PseudoPrimeGenerator generator) { (Integer) -> void } -> void
111
+ | (?Integer? ubound, ?PseudoPrimeGenerator generator) -> PseudoPrimeGenerator
112
+
113
+ # <!--
114
+ # rdoc-file=lib/prime.rb
115
+ # - int_from_prime_division(pd)
116
+ # -->
117
+ # Re-composes a prime factorization and returns the product.
118
+ #
119
+ # For the decomposition:
120
+ #
121
+ # [[p_1, e_1], [p_2, e_2], ..., [p_n, e_n]],
122
+ #
123
+ # it returns:
124
+ #
125
+ # p_1**e_1 * p_2**e_2 * ... * p_n**e_n.
126
+ #
127
+ # ## Parameters
128
+ # `pd`
129
+ # : Array of pairs of integers. Each pair consists of a prime number -- a
130
+ # prime factor -- and a natural number -- its exponent (multiplicity).
131
+ #
132
+ #
133
+ # ## Example
134
+ # Prime.int_from_prime_division([[3, 2], [5, 1]]) #=> 45
135
+ # 3**2 * 5 #=> 45
136
+ #
137
+ def self.int_from_prime_division: (Array[[ Integer, Integer ]]) -> (Integer | Rational)
138
+
139
+ # <!--
140
+ # rdoc-file=lib/prime.rb
141
+ # - int_from_prime_division(pd)
142
+ # -->
143
+ # Re-composes a prime factorization and returns the product.
144
+ #
145
+ # For the decomposition:
146
+ #
147
+ # [[p_1, e_1], [p_2, e_2], ..., [p_n, e_n]],
148
+ #
149
+ # it returns:
150
+ #
151
+ # p_1**e_1 * p_2**e_2 * ... * p_n**e_n.
152
+ #
153
+ # ## Parameters
154
+ # `pd`
155
+ # : Array of pairs of integers. Each pair consists of a prime number -- a
156
+ # prime factor -- and a natural number -- its exponent (multiplicity).
157
+ #
158
+ #
159
+ # ## Example
160
+ # Prime.int_from_prime_division([[3, 2], [5, 1]]) #=> 45
161
+ # 3**2 * 5 #=> 45
162
+ #
163
+ def int_from_prime_division: (Array[[ Integer, Integer ]]) -> (Integer | Rational)
164
+
165
+ # <!--
166
+ # rdoc-file=lib/prime.rb
167
+ # - prime?(value, generator = Prime::Generator23.new)
168
+ # -->
169
+ # Returns true if `value` is a prime number, else returns false. Integer#prime?
170
+ # is much more performant.
171
+ #
172
+ # ## Parameters
173
+ #
174
+ # `value`
175
+ # : an arbitrary integer to be checked.
176
+ # `generator`
177
+ # : optional. A pseudo-prime generator.
178
+ #
179
+ def self.prime?: (Integer value, ?PseudoPrimeGenerator generator) -> bool
180
+
181
+ # <!--
182
+ # rdoc-file=lib/prime.rb
183
+ # - prime?(value, generator = Prime::Generator23.new)
184
+ # -->
185
+ # Returns true if `value` is a prime number, else returns false. Integer#prime?
186
+ # is much more performant.
187
+ #
188
+ # ## Parameters
189
+ #
190
+ # `value`
191
+ # : an arbitrary integer to be checked.
192
+ # `generator`
193
+ # : optional. A pseudo-prime generator.
194
+ #
195
+ def prime?: (Integer value, ?PseudoPrimeGenerator generator) -> bool
196
+
197
+ # <!--
198
+ # rdoc-file=lib/prime.rb
199
+ # - prime_division(value, generator = Prime::Generator23.new)
200
+ # -->
201
+ # Returns the factorization of `value`.
202
+ #
203
+ # For an arbitrary integer:
204
+ #
205
+ # p_1**e_1 * p_2**e_2 * ... * p_n**e_n,
206
+ #
207
+ # prime_division returns an array of pairs of integers:
208
+ #
209
+ # [[p_1, e_1], [p_2, e_2], ..., [p_n, e_n]].
210
+ #
211
+ # Each pair consists of a prime number -- a prime factor -- and a natural number
212
+ # -- its exponent (multiplicity).
213
+ #
214
+ # ## Parameters
215
+ # `value`
216
+ # : An arbitrary integer.
217
+ # `generator`
218
+ # : Optional. A pseudo-prime generator. `generator`.succ must return the next
219
+ # pseudo-prime number in ascending order. It must generate all prime
220
+ # numbers, but may also generate non-prime numbers, too.
221
+ #
222
+ #
223
+ # ### Exceptions
224
+ # `ZeroDivisionError`
225
+ # : when `value` is zero.
226
+ #
227
+ #
228
+ # ## Example
229
+ #
230
+ # Prime.prime_division(45) #=> [[3, 2], [5, 1]]
231
+ # 3**2 * 5 #=> 45
232
+ #
233
+ def self.prime_division: (Integer, ?PseudoPrimeGenerator generator) -> Array[[ Integer, Integer ]]
234
+
235
+ # <!--
236
+ # rdoc-file=lib/prime.rb
237
+ # - prime_division(value, generator = Prime::Generator23.new)
238
+ # -->
239
+ # Returns the factorization of `value`.
240
+ #
241
+ # For an arbitrary integer:
242
+ #
243
+ # p_1**e_1 * p_2**e_2 * ... * p_n**e_n,
244
+ #
245
+ # prime_division returns an array of pairs of integers:
246
+ #
247
+ # [[p_1, e_1], [p_2, e_2], ..., [p_n, e_n]].
248
+ #
249
+ # Each pair consists of a prime number -- a prime factor -- and a natural number
250
+ # -- its exponent (multiplicity).
251
+ #
252
+ # ## Parameters
253
+ # `value`
254
+ # : An arbitrary integer.
255
+ # `generator`
256
+ # : Optional. A pseudo-prime generator. `generator`.succ must return the next
257
+ # pseudo-prime number in ascending order. It must generate all prime
258
+ # numbers, but may also generate non-prime numbers, too.
259
+ #
260
+ #
261
+ # ### Exceptions
262
+ # `ZeroDivisionError`
263
+ # : when `value` is zero.
264
+ #
265
+ #
266
+ # ## Example
267
+ #
268
+ # Prime.prime_division(45) #=> [[3, 2], [5, 1]]
269
+ # 3**2 * 5 #=> 45
270
+ #
271
+ def prime_division: (Integer, ?PseudoPrimeGenerator generator) -> Array[[ Integer, Integer ]]
272
+
273
+ # Returns the singleton instance.
274
+ #
275
+ def self.instance: () -> Prime
276
+
277
+ # <!-- rdoc-file=lib/prime.rb -->
278
+ # An abstract class for enumerating pseudo-prime numbers.
279
+ #
280
+ # Concrete subclasses should override succ, next, rewind.
281
+ #
282
+ class PseudoPrimeGenerator
283
+ # <!--
284
+ # rdoc-file=lib/prime.rb
285
+ # - new(ubound = nil)
286
+ # -->
287
+ #
288
+ def initialize: (?Integer?) -> void
289
+
290
+ include Enumerable[Integer]
291
+
292
+ # <!--
293
+ # rdoc-file=lib/prime.rb
294
+ # - upper_bound()
295
+ # -->
296
+ # ----
297
+ # <!--
298
+ # rdoc-file=lib/prime.rb
299
+ # - upper_bound=(ubound)
300
+ # -->
301
+ #
302
+ attr_accessor upper_bound(): Integer?
303
+
304
+ # <!--
305
+ # rdoc-file=lib/prime.rb
306
+ # - each() { |prime| ... }
307
+ # -->
308
+ # Iterates the given block for each prime number.
309
+ #
310
+ def each: () { (Integer) -> void } -> void
311
+
312
+ # <!--
313
+ # rdoc-file=lib/prime.rb
314
+ # - next()
315
+ # -->
316
+ # alias of `succ`.
317
+ #
318
+ def next: () -> Integer
319
+
320
+ # <!--
321
+ # rdoc-file=lib/prime.rb
322
+ # - rewind()
323
+ # -->
324
+ # Rewinds the internal position for enumeration.
325
+ #
326
+ # See `Enumerator`#rewind.
327
+ #
328
+ def rewind: () -> void
329
+
330
+ # <!--
331
+ # rdoc-file=lib/prime.rb
332
+ # - size()
333
+ # -->
334
+ #
335
+ def size: () -> Float
336
+
337
+ # <!--
338
+ # rdoc-file=lib/prime.rb
339
+ # - succ()
340
+ # -->
341
+ # returns the next pseudo-prime number, and move the internal position forward.
342
+ #
343
+ # `PseudoPrimeGenerator`#succ raises `NotImplementedError`.
344
+ #
345
+ def succ: () -> Integer
346
+ end
347
+
348
+ # <!-- rdoc-file=lib/prime.rb -->
349
+ # An implementation of `PseudoPrimeGenerator`.
350
+ #
351
+ # Uses `EratosthenesSieve`.
352
+ #
353
+ class EratosthenesGenerator < PseudoPrimeGenerator
354
+ end
355
+
356
+ # <!-- rdoc-file=lib/prime.rb -->
357
+ # An implementation of `PseudoPrimeGenerator` which uses a prime table generated
358
+ # by trial division.
359
+ #
360
+ class TrialDivisionGenerator < PseudoPrimeGenerator
361
+ end
362
+
363
+ # <!-- rdoc-file=lib/prime.rb -->
364
+ # Generates all integers which are greater than 2 and are not divisible by
365
+ # either 2 or 3.
366
+ #
367
+ # This is a pseudo-prime generator, suitable on checking primality of an integer
368
+ # by brute force method.
369
+ #
370
+ class Generator23 < PseudoPrimeGenerator
371
+ end
372
+ end
@@ -282,9 +282,9 @@ non-default behaviours: extra `paths`, an alternative
282
282
  If you used the [AI-assisted setup](#the-fast-path-let-an-ai-agent-set-it-up),
283
283
  the `rigor-project-init` skill already wrote one for you. To
284
284
  write a starter by hand, `rigor init` emits `.rigor.dist.yml`
285
- — the project default that gets committed, with `target_ruby`,
286
- `paths`, and a `severity_profile` filled in and the rest
287
- commented out. That is all most projects need; the key
285
+ — the project default that gets committed, with `paths` and a
286
+ `severity_profile` filled in, `target_ruby` commented out until
287
+ you set it to your project's Ruby, and the rest commented out. That is all most projects need; the key
288
288
  reference, the JSON-schema editor integration, and `includes:`
289
289
  composition are in
290
290
  [Configuration](../manual/03-configuration.md).
@@ -96,7 +96,9 @@ u.name # Constant<"Alice"> — through in-source dispatch +
96
96
  both. The setter's argument type is whatever the call site
97
97
  provides. The `def.ivar-write-mismatch` rule (v0.1.2) checks
98
98
  that two writes to the same ivar in the same class body
99
- agree on the concrete class — see
99
+ agree on the concrete class, unless the class's RBS
100
+ declaration of the ivar (`@name: String | Integer`, or
101
+ `attr_reader name: String | Integer`) admits both — see
100
102
  [manual — Diagnostics](../manual/04-diagnostics.md)
101
103
  for the rule's exact contract; it lets you catch an
102
104
  accidental rebind from `String` to `Array` in the same class
@@ -159,6 +159,12 @@ The `sig.skipped.*` reasons are:
159
159
  inline and in `sig/`, and the two disagree. A refusal:
160
160
  `--write` and `--check` exit `1`. See
161
161
  [Methods declared inline](#methods-declared-inline).
162
+ - `sig.skipped.top-level-def` — a plain top-level `def`, which
163
+ defines a private `Object` method. sig-gen lists it (file,
164
+ method name and this reason in `--format=json`) but writes no
165
+ RBS for it, so `--write` and `--check` are unaffected. Its
166
+ JSON row has no `class` key. `def self.x`, a `def` inside a
167
+ block body and one inside `class << obj` are not reported.
162
168
  - `sig.skipped.unrenderable-rbs` — the signature Rigor
163
169
  rendered for this method does not parse as RBS. This one
164
170
  is a **bug in Rigor**, not a property of your code: every
@@ -482,6 +482,11 @@ stays silent), only a proven (`:no`) violation fires, and severity
482
482
  maps through `severity_profile:` (`lenient` → off, `balanced` →
483
483
  warning, `strict` → error). The ancestor scope is the superclass
484
484
  chain plus included/prepended modules, resolved cross-file.
485
+ Constructors and copy hooks (`initialize`, `initialize_copy`,
486
+ `initialize_dup`, `initialize_clone`) are outside the family:
487
+ `Class#new`, `dup` and `clone` call them on the receiver's own class,
488
+ never through a parent-typed reference, so a subclass constructor
489
+ that takes different arguments is not a violation.
485
490
 
486
491
  The escape hatch for a *legitimate* specialization that looks like a
487
492
  narrowing is **generics first, not suppression**: declare the parent
@@ -62,7 +62,7 @@ cache:
62
62
 
63
63
  | Key | Type | Default | Meaning |
64
64
  | --- | --- | --- | --- |
65
- | `target_ruby` | String | `"4.0"` | The Ruby version *your* project runs — `"X.Y"`, `"X.Y.Z"`, or `"latest"`. Independent of the Ruby Rigor itself runs on. |
65
+ | `target_ruby` | String | `"4.0"` | The lowest Ruby version *your* project runs on — `"X.Y"`, `"X.Y.Z"`, or `"latest"`. Independent of the Ruby Rigor itself runs on. Every value selects the syntax Rigor parses. A version you set explicitly also turns on the Ruby-deprecation rules for that version ([`call.deprecated-ruby2-keywords`](04-diagnostics.md#rule-call-deprecated-ruby2-keywords) from `"4.1"`); the default and `"latest"` never do, which is why `rigor init` writes the key commented out. |
66
66
  | `paths` | Array | `["lib"]` | Directories or files to analyse. |
67
67
  | `exclude` | Array | `[]` | Glob patterns to skip. `vendor/bundle`, `.bundle`, and `node_modules` are always excluded. |
68
68
  | `test_paths` | Array | `nil` | The project's test roots: the directories (or files) holding its tests. Relative entries resolve against the config file's directory. Unset auto-detects whichever of `spec/` and `test/` exist; `[]` declares none. `rigor sig-gen --params=observed` reads call sites there to type parameters, and names on stderr a declared root that does not exist. Test roots are not analysed unless `paths:` also lists them, and changing them invalidates no cache. |
@@ -56,6 +56,7 @@ carries no `documentation_url`.
56
56
  | <a id="rule-call-argument-type-mismatch"></a>`call.argument-type-mismatch` | An argument's type provably violates the parameter contract. | high |
57
57
  | <a id="rule-call-possible-nil-receiver"></a>`call.possible-nil-receiver` | The receiver is `T \| nil` and the method is not defined on `NilClass`. | high |
58
58
  | <a id="rule-call-raise-non-exception"></a>`call.raise-non-exception` | A `raise` / `fail` argument's concrete type is provably not an Exception class, an Exception instance, a String, or an object defining `#exception` — a runtime `TypeError`. | high |
59
+ | <a id="rule-call-deprecated-ruby2-keywords"></a>`call.deprecated-ruby2-keywords` | A call to the `ruby2_keywords` family Ruby 4.1 deprecates: `Module#ruby2_keywords`, top-level `ruby2_keywords` and `Proc#ruby2_keywords` (removed in Ruby 4.4), or `Hash.ruby2_keywords_hash?` and `Hash.ruby2_keywords_hash` (removed in Ruby 4.5), directly or through `send` with a literal Symbol. Runs only when `.rigor.yml` sets `target_ruby` explicitly to `"4.1"` or later: the default, an older version and `"latest"` report nothing, because on Ruby 4.0 the call is correct. Not reported when a project or `pre_eval:` file defines a method by the same name, inside a block at the top level, an `instance_eval` / `instance_exec` block or an instance method of a class or module, or where a `RUBY_VERSION` guard rules the call out on the stated Ruby or cannot be decided. `info` under `lenient`, `warning` under `balanced`, `error` under `strict`. | high |
59
60
  | <a id="rule-call-unresolved-toplevel"></a>`call.unresolved-toplevel` | A top-level implicit-self call resolves against no same-file `def`, `pre_eval:` patch, or `Kernel` / `Object` method. | low |
60
61
  | <a id="rule-flow-always-raises"></a>`flow.always-raises` | The expression provably raises on every reachable path. | high |
61
62
  | <a id="rule-flow-unreachable-branch"></a>`flow.unreachable-branch` | An `if` / `unless` / ternary branch is statically dead. | high |
@@ -66,7 +67,7 @@ carries no `documentation_url`.
66
67
  | <a id="rule-flow-return-in-ensure"></a>`flow.return-in-ensure` | An explicit `return` inside an `ensure` clause — it overrides the method's in-flight return value and silently swallows any in-flight exception. A `return` in a nested `def`, lambda, or `define_method` block inside the `ensure` does not fire (it exits that inner frame). | high |
67
68
  | <a id="rule-flow-shadowed-rescue-clause"></a>`flow.shadowed-rescue-clause` | A `rescue` clause can never run because an earlier clause of the same chain already catches a superclass (or the same class) of every exception class it names. | high |
68
69
  | <a id="rule-def-return-type-mismatch"></a>`def.return-type-mismatch` | The method body's result violates its declared RBS return type. | medium |
69
- | <a id="rule-def-ivar-write-mismatch"></a>`def.ivar-write-mismatch` | An instance variable is written with a type disagreeing with its first write. | high |
70
+ | <a id="rule-def-ivar-write-mismatch"></a>`def.ivar-write-mismatch` | An instance variable is written with a type disagreeing with its first write, unless its RBS declaration (`@x: T` or an `attr_*` member) admits both writes. | high |
70
71
  | <a id="rule-def-method-visibility-mismatch"></a>`def.method-visibility-mismatch` | An explicit-receiver call reaches a private method. | high |
71
72
  | <a id="rule-def-override-visibility-reduced"></a>`def.override-visibility-reduced` | An override reduces the visibility it inherits from a project-defined ancestor. | high |
72
73
  | <a id="rule-def-override-return-widened"></a>`def.override-return-widened` | An override's declared return type widens the inherited return (covariance). | high |
@@ -77,6 +77,8 @@ a diagnostic another layer has already suppressed.
77
77
  | `rigor baseline drift` | Show how buckets have moved — `--only=over` for buckets that grew, `reducible` for ones you have already improved past, `cleared` for empty ones. |
78
78
  | `rigor baseline prune` | Drop buckets that match nothing any more. `--dry-run` previews. |
79
79
 
80
+ `generate` and `regenerate` analyse with your loaded configuration unchanged except that `baseline:` is off, so options such as `bleeding_edge`, `target_ruby` or `severity_overrides` shape the recorded set exactly as they shape `rigor check`.
81
+
80
82
  `generate` and `regenerate` take `--match-mode=rule` (the
81
83
  default — one bucket per file × rule) or `--match-mode=message`
82
84
  (a bucket per distinct message: more precise, more churn).
@@ -54,7 +54,7 @@ each plugin's options — but the families today are:
54
54
  `rigor-ffi` provides the `ffi_binding_recognizer` class DSL on
55
55
  `Rigor::Plugin::Base` to recognize custom binding definitions.
56
56
  - **Other ecosystems** — `rigor-sinatra`, `rigor-hanami`,
57
- `rigor-devise`, `rigor-pundit`, `rigor-sidekiq`,
57
+ `rigor-devise`, `rigor-pundit`, `rigor-alba`, `rigor-typelizer`, `rigor-sidekiq`,
58
58
  `rigor-graphql`, `rigor-statesman`, `rigor-sorbet`,
59
59
  `rigor-typescript-utility-types`,
60
60
  `rigor-activesupport-core-ext`.
@@ -145,7 +145,7 @@ Four sections, needing four different decisions:
145
145
  | **Reachable only from test code** | Live test, no production caller Rigor can see or suspect | Work these first |
146
146
  | **Candidates** | Nothing reachable names it | Adjudicate — most are still live |
147
147
  | **Cannot decide** | Something can name it at runtime | Read the reason; do not delete from here |
148
- | **Namespace-only** | A module wrapping live code | Excluded from candidates; count only |
148
+ | **Namespace-only** | A module wrapping code production reaches | Excluded from candidates; count only |
149
149
 
150
150
  Work them in that order — which is not the order you *read* the
151
151
  summary in, where `roots` comes first because it tells you whether to
@@ -162,6 +162,12 @@ section held 22 policy classes whose only remaining caller was
162
162
  `spec/policies/`. Those 22 were not adjudicated, so treat the section
163
163
  as high-signal rather than high-yield; the yield is unmeasured.
164
164
 
165
+ "Test code" is decided by path, relative to the directory you run `rigor unused` in (run it from the
166
+ project root): `spec/` and `test/` anywhere below it, `*_spec.rb` and `*_test.rb`, and the end-to-end
167
+ trees `qa/`, `e2e/` and `features/` at that root. Tooling directories such as
168
+ `rubocop/`, `keeps/`, `tooling/` and `scripts/` count as production, so a class used only by them is
169
+ rooted rather than listed: deleting it would break the tooling.
170
+
165
171
  ### Candidates: sort by how conventional the class is
166
172
 
167
173
  A candidate row is its name and declaration site:
@@ -171,6 +177,23 @@ Candidates — nothing reachable references these (45)
171
177
  1 Api::V1::Timelines::TopicController app/controllers/api/v1/timelines/topic_controller.rb:3
172
178
  ```
173
179
 
180
+ A base class is reachable whenever one of its subclasses is:
181
+ `class Sub < Base` names `Base` on `Sub`'s behalf. So a base class
182
+ that nothing else names appears together with its subclasses when none
183
+ of them is reachable, and the decision belongs to the whole family
184
+ rather than to one row. When a subclass sits under **Cannot decide**,
185
+ its base moves there too.
186
+
187
+ A class declared outside `paths:` — `config/application.rb`'s
188
+ `Application`, a class in an initializer, a spec helper — is not
189
+ itself in the report, so what it names counts as named from its file:
190
+ `config.middleware.use MyMw` keeps `MyMw` live, and a fake subclass in
191
+ `spec/support` keeps its base reachable from tests. The same holds for
192
+ a gem class you reopen inside `paths:`. A name the file declares
193
+ itself still means that declaration: a migration's local
194
+ `class LegacyThing < ActiveRecord::Base` stub does not keep the app's
195
+ `LegacyThing` model live.
196
+
174
197
  The 53 false positives in the adjudicated run fell into a few recurring
175
198
  shapes, and 28 of them were the first one. Recognising these lets you
176
199
  skip most of a list quickly:
@@ -230,7 +253,14 @@ belongs here instead, with the file named.
230
253
 
231
254
  `"Foo".constantize` names `Foo` exactly, so it counts as an ordinary
232
255
  reference and never reaches this section. `"Foo::#{key}".constantize`
233
- can only bound the namespace, so everything under `Foo` is demoted. A
256
+ can only bound the namespace, so everything under `Foo` is demoted;
257
+ `"V#{version}"` is the start of a name, so `V1`, `V2_0` and everything
258
+ under them are. `const_get` looks the name up from its receiver, so
259
+ `const_get("V#{version}")` inside `class Migration` demotes
260
+ `Migration::V1` — and the top-level `V*` names as well, unless the call
261
+ passes `false` as its second argument. A name built into a local
262
+ variable first (`name = "V#{version}"; const_get(name)`) is read the
263
+ same way. A
234
264
  class name appearing as a string in a `.yml`, `.json` or template file
235
265
  demotes the same way — weaker evidence than a constant reference, and
236
266
  neither proof of use nor grounds to call it dead.