rigortype 0.4.1 → 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 (230) 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 +18 -5
  25. data/docs/handbook/appendix-liskov.md +5 -0
  26. data/docs/manual/02-cli-reference.md +19 -1
  27. data/docs/manual/03-configuration.md +7 -5
  28. data/docs/manual/04-diagnostics.md +2 -1
  29. data/docs/manual/05-inspecting-types.md +50 -0
  30. data/docs/manual/06-baseline.md +2 -0
  31. data/docs/manual/07-plugins.md +1 -1
  32. data/docs/manual/12-caching.md +18 -1
  33. data/docs/manual/14-rails-quickstart.md +2 -2
  34. data/docs/manual/18-removing-dead-code.md +41 -4
  35. data/docs/manual/plugins/README.md +6 -1
  36. data/docs/manual/plugins/rigor-activerecord.md +21 -0
  37. data/docs/manual/plugins/rigor-alba.md +60 -0
  38. data/docs/manual/plugins/rigor-minitest.md +22 -4
  39. data/docs/manual/plugins/rigor-typelizer.md +71 -0
  40. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +27 -3
  41. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +21 -92
  42. data/lib/rigor/analysis/check_rules/ruby_deprecations.rb +275 -0
  43. data/lib/rigor/analysis/check_rules/rule_ids.rb +11 -0
  44. data/lib/rigor/analysis/check_rules/source_arity.rb +21 -2
  45. data/lib/rigor/analysis/check_rules.rb +292 -59
  46. data/lib/rigor/analysis/dependency_recorder.rb +15 -0
  47. data/lib/rigor/analysis/dependency_source_inference/builder.rb +8 -1
  48. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +21 -0
  49. data/lib/rigor/analysis/dependency_source_inference/index.rb +36 -11
  50. data/lib/rigor/analysis/dependency_source_inference/walker.rb +196 -27
  51. data/lib/rigor/analysis/effects_cache_probe.rb +1 -1
  52. data/lib/rigor/analysis/erb_template_detector.rb +2 -1
  53. data/lib/rigor/analysis/incremental.rb +29 -9
  54. data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +30 -5
  55. data/lib/rigor/analysis/incremental_run_slot.rb +6 -4
  56. data/lib/rigor/analysis/incremental_session.rb +103 -23
  57. data/lib/rigor/analysis/path_expansion.rb +62 -6
  58. data/lib/rigor/analysis/plugin_fact_fingerprint.rb +124 -37
  59. data/lib/rigor/analysis/reachability/graph.rb +241 -37
  60. data/lib/rigor/analysis/reachability/scan.rb +490 -77
  61. data/lib/rigor/analysis/reachability/scan_cache.rb +4 -2
  62. data/lib/rigor/analysis/rule_catalog.rb +43 -0
  63. data/lib/rigor/analysis/run_cache_key.rb +16 -1
  64. data/lib/rigor/analysis/run_cache_probe.rb +1 -1
  65. data/lib/rigor/analysis/run_stats.rb +28 -6
  66. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +113 -6
  67. data/lib/rigor/analysis/runner/pool_coordinator.rb +77 -19
  68. data/lib/rigor/analysis/runner/project_pre_passes.rb +21 -1
  69. data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
  70. data/lib/rigor/analysis/runner.rb +62 -13
  71. data/lib/rigor/analysis/worker_session.rb +8 -5
  72. data/lib/rigor/cache/engine_source.rb +21 -7
  73. data/lib/rigor/cache/incremental_snapshot.rb +55 -5
  74. data/lib/rigor/cli/annotate_command.rb +5 -0
  75. data/lib/rigor/cli/baseline_command.rb +7 -18
  76. data/lib/rigor/cli/check_command.rb +140 -40
  77. data/lib/rigor/cli/docs_command.rb +5 -4
  78. data/lib/rigor/cli/lens_command.rb +144 -0
  79. data/lib/rigor/cli/lsp_command.rb +5 -2
  80. data/lib/rigor/cli/skill_command.rb +3 -3
  81. data/lib/rigor/cli/skill_describe.rb +1 -1
  82. data/lib/rigor/cli/trace_command.rb +5 -0
  83. data/lib/rigor/cli/type_of_command.rb +6 -3
  84. data/lib/rigor/cli/unused_command.rb +166 -27
  85. data/lib/rigor/cli.rb +39 -5
  86. data/lib/rigor/configuration/severity_profile.rb +6 -0
  87. data/lib/rigor/configuration.rb +61 -2
  88. data/lib/rigor/environment/bundle_sig_discovery.rb +11 -7
  89. data/lib/rigor/environment/gated_signature_guard.rb +43 -0
  90. data/lib/rigor/environment/gated_signature_plan.rb +339 -0
  91. data/lib/rigor/environment/installed_gem_set.rb +38 -11
  92. data/lib/rigor/environment/lockfile_resolver.rb +37 -6
  93. data/lib/rigor/environment/missing_gem_constant_index.rb +22 -7
  94. data/lib/rigor/environment/rbs_coverage_report.rb +1 -1
  95. data/lib/rigor/environment/rbs_loader.rb +316 -14
  96. data/lib/rigor/environment/required_features.rb +208 -0
  97. data/lib/rigor/environment.rb +33 -8
  98. data/lib/rigor/inference/acceptance.rb +28 -3
  99. data/lib/rigor/inference/builtins/cruby_definers.rb +78 -0
  100. data/lib/rigor/inference/builtins/method_catalog.rb +3 -0
  101. data/lib/rigor/inference/captured_locals.rb +43 -3
  102. data/lib/rigor/inference/def_node_resolver.rb +23 -1
  103. data/lib/rigor/inference/expression_typer.rb +261 -29
  104. data/lib/rigor/inference/global_write_census.rb +7 -4
  105. data/lib/rigor/inference/guard_rebinding.rb +1 -1
  106. data/lib/rigor/inference/in_effect_refinements/proc_literals.rb +83 -0
  107. data/lib/rigor/inference/in_effect_refinements/refine_defs.rb +54 -0
  108. data/lib/rigor/inference/in_effect_refinements/scope_reads.rb +286 -0
  109. data/lib/rigor/inference/in_effect_refinements/walk.rb +276 -0
  110. data/lib/rigor/inference/in_effect_refinements.rb +256 -0
  111. data/lib/rigor/inference/key_presence_guard/scan.rb +281 -0
  112. data/lib/rigor/inference/key_presence_guard.rb +303 -0
  113. data/lib/rigor/inference/macro_block_self_type.rb +59 -12
  114. data/lib/rigor/inference/match_rebinding.rb +1 -1
  115. data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
  116. data/lib/rigor/inference/method_dispatcher/argument_nodes.rb +25 -0
  117. data/lib/rigor/inference/method_dispatcher/arithmetic_sequence_elements.rb +73 -0
  118. data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +68 -0
  119. data/lib/rigor/inference/method_dispatcher/integer_step_block_params.rb +81 -0
  120. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +6 -0
  121. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +3 -2
  122. data/lib/rigor/inference/method_dispatcher/keyword_arguments.rb +267 -0
  123. data/lib/rigor/inference/method_dispatcher/match_data_folding.rb +2 -2
  124. data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +3 -3
  125. data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -1
  126. data/lib/rigor/inference/method_dispatcher/optional_count.rb +63 -0
  127. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +122 -105
  128. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +247 -28
  129. data/lib/rigor/inference/method_dispatcher/splat_arity.rb +71 -0
  130. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +4 -7
  131. data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +8 -26
  132. data/lib/rigor/inference/method_dispatcher/unit_denominator_rational.rb +67 -0
  133. data/lib/rigor/inference/method_dispatcher.rb +96 -14
  134. data/lib/rigor/inference/module_function_state.rb +2 -1
  135. data/lib/rigor/inference/narrowing.rb +20 -6
  136. data/lib/rigor/inference/object_mixins.rb +180 -0
  137. data/lib/rigor/inference/project_patched_definedness_guard.rb +86 -0
  138. data/lib/rigor/inference/project_patched_methods.rb +12 -7
  139. data/lib/rigor/inference/project_patched_scanner.rb +108 -26
  140. data/lib/rigor/inference/refine_census.rb +239 -0
  141. data/lib/rigor/inference/refine_self.rb +317 -0
  142. data/lib/rigor/inference/refined_dispatch/class_object.rb +350 -0
  143. data/lib/rigor/inference/refined_dispatch/own_method.rb +475 -0
  144. data/lib/rigor/inference/refined_dispatch.rb +320 -0
  145. data/lib/rigor/inference/repeated_or_writes.rb +2 -0
  146. data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +13 -0
  147. data/lib/rigor/inference/scope_indexer.rb +827 -65
  148. data/lib/rigor/inference/statement_evaluator.rb +294 -51
  149. data/lib/rigor/inference/string_mutation.rb +9 -3
  150. data/lib/rigor/inference/synthetic_method_scanner.rb +2 -1
  151. data/lib/rigor/inference/untyped_rejoin.rb +75 -0
  152. data/lib/rigor/inference/version_guard.rb +19 -10
  153. data/lib/rigor/language_server/project_context.rb +87 -5
  154. data/lib/rigor/language_server/server.rb +18 -0
  155. data/lib/rigor/lens/anchor.rb +115 -0
  156. data/lib/rigor/lens/contract_reader.rb +118 -0
  157. data/lib/rigor/lens/document.rb +113 -0
  158. data/lib/rigor/lens/file_reader.rb +433 -0
  159. data/lib/rigor/lens/members.rb +153 -0
  160. data/lib/rigor/lens/parameters.rb +91 -0
  161. data/lib/rigor/lens/plugins.rb +40 -0
  162. data/lib/rigor/lens/run.rb +280 -0
  163. data/lib/rigor/lens/slot.rb +79 -0
  164. data/lib/rigor/lens.rb +40 -0
  165. data/lib/rigor/plugin/inflector.rb +1 -1
  166. data/lib/rigor/plugin/io_boundary.rb +42 -3
  167. data/lib/rigor/plugin/macro/block_as_method.rb +51 -8
  168. data/lib/rigor/project_environment.rb +48 -6
  169. data/lib/rigor/protection/mutation_cache.rb +3 -2
  170. data/lib/rigor/protection/mutator.rb +1 -1
  171. data/lib/rigor/reflection/constant_ancestors.rb +127 -11
  172. data/lib/rigor/reflection/constant_path.rb +13 -2
  173. data/lib/rigor/reflection.rb +40 -8
  174. data/lib/rigor/scope/discovery_index.rb +40 -1
  175. data/lib/rigor/scope/resolution_chain.rb +159 -11
  176. data/lib/rigor/scope.rb +211 -21
  177. data/lib/rigor/sig_gen/classification.rb +4 -1
  178. data/lib/rigor/sig_gen/generator.rb +100 -10
  179. data/lib/rigor/sig_gen/method_candidate.rb +2 -2
  180. data/lib/rigor/sig_gen/skip_reason_catalog.rb +12 -0
  181. data/lib/rigor/source/alias_names.rb +52 -0
  182. data/lib/rigor/source/literals.rb +3 -2
  183. data/lib/rigor/source/symbol_name.rb +20 -0
  184. data/lib/rigor/type/combinator.rb +34 -2
  185. data/lib/rigor/type/float_range.rb +4 -1
  186. data/lib/rigor/type/nominal.rb +6 -1
  187. data/lib/rigor/version.rb +1 -1
  188. data/plugins/rigor-ac-library-rb/lib/rigor/plugin/ac_library_rb.rb +28 -0
  189. data/plugins/rigor-ac-library-rb/lib/rigor-ac-library-rb.rb +3 -0
  190. data/plugins/rigor-ac-library-rb/sig/ac_library_rb/convolution.rbs +7 -0
  191. data/plugins/rigor-ac-library-rb/sig/ac_library_rb/data_structures.rbs +160 -0
  192. data/plugins/rigor-ac-library-rb/sig/ac_library_rb/graph.rbs +94 -0
  193. data/plugins/rigor-ac-library-rb/sig/ac_library_rb/math.rbs +42 -0
  194. data/plugins/rigor-ac-library-rb/sig/ac_library_rb/modint.rbs +71 -0
  195. data/plugins/rigor-ac-library-rb/sig/ac_library_rb/object.rbs +37 -0
  196. data/plugins/rigor-ac-library-rb/sig/ac_library_rb/segtree.rbs +83 -0
  197. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +2 -1
  198. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +29 -3
  199. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +5 -1
  200. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +100 -0
  201. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +94 -10
  202. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +43 -2
  203. data/plugins/rigor-activerecord/sig/active_record/relation.rbs +3 -1
  204. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +149 -0
  205. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +74 -0
  206. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +104 -0
  207. data/plugins/rigor-alba/lib/rigor/plugin/alba.rb +85 -0
  208. data/plugins/rigor-alba/lib/rigor-alba.rb +3 -0
  209. data/plugins/rigor-alba/sig/alba.rbs +4 -0
  210. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +2 -2
  211. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/target_detector.rb +29 -12
  212. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +7 -6
  213. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +130 -21
  214. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +6 -4
  215. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +4 -4
  216. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +1 -1
  217. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +1 -1
  218. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +31 -0
  219. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +102 -0
  220. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +87 -0
  221. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +76 -0
  222. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer.rb +73 -0
  223. data/plugins/rigor-typelizer/lib/rigor-typelizer.rb +3 -0
  224. data/sig/rigor/analysis/dependency_source_inference/index.rbs +1 -1
  225. data/sig/rigor/plugin/io_boundary.rbs +3 -0
  226. data/sig/rigor/scope.rbs +30 -4
  227. data/sig/rigor/source.rbs +4 -0
  228. data/sig/rigor.rbs +9 -4
  229. data/skills/rigor-project-init/references/02-configure.md +2 -2
  230. metadata +83 -1
@@ -135,6 +135,18 @@ module Rigor
135
135
  "body, not the override the subclass actually runs.",
136
136
  next_step: "Generate signatures for the overriding subclass too — widen the `sig-gen` path set to " \
137
137
  "cover it — and the parent's declaration becomes safe to emit."
138
+ ),
139
+ "sig.skipped.top-level-def" => Entry.new(
140
+ id: "sig.skipped.top-level-def",
141
+ summary: "A top-level `def` defines a private `Object` method, and sig-gen writes no signature for it.",
142
+ explanation: "Not a gap in inference. A `def` outside any `class` or `module` has no class to " \
143
+ "declare the member under, and Ruby makes it a private method on `Object`. sig-gen " \
144
+ "reports it instead of dropping it silently, so a file that defines only top-level " \
145
+ "methods is not mistaken for one that was never read. Whether to emit it as a private " \
146
+ "`Object` method is undecided (#1676); `def self.x` and `def` inside a block body are " \
147
+ "not reported.",
148
+ next_step: "Nothing is required. To type the method, move it into a module (`module_function` or " \
149
+ "`extend self` keeps the call sites), or write the `Object` declaration by hand in `sig/`."
138
150
  )
139
151
  }.freeze
140
152
 
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prism"
4
+ require_relative "symbol_name"
5
+
6
+ module Rigor
7
+ module Source
8
+ # The `[new_name, old_name]` pair a method-alias statement names, read off its syntax alone: the `alias` keyword
9
+ # (`alias to_m to_modint`, `alias :to_m :to_modint`) and an implicit-self `alias_method :to_m, :to_modint` call
10
+ # whose two arguments are Symbol or String literals. A computed name (`alias_method name, :x`, an interpolated
11
+ # symbol) is runtime data and reads as nil, so no walker files an alias for it.
12
+ #
13
+ # The one reader the per-file discovery walk ({Inference::ScopeIndexer}) and the `pre_eval:` pre-pass
14
+ # ({Inference::ProjectPatchedScanner}) share, so a patch file's aliases mean the same thing in both.
15
+ module AliasNames
16
+ module_function
17
+
18
+ # The pair for an `alias` keyword whose two names are plain symbols (a bare identifier parses as one), or nil.
19
+ def keyword_names(alias_node)
20
+ new_name = symbol_name(alias_node.new_name)
21
+ old_name = symbol_name(alias_node.old_name)
22
+ return nil if new_name.nil? || old_name.nil?
23
+
24
+ [new_name, old_name]
25
+ end
26
+
27
+ # The pair for an implicit-self `alias_method` call with two literal Symbol / String arguments, or nil.
28
+ def alias_method_call_names(call_node)
29
+ return nil unless call_node.name == :alias_method && call_node.receiver.nil?
30
+
31
+ args = call_node.arguments&.arguments
32
+ return nil unless args && args.size == 2
33
+
34
+ new_name = literal_name(args[0])
35
+ old_name = literal_name(args[1])
36
+ return nil if new_name.nil? || old_name.nil?
37
+
38
+ [new_name, old_name]
39
+ end
40
+
41
+ def symbol_name(node)
42
+ node.is_a?(Prism::SymbolNode) ? SymbolName.of(node.unescaped) : nil
43
+ end
44
+
45
+ def literal_name(node)
46
+ return nil unless node.is_a?(Prism::SymbolNode) || node.is_a?(Prism::StringNode)
47
+
48
+ SymbolName.of(node.unescaped)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "prism"
4
+ require_relative "symbol_name"
4
5
 
5
6
  module Rigor
6
7
  module Source
@@ -34,7 +35,7 @@ module Rigor
34
35
  def symbol_or_string(node)
35
36
  return nil unless node.is_a?(Prism::SymbolNode) || node.is_a?(Prism::StringNode)
36
37
 
37
- node.unescaped.to_sym
38
+ SymbolName.of(node.unescaped)
38
39
  end
39
40
 
40
41
  # The String a literal `Prism::SymbolNode` / `Prism::StringNode` names, or `nil` for any other node
@@ -53,7 +54,7 @@ module Rigor
53
54
  def symbol(node)
54
55
  return nil unless node.is_a?(Prism::SymbolNode)
55
56
 
56
- node.unescaped.to_sym
57
+ SymbolName.of(node.unescaped)
57
58
  end
58
59
 
59
60
  # The String a literal `Prism::SymbolNode` names, or `nil` for any other node (including a
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rigor
4
+ module Source
5
+ # The Symbol a name's text denotes, or `nil` when no Symbol can hold its bytes.
6
+ #
7
+ # `"\xff".to_sym` raises EncodingError, and a String literal may carry any bytes (a Symbol literal with invalid
8
+ # bytes is a syntax error, so no analysed file has one). Every reader of a literal name goes through here, so such
9
+ # a name reads as `nil` — the answer a computed name already gets — instead of aborting the file's analysis (#1821).
10
+ # `valid_encoding?` is the test, not `ascii_only?`: a name that is valid in its own encoding (a binary or Shift_JIS
11
+ # source) is a real Symbol.
12
+ module SymbolName
13
+ module_function
14
+
15
+ def of(text)
16
+ text.to_sym if text&.valid_encoding?
17
+ end
18
+ end
19
+ end
20
+ end
@@ -832,7 +832,8 @@ module Rigor
832
832
  # instead of several readings of it. The normative relation is
833
833
  # docs/type-specification/normalization.md § "Member absorption"; {absorbed_by?} is that list.
834
834
  #
835
- # Only a `FloatRange`, `Tuple` or `HashShape` member can be dropped, so the O(n²) scan is
835
+ # Only a `FloatRange`, `Tuple`, `HashShape` or element-carrying `Enumerator::ArithmeticSequence` member
836
+ # can be dropped (the last also joins with its kind, {join_sequence_elements}), so the O(n²) scan is
836
837
  # skipped for every union that carries none of them — this runs on the hot path of every join.
837
838
  #
838
839
  # The `!absorbed_by?(other, member)` re-check costs one predicate call per pair that absorbed
@@ -841,6 +842,7 @@ module Rigor
841
842
  def absorb_contained_members(members)
842
843
  return members unless members.any? { |m| absorbable_member?(m) }
843
844
 
845
+ members = join_sequence_elements(members)
844
846
  members.reject do |member|
845
847
  absorbable_member?(member) &&
846
848
  members.any? do |other|
@@ -850,7 +852,36 @@ module Rigor
850
852
  end
851
853
 
852
854
  def absorbable_member?(type)
853
- type.is_a?(FloatRange) || type.is_a?(Tuple) || type.is_a?(HashShape)
855
+ type.is_a?(FloatRange) || type.is_a?(Tuple) || type.is_a?(HashShape) || element_sequence?(type)
856
+ end
857
+
858
+ ARITHMETIC_SEQUENCE = "Enumerator::ArithmeticSequence"
859
+ private_constant :ARITHMETIC_SEQUENCE
860
+
861
+ # Issue #1794 — the `Enumerator::ArithmeticSequence[E]` block-less `Integer#step` returns, whose element is
862
+ # a Rigor-private type argument (`Nominal::RIGOR_PRIVATE_TYPE_ARGS`).
863
+ def element_sequence?(type)
864
+ type.is_a?(Nominal) && type.class_name == ARITHMETIC_SEQUENCE && type.type_args.size == 1
865
+ end
866
+
867
+ def plain_sequence?(type)
868
+ type.is_a?(Nominal) && type.class_name == ARITHMETIC_SEQUENCE && type.type_args.empty?
869
+ end
870
+
871
+ # Two element-carrying sequences join into one over the union of their elements, so a join of
872
+ # `1.step(10, 2)` and `1.step(10, 3)` stays one sequence whose block reads `Integer`, the answer each arm
873
+ # gives. (A plain sequence absorbing one with an element is the {absorbed_by?} clause.)
874
+ def join_sequence_elements(members)
875
+ sequences = members.select { |member| element_sequence?(member) }
876
+ return members if sequences.size < 2
877
+
878
+ joined = nominal_of(ARITHMETIC_SEQUENCE, type_args: [union(*sequences.map { |seq| seq.type_args.first })])
879
+ first = sequences.first
880
+ members.filter_map do |member|
881
+ if member.equal?(first) then joined
882
+ elsif !sequences.include?(member) then member
883
+ end
884
+ end
854
885
  end
855
886
 
856
887
  # `narrower`'s inhabitants are a subset of `wider`'s under a rule the union already applies to
@@ -864,6 +895,7 @@ module Rigor
864
895
  return true if listed_in_union?(narrower, wider)
865
896
 
866
897
  case narrower
898
+ when Nominal then element_sequence?(narrower) && plain_sequence?(wider)
867
899
  when FloatRange then float_range_absorbed_by?(narrower, wider)
868
900
  when Tuple then wider.is_a?(Tuple) && tuple_absorbed_by?(narrower, wider)
869
901
  when HashShape then wider.is_a?(HashShape) && hash_shape_absorbed_by?(narrower, wider)
@@ -11,7 +11,7 @@ module Rigor
11
11
  # bounds, never sentinels, because Ruby orders them. The range may exclude its end (`0.0...1.0`)
12
12
  # and never its beginning, because Ruby's literal has no exclusive begin.
13
13
  #
14
- # No bounded range contains NaN: `(a..b).cover?(Float::NAN)` is false for every `a` and `b`, so
14
+ # No bounded range contains NaN (`#covers?` answers false for it on every Ruby), so
15
15
  # the range from `-Float::INFINITY` to `Float::INFINITY` is "every Float except NaN"
16
16
  # (`non-nan-float`), not `Float`. The whole of `Float` (`nil..nil` in Ruby, which does cover NaN)
17
17
  # is never a carrier; the payload builder normalises it to `Nominal[Float]`.
@@ -58,6 +58,9 @@ module Rigor
58
58
  # displays as does.
59
59
  def covers?(value)
60
60
  return false unless value.is_a?(Float)
61
+ # NaN is in no bounded range, so answer false here: `Range#cover?(Float::NAN)` raises
62
+ # ArgumentError on ruby-head (4.1), where Ruby 4.0 returned false.
63
+ return false if value.nan?
61
64
 
62
65
  Range.new(min, max, @exclude_end).cover?(value)
63
66
  end
@@ -49,9 +49,14 @@ module Rigor
49
49
  "#{rendered_name}[#{rendered}]"
50
50
  end
51
51
 
52
+ # Classes RBS declares without a type parameter, on which Rigor carries one of its own: the element type of
53
+ # the `Enumerator::ArithmeticSequence` that `Integer#step` returns (issue #1794). RBS has no slot for it, so
54
+ # the argument erases away.
55
+ RIGOR_PRIVATE_TYPE_ARGS = %w[Enumerator::ArithmeticSequence].freeze
56
+
52
57
  def erase_to_rbs
53
58
  return "untyped" if AnonymousClassName.match?(class_name)
54
- return class_name if type_args.empty?
59
+ return class_name if type_args.empty? || RIGOR_PRIVATE_TYPE_ARGS.include?(class_name)
55
60
 
56
61
  rendered = type_args.map(&:erase_to_rbs).join(", ")
57
62
  "#{class_name}[#{rendered}]"
data/lib/rigor/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rigor
4
- VERSION = "0.4.1"
4
+ VERSION = "0.4.3"
5
5
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rigor/plugin"
4
+
5
+ module Rigor
6
+ module Plugin
7
+ # A pure RBS-bundle plugin for ac-library-rb, the Ruby port of the AtCoder Library. It emits no diagnostic and
8
+ # declares no producer: the manifest's `signature_paths: ["sig"]` adds the bundled `AcLibraryRb` signatures to the
9
+ # RBS environment.
10
+ #
11
+ # Inference cannot write these. The containers are generic over values the caller picks (a segment tree's monoid,
12
+ # a priority queue's elements), and their methods read parameters no call site in the library constrains, so
13
+ # `rigor sig-gen` over the gem's `lib_lock/` leaves 95 of its methods untyped. The signatures were written against
14
+ # that output and the gem's documentation, and `rigor check` over `lib_lock/` holds the method bodies to them.
15
+ class AcLibraryRb < Base
16
+ manifest(
17
+ id: "ac-library-rb",
18
+ target_gems: ["ac-library-rb"],
19
+ version: "0.1.0",
20
+ description: "RBS bundle for ac-library-rb (AtCoder Library): Segtree, LazySegtree, DSU, ModInt, flows " \
21
+ "and the math and string algorithms.",
22
+ signature_paths: ["sig"]
23
+ )
24
+ end
25
+ end
26
+ end
27
+
28
+ Rigor::Plugin.register(Rigor::Plugin::AcLibraryRb)
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rigor/plugin/ac_library_rb"
@@ -0,0 +1,7 @@
1
+ module AcLibraryRb
2
+ class Convolution
3
+ def initialize: (?Integer mod, ?Integer? primitive_root) -> void
4
+
5
+ def convolution: (Array[Integer] a, Array[Integer] b) -> Array[Integer]
6
+ end
7
+ end
@@ -0,0 +1,160 @@
1
+ module AcLibraryRb
2
+ class DSU
3
+ attr_reader parent_or_size: Array[Integer]
4
+ attr_reader n: Integer
5
+
6
+ def initialize: (?Integer n) -> void
7
+
8
+ # The leader of the merged component.
9
+ def merge: (Integer a, Integer b) -> Integer
10
+ alias unite merge
11
+
12
+ def same?: (Integer a, Integer b) -> bool
13
+ alias same same?
14
+
15
+ def leader: (Integer a) -> Integer
16
+ alias root leader
17
+ alias find leader
18
+
19
+ # Like `leader`, growing the set to hold `a` when it is out of range.
20
+ def []: (Integer a) -> Integer
21
+
22
+ def size: (Integer a) -> Integer
23
+
24
+ def groups: () -> Array[Array[Integer]]
25
+
26
+ def to_s: () -> String
27
+ end
28
+
29
+ class UnionFind = DSU
30
+
31
+ # Element values are whatever the caller adds: the tree starts from `0` and adds with `+`, so an
32
+ # `Integer`, a `Float` and a `ModInt` all work.
33
+ class FenwickTree
34
+ attr_reader data: Array[untyped]
35
+ attr_reader size: Integer
36
+
37
+ def initialize: (Array[untyped] | Integer arg) -> void
38
+
39
+ def add: (Integer i, untyped x) -> void
40
+
41
+ def sum: (Integer l, Integer r) -> untyped
42
+ | (Integer r) -> untyped
43
+ | (Range[Integer] range) -> untyped
44
+
45
+ def _sum: (Integer i) -> untyped
46
+ alias left_sum _sum
47
+
48
+ def to_s: () -> String
49
+ end
50
+
51
+ # A binary heap that pops the highest-priority element first: the largest by default, or the first
52
+ # argument of the block when it returns truthy.
53
+ class PriorityQueue[T]
54
+ # The factories answer `PriorityQueue[untyped]`: bound from a literal array, `T` would be the literals it
55
+ # starts with, and a later `pop` would read as one of them.
56
+ def self.max: (Array[untyped] array) -> PriorityQueue[untyped]
57
+
58
+ def self.min: (Array[untyped] array) -> PriorityQueue[untyped]
59
+
60
+ def self.[]: (*untyped array) ?{ (untyped, untyped) -> boolish } -> PriorityQueue[untyped]
61
+
62
+ def initialize: (?Array[T] array) ?{ (T, T) -> boolish } -> void
63
+
64
+ attr_reader heap: Array[T]
65
+ alias to_a heap
66
+
67
+ def push: (T item) -> self
68
+ alias << push
69
+ alias append push
70
+
71
+ def pop: () -> T?
72
+
73
+ def get: () -> T?
74
+ alias top get
75
+ alias first get
76
+
77
+ def empty?: () -> bool
78
+
79
+ def size: () -> Integer
80
+
81
+ def to_s: () -> String
82
+ end
83
+
84
+ class HeapQueue = PriorityQueue
85
+
86
+ class Deque[T]
87
+ include Enumerable[T]
88
+
89
+ def self.[]: (*untyped args) -> Deque[untyped]
90
+
91
+ def initialize: (?Array[T] arg, ?untyped value, ?initial_capacity: Integer) -> void
92
+ | (Integer size, ?T value, ?initial_capacity: Integer) -> void
93
+
94
+ def empty?: () -> bool
95
+
96
+ def size: () -> Integer
97
+ alias length size
98
+
99
+ def <<: (T x) -> self
100
+
101
+ def push: (*T args) -> self
102
+ alias append push
103
+
104
+ def unshift: (*T args) -> self
105
+ alias prepend unshift
106
+
107
+ def pop: () -> T?
108
+
109
+ def shift: () -> T?
110
+
111
+ def last: () -> T?
112
+
113
+ def slice: (Integer idx) -> T?
114
+
115
+ def []: (Integer idx) -> T?
116
+ | (Integer start, Integer length) -> Deque[T]?
117
+ | (Range[Integer] range) -> Deque[T]?
118
+
119
+ def at: (Integer idx) -> T?
120
+
121
+ def []=: (Integer idx, T value) -> T
122
+
123
+ def ==: (untyped other) -> bool
124
+
125
+ def hash: () -> Integer
126
+
127
+ def reverse: () -> Deque[T]
128
+
129
+ def reverse!: () -> self
130
+
131
+ def reversed?: () -> bool
132
+
133
+ def dig: (int idx, *untyped) -> untyped
134
+
135
+ def each: () { (T) -> void } -> Array[T]
136
+ | () -> Enumerator[T, Array[T]]
137
+
138
+ def clear: () -> self
139
+
140
+ def join: (?String? sep) -> String
141
+
142
+ def rotate!: (?Integer cnt) -> self
143
+
144
+ def rotate: (?Integer cnt) -> Deque[T]
145
+
146
+ def sample: () -> T?
147
+
148
+ def shuffle: () -> Deque[T]
149
+
150
+ def replace: (_ToA[T] other) -> self
151
+
152
+ def swap: (Integer i, Integer j) -> self
153
+
154
+ def to_a: () -> Array[T]
155
+
156
+ def to_s: () -> String
157
+
158
+ def inspect: () -> String
159
+ end
160
+ end
@@ -0,0 +1,94 @@
1
+ module AcLibraryRb
2
+ class MaxFlow
3
+ def initialize: (Integer n) -> void
4
+
5
+ # The index of the added edge.
6
+ def add_edge: (Integer from, Integer to, Integer cap) -> Integer
7
+
8
+ def add_edges: (_Each[[Integer, Integer, Integer]] edges) -> self
9
+
10
+ def add: (Integer from, Integer to, Integer cap) -> Integer
11
+ | (_Each[[Integer, Integer, Integer]] edges) -> self
12
+
13
+ # `[from, to, cap]`, as `gets.split.map(&:to_i)` reads it.
14
+ def push: (Array[Integer] edge) -> Integer
15
+ alias << push
16
+
17
+ # `[from, to, cap, flow]` of the `i`-th edge.
18
+ def []: (Integer i) -> [Integer, Integer, Integer, Integer]
19
+ alias get_edge []
20
+ alias edge []
21
+
22
+ def edges: () -> Array[[Integer, Integer, Integer, Integer]]
23
+
24
+ def change_edge: (Integer i, Integer new_cap, Integer new_flow) -> void
25
+
26
+ # A `Float` limit the capacities do not bound comes back as the flow.
27
+ def flow: (Integer s, Integer t, ?Integer flow_limit) -> Integer
28
+ | (Integer s, Integer t, (Integer | Float) flow_limit) -> (Integer | Float)
29
+ alias max_flow flow
30
+
31
+ # Whether each vertex is reachable from `s` in the residual graph.
32
+ def min_cut: (Integer s) -> Array[bool]
33
+ end
34
+
35
+ class MinCostFlow
36
+ def initialize: (Integer n) -> void
37
+
38
+ # The index of the added edge.
39
+ def add_edge: (Integer from, Integer to, Integer cap, Integer cost) -> Integer
40
+
41
+ def add_edges: (_Each[[Integer, Integer, Integer, Integer]] edges) -> self
42
+
43
+ def add: (Integer from, Integer to, Integer cap, Integer cost) -> Integer
44
+ | (_Each[[Integer, Integer, Integer, Integer]] edges) -> self
45
+
46
+ # `[from, to, cap, flow, cost]` of the `i`-th edge.
47
+ def get_edge: (Integer i) -> [Integer, Integer, Integer, Integer, Integer]
48
+ alias edge get_edge
49
+ alias [] get_edge
50
+
51
+ def edges: () -> Array[[Integer, Integer, Integer, Integer, Integer]]
52
+
53
+ # `[flow, cost]`.
54
+ # The default limit is `Float::MAX`, which the capacities always bound; a smaller `Float` limit may not.
55
+ def flow: (Integer s, Integer t, ?Integer flow_limit) -> [Integer, Integer]
56
+ | (Integer s, Integer t, (Integer | Float) flow_limit) -> [Integer | Float, Integer | Float]
57
+ alias min_cost_max_flow flow
58
+
59
+ def dual_ref: (Integer s, Integer t) -> bool
60
+
61
+ # The `[flow, cost]` breakpoints of the cost curve.
62
+ def slope: (Integer s, Integer t, ?Integer flow_limit) -> Array[[Integer, Integer]]
63
+ | (Integer s, Integer t, (Integer | Float) flow_limit) -> Array[[Integer | Float, Integer | Float]]
64
+ alias min_cost_slop slope
65
+ end
66
+
67
+ class SCC
68
+ def initialize: (Integer n) -> void
69
+
70
+ def add_edge: (Integer from, Integer to) -> self
71
+
72
+ def add_edges: (_Each[[Integer, Integer]] edges) -> self
73
+
74
+ def add: (Integer from, Integer to) -> self
75
+ | (_Each[[Integer, Integer]] edges) -> self
76
+
77
+ # The strongly connected components in topological order.
78
+ def scc: () -> Array[Array[Integer]]
79
+ end
80
+
81
+ class TwoSAT
82
+ def initialize: (Integer n) -> void
83
+
84
+ # One assignment per variable once `satisfiable?` has answered true; `nil` before that.
85
+ attr_reader answer: Array[bool?]
86
+
87
+ def add_clause: (Integer i, boolish f, Integer j, boolish g) -> nil
88
+
89
+ def satisfiable?: () -> bool
90
+ alias satisfiable satisfiable?
91
+ end
92
+
93
+ class TwoSat = TwoSAT
94
+ end
@@ -0,0 +1,42 @@
1
+ # ac-library-rb's math and string algorithms, instance methods of `AcLibraryRb` that a caller reaches by
2
+ # including the module.
3
+ module AcLibraryRb
4
+ # A callable taking two arguments: a `Proc`, a lambda or a `Method`.
5
+ interface _Call2[A, B, R]
6
+ def call: (A, B) -> R
7
+ end
8
+
9
+ # An object `Deque#replace` can read elements from.
10
+ interface _ToA[T]
11
+ def to_a: () -> Array[T]
12
+ end
13
+
14
+ def convolution: (Array[Integer] a, Array[Integer] b, ?mod: Integer, ?k: Integer, ?z: Integer) -> Array[Integer]
15
+
16
+ # `[remainder, modulus]`, or `[0, 0]` when there is no solution.
17
+ def crt: (Array[Integer] r, Array[Integer] m) -> [Integer, Integer]
18
+
19
+ def inv_gcd: (Integer a, Integer b) -> [Integer, Integer]
20
+
21
+ def floor_sum: (Integer n, Integer m, Integer a, Integer b) -> Integer
22
+
23
+ def floor_sum_unsigned: (Integer n, Integer m, Integer a, Integer b) -> Integer
24
+
25
+ def inv_mod: (Integer x, Integer m) -> Integer
26
+
27
+ def _inv_gcd: (Integer a, Integer b) -> [Integer, Integer]
28
+
29
+ def pow_mod: (Integer x, Integer n, Integer m) -> Integer
30
+
31
+ def suffix_array: (String s) -> Array[Integer]
32
+ | (Array[untyped] a, ?Integer? upper) -> Array[Integer]
33
+
34
+ def sa_is: (Array[Integer] s, Integer upper) -> Array[Integer]
35
+
36
+ def sa_is_induce: (Array[Integer] s, Array[bool] ls, Array[Integer] sum_l, Array[Integer] sum_s,
37
+ Array[Integer] lms) -> Array[Integer]
38
+
39
+ def lcp_array: (String | Array[untyped] s, Array[Integer] sa) -> Array[Integer]
40
+
41
+ def z_algorithm: (String | Array[untyped] s) -> Array[Integer]
42
+ end
@@ -0,0 +1,71 @@
1
+ module AcLibraryRb
2
+ # An integer modulo the global modulus set by `ModInt.set_mod`.
3
+ class ModInt < ::Numeric
4
+ def self.set_mod: (Integer mod) -> bool
5
+
6
+ def self.mod=: (Integer mod) -> bool
7
+
8
+ def self.mod: () -> Integer
9
+
10
+ def self.raw: (_ToI val) -> ModInt
11
+
12
+ def self.prime?: (Integer n) -> bool
13
+
14
+ def self.inv_gcd: (Integer a, Integer b) -> [Integer, Integer]
15
+
16
+ attr_accessor val: Integer
17
+ alias to_i val
18
+
19
+ def initialize: (?_ToI val) -> void
20
+
21
+ def inc!: () -> self
22
+
23
+ def dec!: () -> self
24
+
25
+ def add!: (_ToI other) -> self
26
+
27
+ def sub!: (_ToI other) -> self
28
+
29
+ def mul!: (_ToI other) -> self
30
+
31
+ def div!: (_ToI other) -> self
32
+
33
+ def +@: () -> self
34
+
35
+ def -@: () -> ModInt
36
+
37
+ # Answers the Integer `0` when the modulus is 1. Declared `ModInt` all the same: a union return reached
38
+ # `1 + m ** 2` as an argument, where overload selection reads a union whole (#1356), typed the sum `Integer`
39
+ # and reported `call.undefined-method` for `.val` on correct code.
40
+ def **: (Integer other) -> ModInt
41
+ alias pow **
42
+
43
+ def inv: () -> ModInt
44
+
45
+ def coerce: (_ToI other) -> [ModInt, ModInt]
46
+
47
+ def +: (_ToI other) -> ModInt
48
+
49
+ def -: (_ToI other) -> ModInt
50
+
51
+ def *: (_ToI other) -> ModInt
52
+
53
+ def /: (_ToI other) -> ModInt
54
+
55
+ def ==: (untyped other) -> bool
56
+
57
+ def pred: () -> ModInt
58
+
59
+ def succ: () -> ModInt
60
+
61
+ def zero?: () -> bool
62
+
63
+ def dup: () -> ModInt
64
+
65
+ def to_int: () -> Integer
66
+
67
+ def to_s: () -> String
68
+
69
+ def inspect: () -> String
70
+ end
71
+ end
@@ -0,0 +1,37 @@
1
+ # The core extensions under `ac-library-rb/core_ext/`. `ac-library-rb/modint` loads the `ModInt` conversions;
2
+ # `Integer#divisors` and the `Array` conversions need `ac-library-rb/core_ext/all` (or `integer`).
3
+ class Object
4
+ private
5
+
6
+ def ModInt: (_ToI val) -> AcLibraryRb::ModInt
7
+ end
8
+
9
+ class Array[unchecked out Elem]
10
+ def to_fenwick_tree: () -> AcLibraryRb::FenwickTree
11
+ alias to_fetree to_fenwick_tree
12
+
13
+ # The element type is not carried over: a literal array would pin it to the literals it starts with.
14
+ def to_priority_queue: () -> AcLibraryRb::PriorityQueue[untyped]
15
+ alias to_pq to_priority_queue
16
+ end
17
+
18
+ class Integer
19
+ # `ModInt#coerce` makes an Integer on the left answer a `ModInt`.
20
+ def +: (AcLibraryRb::ModInt) -> AcLibraryRb::ModInt | ...
21
+ def -: (AcLibraryRb::ModInt) -> AcLibraryRb::ModInt | ...
22
+ def *: (AcLibraryRb::ModInt) -> AcLibraryRb::ModInt | ...
23
+ def /: (AcLibraryRb::ModInt) -> AcLibraryRb::ModInt | ...
24
+
25
+ def divisors: () -> Array[Integer]
26
+
27
+ def each_divisor: () { (Integer) -> void } -> Array[Integer]
28
+ | () -> Enumerator[Integer, Array[Integer]]
29
+
30
+ def to_modint: () -> AcLibraryRb::ModInt
31
+ alias to_m to_modint
32
+ end
33
+
34
+ class String
35
+ def to_modint: () -> AcLibraryRb::ModInt
36
+ alias to_m to_modint
37
+ end