rigortype 0.3.5 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (266) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -5
  3. data/data/builtins/ruby_core/date.yml +6 -3
  4. data/data/builtins/ruby_core/enumerable.yml +1 -0
  5. data/data/builtins/ruby_core/exception.yml +2 -1
  6. data/data/builtins/ruby_core/file.yml +28 -14
  7. data/data/builtins/ruby_core/hash.yml +5 -2
  8. data/data/builtins/ruby_core/io.yml +33 -16
  9. data/data/builtins/ruby_core/random.yml +2 -1
  10. data/data/builtins/ruby_core/re.yml +6 -3
  11. data/data/builtins/ruby_core/struct.yml +2 -0
  12. data/data/builtins/ruby_core/time.yml +2 -1
  13. data/data/gem_overlay/activesupport/core_ext.rbs +442 -11
  14. data/docs/handbook/02-everyday-types.md +1 -1
  15. data/docs/handbook/04-tuples-and-shapes.md +1 -1
  16. data/docs/handbook/08-understanding-errors.md +1 -1
  17. data/docs/handbook/09-plugins.md +2 -2
  18. data/docs/handbook/10-sorbet.md +1 -1
  19. data/docs/handbook/README.md +2 -2
  20. data/docs/handbook/appendix-go.md +1 -1
  21. data/docs/handbook/appendix-java-csharp.md +2 -2
  22. data/docs/handbook/appendix-mypy.md +1 -1
  23. data/docs/handbook/appendix-protocols-and-structural-typing.md +2 -2
  24. data/docs/handbook/appendix-rust.md +1 -1
  25. data/docs/handbook/appendix-type-theory.md +2 -2
  26. data/docs/handbook/appendix-typescript.md +4 -4
  27. data/docs/manual/02-cli-reference.md +29 -16
  28. data/docs/manual/03-configuration.md +1 -1
  29. data/docs/manual/04-diagnostics.md +12 -0
  30. data/docs/manual/05-inspecting-types.md +20 -8
  31. data/docs/manual/07-plugins.md +4 -0
  32. data/docs/manual/08-skills.md +1 -1
  33. data/docs/manual/11-ci.md +1 -1
  34. data/docs/manual/12-caching.md +1 -1
  35. data/docs/manual/15-type-protection-coverage.md +1 -1
  36. data/docs/manual/18-removing-dead-code.md +13 -4
  37. data/docs/manual/19-effect-labels.md +19 -12
  38. data/docs/manual/README.md +2 -2
  39. data/docs/manual/plugins/rigor-actionmailer.md +4 -4
  40. data/docs/manual/plugins/rigor-actionpack.md +61 -0
  41. data/docs/manual/plugins/rigor-activejob.md +3 -3
  42. data/docs/manual/plugins/rigor-activerecord.md +78 -5
  43. data/docs/manual/plugins/rigor-activesupport-core-ext.md +114 -12
  44. data/docs/manual/plugins/rigor-rails-i18n.md +5 -5
  45. data/docs/manual/plugins/rigor-rspec.md +6 -2
  46. data/docs/manual/plugins/rigor-sidekiq.md +22 -0
  47. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +12 -0
  48. data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +98 -0
  49. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +3 -3
  50. data/lib/rigor/analysis/check_rules/published_constant_guard.rb +199 -0
  51. data/lib/rigor/analysis/check_rules/rule_walk.rb +1 -2
  52. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +2 -2
  53. data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +6 -12
  54. data/lib/rigor/analysis/check_rules.rb +383 -48
  55. data/lib/rigor/analysis/crash_signature.rb +116 -0
  56. data/lib/rigor/analysis/dependency_recorder.rb +18 -3
  57. data/lib/rigor/analysis/dependency_source_inference/walker.rb +2 -1
  58. data/lib/rigor/analysis/diagnostic.rb +17 -6
  59. data/lib/rigor/analysis/effects_cache_probe.rb +132 -0
  60. data/lib/rigor/analysis/incremental.rb +28 -0
  61. data/lib/rigor/analysis/incremental_session.rb +44 -9
  62. data/lib/rigor/analysis/plugin_fact_fingerprint.rb +1 -2
  63. data/lib/rigor/analysis/reachability/graph.rb +44 -16
  64. data/lib/rigor/analysis/reachability/plugin_roots.rb +11 -8
  65. data/lib/rigor/analysis/reachability/scan.rb +27 -7
  66. data/lib/rigor/analysis/reachability/scan_cache.rb +130 -0
  67. data/lib/rigor/analysis/result.rb +32 -0
  68. data/lib/rigor/analysis/run_cache_key.rb +53 -1
  69. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +93 -1
  70. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +14 -8
  71. data/lib/rigor/analysis/runner/pool_coordinator.rb +34 -0
  72. data/lib/rigor/analysis/runner/project_pre_passes.rb +15 -11
  73. data/lib/rigor/analysis/runner/run_snapshots.rb +7 -2
  74. data/lib/rigor/analysis/runner.rb +298 -38
  75. data/lib/rigor/analysis/worker_session.rb +10 -1
  76. data/lib/rigor/bleeding_edge.rb +7 -6
  77. data/lib/rigor/builtins/hkt_builtins.rb +45 -6
  78. data/lib/rigor/builtins/predefined_constant_refinements.rb +108 -59
  79. data/lib/rigor/builtins/static_return_refinements.rb +20 -1
  80. data/lib/rigor/cache/descriptor.rb +42 -2
  81. data/lib/rigor/cache/file_digest.rb +20 -2
  82. data/lib/rigor/cache/incremental_snapshot.rb +40 -3
  83. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +31 -8
  84. data/lib/rigor/cache/store.rb +10 -1
  85. data/lib/rigor/cli/check_command.rb +67 -48
  86. data/lib/rigor/cli/coverage_command.rb +28 -24
  87. data/lib/rigor/cli/coverage_mutation.rb +13 -17
  88. data/lib/rigor/cli/coverage_scan.rb +47 -6
  89. data/lib/rigor/cli/doc_links.rb +100 -0
  90. data/lib/rigor/cli/docs_command.rb +32 -2
  91. data/lib/rigor/cli/effects_command.rb +27 -2
  92. data/lib/rigor/cli/effects_diff_renderer.rb +82 -12
  93. data/lib/rigor/cli/effects_explain_renderer.rb +6 -3
  94. data/lib/rigor/cli/effects_snapshot_command.rb +52 -7
  95. data/lib/rigor/cli/fused_protection_renderer.rb +19 -2
  96. data/lib/rigor/cli/fused_protection_report.rb +23 -2
  97. data/lib/rigor/cli/measurement_integrity_warning.rb +57 -0
  98. data/lib/rigor/cli/mutation_protection_renderer.rb +22 -2
  99. data/lib/rigor/cli/mutation_protection_report.rb +31 -2
  100. data/lib/rigor/cli/sig_gen_command.rb +18 -0
  101. data/lib/rigor/cli/type_of_command.rb +188 -41
  102. data/lib/rigor/cli/type_of_renderer.rb +72 -3
  103. data/lib/rigor/cli/type_scan_command.rb +10 -1
  104. data/lib/rigor/cli/unused_command.rb +65 -16
  105. data/lib/rigor/cli.rb +22 -9
  106. data/lib/rigor/configuration.rb +7 -4
  107. data/lib/rigor/effects/definition_lines.rb +100 -0
  108. data/lib/rigor/effects/envelope_check.rb +18 -1
  109. data/lib/rigor/effects/liskov_check.rb +17 -8
  110. data/lib/rigor/effects/plugin_facts.rb +1 -1
  111. data/lib/rigor/effects/scanner.rb +2 -3
  112. data/lib/rigor/effects/signature_sources.rb +13 -2
  113. data/lib/rigor/effects/snapshot.rb +53 -21
  114. data/lib/rigor/effects/snapshot_diff.rb +26 -3
  115. data/lib/rigor/environment/bundle_sig_discovery.rb +64 -13
  116. data/lib/rigor/environment/lockfile_resolver.rb +15 -5
  117. data/lib/rigor/environment/missing_gem_constant_index.rb +45 -4
  118. data/lib/rigor/environment/rbs_hierarchy.rb +16 -22
  119. data/lib/rigor/environment/rbs_loader.rb +525 -46
  120. data/lib/rigor/environment.rb +38 -15
  121. data/lib/rigor/inference/acceptance.rb +172 -20
  122. data/lib/rigor/inference/captured_locals.rb +62 -0
  123. data/lib/rigor/inference/content_join.rb +347 -0
  124. data/lib/rigor/inference/def_handle.rb +15 -3
  125. data/lib/rigor/inference/def_node_resolver.rb +62 -3
  126. data/lib/rigor/inference/expression_typer.rb +1020 -141
  127. data/lib/rigor/inference/fork_map.rb +6 -1
  128. data/lib/rigor/inference/hkt_reducer.rb +12 -1
  129. data/lib/rigor/inference/hkt_registry.rb +46 -0
  130. data/lib/rigor/inference/hkt_sugar_translator.rb +93 -0
  131. data/lib/rigor/inference/index_write_widening.rb +48 -0
  132. data/lib/rigor/inference/indexed_narrowing.rb +14 -0
  133. data/lib/rigor/inference/method_dispatcher/block_folding.rb +7 -5
  134. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +10 -0
  135. data/lib/rigor/inference/method_dispatcher/json_folding.rb +58 -0
  136. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +81 -38
  137. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +54 -17
  138. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +1 -2
  139. data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +63 -0
  140. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +63 -10
  141. data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +104 -0
  142. data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +81 -0
  143. data/lib/rigor/inference/method_dispatcher.rb +155 -14
  144. data/lib/rigor/inference/method_parameter_binder.rb +3 -3
  145. data/lib/rigor/inference/mutation_widening.rb +244 -174
  146. data/lib/rigor/inference/narrowing.rb +331 -27
  147. data/lib/rigor/inference/parameter_inference_collector.rb +1 -0
  148. data/lib/rigor/inference/pre_eval_constants.rb +15 -0
  149. data/lib/rigor/inference/precision_scanner.rb +5 -2
  150. data/lib/rigor/inference/project_patched_scanner.rb +2 -1
  151. data/lib/rigor/inference/rbs_type_translator.rb +89 -39
  152. data/lib/rigor/inference/scope_indexer.rb +1493 -140
  153. data/lib/rigor/inference/statement_evaluator.rb +276 -91
  154. data/lib/rigor/inference/struct_fold_safety.rb +186 -27
  155. data/lib/rigor/inference/synthetic_method_scanner.rb +7 -0
  156. data/lib/rigor/inference/version_guard.rb +229 -0
  157. data/lib/rigor/language_server/selection_range_provider.rb +1 -1
  158. data/lib/rigor/plugin/base.rb +3 -2
  159. data/lib/rigor/plugin/inflector.rb +14 -5
  160. data/lib/rigor/plugin/io_boundary.rb +107 -4
  161. data/lib/rigor/plugin/loader.rb +14 -0
  162. data/lib/rigor/protection/analysis_guard.rb +60 -0
  163. data/lib/rigor/protection/closure_kill_oracle.rb +43 -38
  164. data/lib/rigor/protection/diagnostic_oracle.rb +12 -4
  165. data/lib/rigor/protection/discovery_seed.rb +1 -1
  166. data/lib/rigor/protection/measurement_integrity.rb +36 -0
  167. data/lib/rigor/protection/mutation_scanner.rb +61 -6
  168. data/lib/rigor/protection/mutator.rb +121 -18
  169. data/lib/rigor/rbs_extended/envelope_scanner.rb +9 -0
  170. data/lib/rigor/reflection.rb +182 -30
  171. data/lib/rigor/scope/discovery_index.rb +39 -1
  172. data/lib/rigor/scope.rb +385 -4
  173. data/lib/rigor/sig_gen/classification.rb +12 -1
  174. data/lib/rigor/sig_gen/generator.rb +254 -5
  175. data/lib/rigor/sig_gen/renderer.rb +1 -6
  176. data/lib/rigor/sig_gen/writer.rb +3 -0
  177. data/lib/rigor/source/constant_path.rb +79 -2
  178. data/lib/rigor/triage/catalogue.rb +1 -1
  179. data/lib/rigor/type/combinator.rb +10 -0
  180. data/lib/rigor/type/maybe.rb +47 -0
  181. data/lib/rigor/type/refined.rb +1 -2
  182. data/lib/rigor/type/result.rb +53 -0
  183. data/lib/rigor/type.rb +2 -0
  184. data/lib/rigor/version.rb +1 -1
  185. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +3 -1
  186. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +58 -8
  187. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +15 -2
  188. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +4 -1
  189. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +3 -1
  190. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +39 -14
  191. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +36 -3
  192. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -2
  193. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +21 -5
  194. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +47 -8
  195. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +13 -3
  196. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +242 -4
  197. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +3 -1
  198. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +7 -3
  199. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +62 -11
  200. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +15 -2
  201. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +3 -1
  202. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +5 -1
  203. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -2
  204. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +798 -22
  205. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +156 -13
  206. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +140 -40
  207. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +2 -2
  208. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +20 -6
  209. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +14 -4
  210. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +6 -3
  211. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +23 -6
  212. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +264 -9
  213. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +876 -23
  214. data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +99 -0
  215. data/plugins/rigor-dry-monads/lib/rigor-dry-monads.rb +5 -0
  216. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +34 -0
  217. data/plugins/rigor-ethon/lib/rigor-ethon.rb +3 -0
  218. data/plugins/rigor-ethon/sig/ethon.rbs +27 -0
  219. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +3 -2
  220. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +198 -0
  221. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/binding_recognizer.rb +75 -0
  222. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/catalog.rb +64 -0
  223. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +173 -0
  224. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/target_detector.rb +46 -0
  225. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +161 -0
  226. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +121 -0
  227. data/plugins/rigor-ffi/lib/rigor-ffi.rb +3 -0
  228. data/plugins/rigor-ffi/sig/ffi.rbs +67 -0
  229. data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +19 -0
  230. data/plugins/rigor-ffi-rzmq/lib/rigor-ffi-rzmq.rb +3 -0
  231. data/plugins/rigor-ffi-rzmq/sig/ffi_rzmq.rbs +29 -0
  232. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +2 -4
  233. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -1
  234. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
  235. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +2 -1
  236. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +2 -1
  237. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +2 -1
  238. data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +156 -11
  239. data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +30 -0
  240. data/plugins/rigor-rbnacl/lib/rigor-rbnacl.rb +3 -0
  241. data/plugins/rigor-rbnacl/sig/rbnacl.rbs +22 -0
  242. data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +55 -0
  243. data/plugins/rigor-sassc/lib/rigor-sassc.rb +3 -0
  244. data/plugins/rigor-sassc/sig/sassc.rbs +24 -0
  245. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +32 -11
  246. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +3 -1
  247. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +4 -1
  248. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +63 -10
  249. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +15 -2
  250. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +61 -2
  251. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +2 -2
  252. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +4 -2
  253. data/sig/rigor/analysis/baseline.rbs +1 -1
  254. data/sig/rigor/environment.rbs +3 -0
  255. data/sig/rigor/inference.rbs +4 -1
  256. data/sig/rigor/plugin/io_boundary.rbs +2 -0
  257. data/sig/rigor/reflection.rbs +3 -1
  258. data/sig/rigor/scope.rbs +25 -1
  259. data/sig/rigor/type.rbs +95 -1
  260. data/sig/rigor.rbs +16 -0
  261. data/skills/rigor-ci-setup/SKILL.md +2 -2
  262. data/skills/rigor-editor-setup/SKILL.md +2 -2
  263. data/skills/rigor-mcp-setup/SKILL.md +2 -2
  264. data/skills/rigor-monkeypatch-resolve/SKILL.md +1 -1
  265. data/skills/rigor-plugin-review/SKILL.md +3 -3
  266. metadata +73 -3
@@ -65,6 +65,9 @@ class Object
65
65
  # `acts_like?(:string)` / `acts_like?(:date)` / `acts_like?(:time)`
66
66
  # is ActiveSupport's "duck-typing helper" predicate.
67
67
  def acts_like?: (Symbol | String) -> bool
68
+
69
+ # `core_ext/object/inclusion` — `x.in?([a, b])` / `x.in?(1..10)`.
70
+ def in?: (untyped) -> bool
68
71
  end
69
72
 
70
73
  # `nil.blank?` / `nil.present?` / `nil.try` are the most frequent
@@ -75,6 +78,8 @@ class NilClass
75
78
  def presence: () -> nil
76
79
  def try: (*untyped) -> nil
77
80
  def try!: (*untyped) -> nil
81
+ def duplicable?: () -> true
82
+ def instance_values: () -> Hash[String, untyped]
78
83
  end
79
84
 
80
85
  class TrueClass
@@ -108,6 +113,9 @@ class String
108
113
  def pluralize: (?Integer count, ?Symbol locale) -> String
109
114
  def singularize: (?Symbol locale) -> String
110
115
  def humanize: (?capitalize: bool, ?keep_id_suffix: bool) -> String
116
+ def titlecase: () -> String
117
+ def dasherize: () -> String
118
+ def upcase_first: () -> String
111
119
 
112
120
  # `core_ext/string/filters`
113
121
  def squish: () -> String
@@ -115,6 +123,8 @@ class String
115
123
  def truncate: (Integer truncate_at, ?omission: String, ?separator: String | Regexp | nil) -> String
116
124
  def truncate_bytes: (Integer truncate_at, ?omission: String) -> String
117
125
  def truncate_words: (Integer words_count, ?omission: String, ?separator: String | Regexp | nil) -> String
126
+ def remove: (*Regexp | String patterns) -> String
127
+ def remove!: (*Regexp | String patterns) -> String
118
128
 
119
129
  # `core_ext/string/output_safety` — `html_safe` returns an
120
130
  # `ActiveSupport::SafeBuffer` (a String subclass). The closest
@@ -236,6 +246,77 @@ class Float
236
246
  def gigabytes: () -> Float
237
247
  end
238
248
 
249
+ # ---------------------------------------------------------------
250
+ # ActiveSupport::Duration — issue #632
251
+ # ---------------------------------------------------------------
252
+
253
+ # Declaring this class at all is normally the wrong move: `Duration` forwards any method its own class
254
+ # doesn't define to the wrapped numeric via `method_missing` (audited against ActiveSupport 8.1.3.1's
255
+ # `lib/active_support/duration.rb`), so an incomplete declaration would turn every omitted member into a
256
+ # false `call.undefined-method` for every project that locks activesupport. It is safe here only because
257
+ # `ActiveSupport::Duration` is listed in `Rigor::Analysis::CheckRules::GEM_OVERLAY_OPEN_RECEIVERS`
258
+ # (`lib/rigor/analysis/check_rules.rb`) — an open-receiver membership for exactly this bundle, gated on this
259
+ # overlay directory actually having been loaded, since this overlay has no plugin manifest of its own for
260
+ # an ADR-26 `open_receivers:` entry to live on (issue #660 tracks whether that gap gets a first-class
261
+ # mechanism instead of this constant). What follows is the reader surface, mirroring
262
+ # `rigor-activesupport-core-ext/sig/active_support/core_ext.rbs` (parity enforced by
263
+ # `spec/rigor/environment/activesupport_overlay_parity_spec.rb`); every method not listed here stays
264
+ # undeclared and silently `Dynamic` rather than guessed.
265
+ module ActiveSupport
266
+ class Duration
267
+ # The `ago` family (issue #659). `since(time = ::Time.current)` and `ago(time = ::Time.current)` are
268
+ # the only two real methods; `from_now` / `after` alias the first and `until` / `before` the second,
269
+ # so all six share one signature. They were declared once before and removed, because typing them
270
+ # `Time` while the Rails `Time` INSTANCE surface was undeclared made `1.hour.ago.to_fs(:db)` —
271
+ # ordinary Rails — a false `call.undefined-method` on the returned `Time`, which
272
+ # `GEM_OVERLAY_OPEN_RECEIVERS` cannot suppress since it protects a Duration receiver, not a `Time`
273
+ # one. #658 declared that surface and #670 the `Date` / `DateTime` twins, so the chains resolve now.
274
+ #
275
+ # `Time` is the right class rather than a compromise: under a zone these answer an
276
+ # `ActiveSupport::TimeWithZone`, and Rails overrides `TimeWithZone#is_a?` to answer true for
277
+ # `::Time`. A `Time | ActiveSupport::TimeWithZone` union was measured and rejected — it types every
278
+ # downstream call `Dynamic[top]`. The with-an-argument form stays `untyped` because the runtime
279
+ # return depends on the argument's class AND on whether the duration carries a date-scale part
280
+ # (`(1.month).ago(Date.today)` answers a `Date`; `(30.minutes).ago(Date.today)` a `TimeWithZone`).
281
+ def ago: () -> Time
282
+ | (untyped time) -> untyped
283
+ def until: () -> Time
284
+ | (untyped time) -> untyped
285
+ def before: () -> Time
286
+ | (untyped time) -> untyped
287
+ def since: () -> Time
288
+ | (untyped time) -> untyped
289
+ def from_now: () -> Time
290
+ | (untyped time) -> untyped
291
+ def after: () -> Time
292
+ | (untyped time) -> untyped
293
+
294
+ # `to_i` / its `in_seconds` alias return `@value.to_i` — always Integer.
295
+ def to_i: () -> Integer
296
+ def in_seconds: () -> Integer
297
+
298
+ # `to_f` — always Float.
299
+ def to_f: () -> Float
300
+
301
+ # `in_minutes` / `in_hours` / `in_days` / `in_weeks` / `in_months` / `in_years` — each
302
+ # `in_seconds / SECONDS_PER_*.to_f`, always Float.
303
+ def in_minutes: () -> Float
304
+ def in_hours: () -> Float
305
+ def in_days: () -> Float
306
+ def in_weeks: () -> Float
307
+ def in_months: () -> Float
308
+ def in_years: () -> Float
309
+
310
+ # Always a String.
311
+ def iso8601: (?precision: Integer?) -> String
312
+
313
+ # A `dup` of the internal parts hash — Integer-valued for a duration built from Integer multipliers,
314
+ # but real runtime Float for a fractional one (`1.5.seconds.parts == {seconds: 1.5}`); `Integer |
315
+ # Float` is what stays honest without costing the common `parts[:days] * 2` chain anything.
316
+ def parts: () -> Hash[Symbol, Integer | Float]
317
+ end
318
+ end
319
+
239
320
  # ---------------------------------------------------------------
240
321
  # Time — singleton + instance extensions
241
322
  # ---------------------------------------------------------------
@@ -245,6 +326,26 @@ class Time
245
326
  def self.zone: () -> untyped # ActiveSupport::TimeZone | nil
246
327
  def self.zone=: (String | Symbol | untyped) -> untyped
247
328
 
329
+ # Issue #658, `core_ext/time/zones` + `core_ext/time/calculations` — the rest of the singleton
330
+ # surface those two files add, none of it typed by rbs. `zone_default` is the `attr_accessor`
331
+ # behind `Time.zone`'s fallback; `find_zone!` raises on a bad zone and `find_zone` rescues to
332
+ # `nil`; `use_zone` swaps the zone around a block and answers the block's value. All four hand
333
+ # back or take an `ActiveSupport::TimeZone`, which this overlay does not declare, so they stay
334
+ # `untyped` rather than guessing a carrier.
335
+ def self.zone_default: () -> untyped
336
+ def self.zone_default=: (untyped) -> untyped
337
+ def self.use_zone: (untyped time_zone) { () -> untyped } -> untyped
338
+ def self.find_zone!: (untyped time_zone) -> untyped
339
+ def self.find_zone: (untyped time_zone) -> untyped
340
+ # `COMMON_YEAR_DAYS_IN_MONTH[month]` answers `nil` for a month outside 1..12, but the declared
341
+ # return stays `Integer`: `Integer?` would make `Time.days_in_month(2, 2024) - 1` — how every real
342
+ # call site uses it — a nil-arithmetic diagnostic, the more expensive of the two on correct code.
343
+ def self.days_in_month: (Integer month, ?Integer year) -> Integer
344
+ def self.days_in_year: (?Integer year) -> Integer
345
+ # `Time.rfc3339(str)` builds a `Time` from the parsed parts. Distinct from the `rfc3339` INSTANCE
346
+ # alias below.
347
+ def self.rfc3339: (String str) -> Time
348
+
248
349
  # `core_ext/time/conversions` — nothing to declare. `Time.parse`, `Time.httpdate` and `Time.iso8601`
249
350
  # all come from stdlib `time`, which rbs types; ActiveSupport only re-opens them. Declaring them
250
351
  # here raised `RBS::DuplicatedMethodDefinitionError` and collapsed the whole `Time` definition to
@@ -271,8 +372,11 @@ class Time
271
372
  def change: (**untyped) -> Time
272
373
  def at_beginning_of_day: () -> Time
273
374
  def at_end_of_day: () -> Time
274
- def at_beginning_of_week: () -> Time
275
- def at_end_of_week: () -> Time
375
+ # `at_beginning_of_week` / `at_end_of_week` are `alias`es of `beginning_of_week` / `end_of_week`
376
+ # in `DateAndTime::Calculations` and take the same optional `start_day` — declared zero-arity,
377
+ # `time.at_beginning_of_week(:sunday)` drew an arity diagnostic on correct Rails code.
378
+ def at_beginning_of_week: (?Symbol start_day) -> Time
379
+ def at_end_of_week: (?Symbol start_day) -> Time
276
380
  def at_midnight: () -> Time
277
381
  def at_noon: () -> Time
278
382
  def midday: () -> Time
@@ -282,7 +386,134 @@ class Time
282
386
  # Re-declaring it raised `RBS::DuplicatedMethodDefinitionError` and took the WHOLE `Time` definition
283
387
  # down with it (every `Time` call degraded to `Dynamic[top]`). This overlay only ADDS what
284
388
  # ActiveSupport adds.
389
+ def advance: (untyped options) -> Time
390
+ def all_day: () -> Range[Time]
285
391
  def acts_like_time?: () -> true
392
+
393
+ # -------------------------------------------------------------
394
+ # Issue #658 — the rest of the Rails `Time` INSTANCE surface
395
+ # -------------------------------------------------------------
396
+ #
397
+ # `Time` is a CORE class, fully RBS-known and therefore CLOSED: a name this file does not declare
398
+ # is reported `call.undefined-method`. On a closed class an OMISSION is a false positive on
399
+ # correct Rails code exactly as much as a wrong return type is, and there is no gradual middle —
400
+ # making `Time` an open receiver would suppress the genuine diagnostic project-wide. So every row
401
+ # below was audited name by name against the vendored activesupport-8.1.3.1 sources:
402
+ # `core_ext/date_and_time/calculations.rb` (the module `Time`, `Date` and `DateTime` all include),
403
+ # `core_ext/time/calculations.rb`, `core_ext/time/conversions.rb`,
404
+ # `core_ext/date_and_time/zones.rb` and `core_ext/date_and_time/compatibility.rb`.
405
+ #
406
+ # Every row was also checked against the assembled environment for a name rbs already declares:
407
+ # a second full declaration raises `RBS::DuplicatedMethodDefinitionError` and collapses the WHOLE
408
+ # class (the `Time#utc?` note above). That is why `to_time`, `xmlschema`, `localtime`, `getlocal`,
409
+ # `utc`, `getutc`, `utc?`, `utc_offset`, `usec`, `nsec`, `subsec`, `to_f` and `to_i` are absent
410
+ # even though ActiveSupport re-opens some of them.
411
+
412
+ # `core_ext/date_and_time/calculations` — the shared module; each method is `advance` / `change`
413
+ # on the receiver, so on a `Time` receiver each answers `Time`.
414
+ def today?: () -> bool
415
+ def tomorrow?: () -> bool
416
+ def next_day?: () -> bool
417
+ def yesterday?: () -> bool
418
+ def prev_day?: () -> bool
419
+ def past?: () -> bool
420
+ def future?: () -> bool
421
+ def on_weekend?: () -> bool
422
+ def on_weekday?: () -> bool
423
+ # `untyped` parameters: `self < date_or_time` accepts `Time`, `Date`, `DateTime` and
424
+ # `ActiveSupport::TimeWithZone` alike through ActiveSupport's `<=>` override.
425
+ def before?: (untyped date_or_time) -> bool
426
+ def after?: (untyped date_or_time) -> bool
427
+ # `Numeric` and not `Integer`: `advance` divmods a fractional `days:` / `weeks:` on purpose.
428
+ def days_ago: (Numeric days) -> Time
429
+ def days_since: (Numeric days) -> Time
430
+ def weeks_ago: (Numeric weeks) -> Time
431
+ def weeks_since: (Numeric weeks) -> Time
432
+ def months_ago: (Numeric months) -> Time
433
+ def months_since: (Numeric months) -> Time
434
+ def years_ago: (Numeric years) -> Time
435
+ def years_since: (Numeric years) -> Time
436
+ def at_beginning_of_month: () -> Time
437
+ def at_beginning_of_year: () -> Time
438
+ def at_end_of_month: () -> Time
439
+ def at_end_of_year: () -> Time
440
+ def beginning_of_quarter: () -> Time
441
+ def at_beginning_of_quarter: () -> Time
442
+ def end_of_quarter: () -> Time
443
+ def at_end_of_quarter: () -> Time
444
+ # `(month / 3.0).ceil` — `Float#ceil` with no argument is Integer.
445
+ def quarter: () -> Integer
446
+ def next_week: (?Symbol given_day_in_next_week, ?same_time: bool) -> Time
447
+ def prev_week: (?Symbol start_day, ?same_time: bool) -> Time
448
+ def last_week: (?Symbol start_day, ?same_time: bool) -> Time
449
+ def next_weekday: () -> Time
450
+ def prev_weekday: () -> Time
451
+ def last_weekday: () -> Time
452
+ def days_to_week_start: (?Symbol start_day) -> Integer
453
+ def all_week: (?Symbol start_day) -> Range[Time]
454
+ def monday: () -> Time
455
+ def sunday: () -> Time
456
+ def next_quarter: () -> Time
457
+ def prev_quarter: () -> Time
458
+ def last_quarter: () -> Time
459
+ def last_month: () -> Time
460
+ def last_year: () -> Time
461
+ def all_month: () -> Range[Time]
462
+ def all_quarter: () -> Range[Time]
463
+ def all_year: () -> Range[Time]
464
+ def next_occurring: (Symbol day_of_week) -> Time
465
+ def prev_occurring: (Symbol day_of_week) -> Time
466
+
467
+ # `core_ext/time/calculations` — the `Time`-only half. `seconds_since_midnight` ends in
468
+ # `+ (usec / 1.0e+6)`, so it is Float even at exactly midnight; `seconds_until_end_of_day` is
469
+ # `end_of_day.to_i - to_i`, Integer. `sec_fraction` is a plain alias of `subsec` and carries
470
+ # `subsec`'s own core type — CRuby answers the Integer `0` for a whole-second receiver.
471
+ def seconds_since_midnight: () -> Float
472
+ def seconds_until_end_of_day: () -> Integer
473
+ def sec_fraction: () -> (0 | Rational)
474
+ # `middle_of_day` is `change(hour: 12)`; `midday` / `noon` / `at_noon` above and `at_midday` /
475
+ # `at_middle_of_day` here are its aliases.
476
+ def middle_of_day: () -> Time
477
+ def at_midday: () -> Time
478
+ def at_middle_of_day: () -> Time
479
+ def at_beginning_of_hour: () -> Time
480
+ def at_end_of_hour: () -> Time
481
+ def at_beginning_of_minute: () -> Time
482
+ def at_end_of_minute: () -> Time
483
+ # `Time`'s OWN redefinitions (`advance(days: -days)` and friends), taking an optional count —
484
+ # not the zero-argument `Date` methods of the same name that rbs's `stdlib/date` types.
485
+ def prev_day: (?Numeric days) -> Time
486
+ def next_day: (?Numeric days) -> Time
487
+ def prev_month: (?Numeric months) -> Time
488
+ def next_month: (?Numeric months) -> Time
489
+ def prev_year: (?Numeric years) -> Time
490
+ def next_year: (?Numeric years) -> Time
491
+
492
+ # `core_ext/time/conversions`. `to_fs` / `to_formatted_s` always answer a String whatever the
493
+ # `Time::DATE_FORMATS` entry returns: the lambda branch ends in `.to_s` and the fallback is
494
+ # `to_s`. `formatted_offset` is String in both of its branches; its `colon` is `boolish` and not
495
+ # `bool` because the body is plain truthiness (`colon ? WITH_COLON : WITHOUT_COLON`), and
496
+ # `alternate_utc_string` is `untyped` because it is returned as-is and callers print whatever
497
+ # they like there. `rfc3339` is
498
+ # `alias_method :rfc3339, :xmlschema`, so it takes `xmlschema`'s optional `fraction_digits`; rbs
499
+ # types `Time#xmlschema` but not `Time#rfc3339`, so this is an addition and not a collision.
500
+ def to_fs: (?Symbol format) -> String
501
+ def to_formatted_s: (?Symbol format) -> String
502
+ def formatted_offset: (?boolish colon, ?untyped alternate_utc_string) -> String
503
+ def rfc3339: (?Integer fraction_digits) -> String
504
+
505
+ # `core_ext/date_and_time/zones`. Answers an `ActiveSupport::TimeWithZone`, which nothing in this
506
+ # overlay declares, so the return is `untyped` rather than a guess — the same answer
507
+ # `DateTime#in_time_zone` gives below. The parameter is `untyped` because `Time.find_zone!` also
508
+ # accepts an `ActiveSupport::TimeZone`, a `TZInfo::Timezone` and an offset `Duration`
509
+ # (`Time.find_zone! -5.hours` is the documented spelling).
510
+ def in_time_zone: (?untyped zone) -> untyped
511
+
512
+ # `core_ext/date_and_time/compatibility` — `mattr_accessor
513
+ # :utc_to_local_returns_utc_offset_times, instance_writer: false` defines an instance READER on
514
+ # every class including the module. Rarely called on an instance, but public, and `Time` is
515
+ # closed, so the omission would be the false positive.
516
+ def utc_to_local_returns_utc_offset_times: () -> bool
286
517
  end
287
518
 
288
519
  # ---------------------------------------------------------------
@@ -290,15 +521,16 @@ end
290
521
  # ---------------------------------------------------------------
291
522
 
292
523
  class Date
524
+ # The whole date-constructing singleton surface `core_ext/date/calculations.rb`'s `class << self`
525
+ # adds. `self.end_of_week`, `self.beginning_of_month`, `self.end_of_month`,
526
+ # `self.beginning_of_year` and `self.end_of_year` were declared here until #762 and do not exist —
527
+ # `Date` only INCLUDES `DateAndTime::Calculations`, so those five are instance methods, below. The
528
+ # declarations made `Date.end_of_month`, a real `NoMethodError`, type-check as a `Date`, and
529
+ # `DateTime` inherited all five. `self.beginning_of_week` exists but is the week-start
530
+ # configuration reader, declared with that group at the bottom of this class.
293
531
  def self.current: () -> Date
294
532
  def self.yesterday: () -> Date
295
533
  def self.tomorrow: () -> Date
296
- def self.beginning_of_week: () -> Date
297
- def self.end_of_week: () -> Date
298
- def self.beginning_of_month: () -> Date
299
- def self.end_of_month: () -> Date
300
- def self.beginning_of_year: () -> Date
301
- def self.end_of_year: () -> Date
302
534
 
303
535
  def yesterday: () -> Date
304
536
  def tomorrow: () -> Date
@@ -323,6 +555,98 @@ class Date
323
555
  def at_beginning_of_day: () -> Time
324
556
  def end_of_day: () -> Time
325
557
  def at_end_of_day: () -> Time
558
+ def advance: (untyped options) -> Date
559
+ def all_day: () -> Range[Time]
560
+
561
+ # `Date#to_time` is an OVERLOAD CONTINUATION (`| ...`), not a plain declaration: stdlib `date`
562
+ # already types `to_time: () -> Time`, and re-declaring it raises
563
+ # `RBS::DuplicatedMethodDefinitionError`, which collapses `Date` to `Dynamic[top]` for every project
564
+ # this overlay reaches — the failure the `Time#utc?` note above records, and the one #437 hit on the
565
+ # plugin. The row cannot simply be omitted either: ActiveSupport genuinely widens the arity, so
566
+ # without it `date.to_time(:utc)` — correct Rails code — draws an arity diagnostic (#449). `| ...`
567
+ # appends this overload ahead of the stdlib one, so both arities resolve.
568
+ def to_time: (?Symbol form) -> Time | ...
569
+
570
+ # Issue #670 — the rest of the Rails `Date` instance surface, plus the week-start configuration
571
+ # singletons. `Date` is CLOSED (rbs's `stdlib/date` types it fully), so an OMISSION here is a false
572
+ # positive on correct Rails code exactly as much as a wrong return type is. Audited against the
573
+ # vendored activesupport-8.1.3.1 sources and every return confirmed by calling it on a real `Date`
574
+ # receiver; the plugin twin carries the per-method reasoning and the purity verdicts, which the
575
+ # overlay deliberately does not (see the header). The six `:nodoc:` operator-alias artefacts
576
+ # (`plus_with{,out}_duration`, `minus_with{,out}_duration`, `compare_with{,out}_coercion`) are left
577
+ # out on purpose, as are the `Object`- / `Module`-level core_ext methods that reach `Date` because
578
+ # they reach every class (#673).
579
+ def today?: () -> bool
580
+ def tomorrow?: () -> bool
581
+ def next_day?: () -> bool
582
+ def yesterday?: () -> bool
583
+ def prev_day?: () -> bool
584
+ def past?: () -> bool
585
+ def future?: () -> bool
586
+ def on_weekend?: () -> bool
587
+ def on_weekday?: () -> bool
588
+ def before?: (untyped date_or_time) -> bool
589
+ def after?: (untyped date_or_time) -> bool
590
+ def days_ago: (Numeric days) -> Date
591
+ def days_since: (Numeric days) -> Date
592
+ def weeks_ago: (Numeric weeks) -> Date
593
+ def weeks_since: (Numeric weeks) -> Date
594
+ def months_ago: (Numeric months) -> Date
595
+ def months_since: (Numeric months) -> Date
596
+ def years_ago: (Numeric years) -> Date
597
+ def years_since: (Numeric years) -> Date
598
+ def at_beginning_of_month: () -> Date
599
+ def at_end_of_month: () -> Date
600
+ def at_beginning_of_year: () -> Date
601
+ def at_end_of_year: () -> Date
602
+ def beginning_of_quarter: () -> Date
603
+ def at_beginning_of_quarter: () -> Date
604
+ def end_of_quarter: () -> Date
605
+ def at_end_of_quarter: () -> Date
606
+ def quarter: () -> Integer
607
+ def next_week: (?Symbol given_day_in_next_week, ?same_time: bool) -> Date
608
+ def prev_week: (?Symbol start_day, ?same_time: bool) -> Date
609
+ def last_week: (?Symbol start_day, ?same_time: bool) -> Date
610
+ def next_weekday: () -> Date
611
+ def days_to_week_start: (?Symbol start_day) -> Integer
612
+ def all_week: (?Symbol start_day) -> Range[Date]
613
+ def at_beginning_of_week: (?Symbol start_day) -> Date
614
+ def at_end_of_week: (?Symbol start_day) -> Date
615
+ def prev_weekday: () -> Date
616
+ def last_weekday: () -> Date
617
+ def monday: () -> Date
618
+ def sunday: () -> Date
619
+ def next_quarter: () -> Date
620
+ def prev_quarter: () -> Date
621
+ def last_quarter: () -> Date
622
+ def last_month: () -> Date
623
+ def last_year: () -> Date
624
+ def all_month: () -> Range[Date]
625
+ def all_quarter: () -> Range[Date]
626
+ def all_year: () -> Range[Date]
627
+ def next_occurring: (Symbol day_of_week) -> Date
628
+ def prev_occurring: (Symbol day_of_week) -> Date
629
+ def change: (untyped options) -> Date
630
+ def in: (Numeric seconds) -> Time
631
+ def middle_of_day: () -> Time
632
+ def midday: () -> Time
633
+ def noon: () -> Time
634
+ def at_midday: () -> Time
635
+ def at_noon: () -> Time
636
+ def at_middle_of_day: () -> Time
637
+ def to_fs: (?Symbol format) -> String
638
+ def to_formatted_s: (?Symbol format) -> String
639
+ def readable_inspect: () -> String
640
+ def default_inspect: () -> String
641
+ def in_time_zone: (?untyped zone) -> untyped
642
+ # The week-start configuration group. `self.beginning_of_week` is its READER and answers the week
643
+ # start (`:monday` by default), not a date — it was `() -> Date` until #762, which made every
644
+ # correct Symbol use of the value look wrong and every incorrect `Date` use look right.
645
+ def self.beginning_of_week: () -> Symbol
646
+ def self.beginning_of_week=: (Symbol week_start) -> Symbol
647
+ def self.beginning_of_week_default: () -> Symbol?
648
+ def self.beginning_of_week_default=: (Symbol? week_start) -> Symbol?
649
+ def self.find_beginning_of_week!: (Symbol week_start) -> Symbol
326
650
  end
327
651
 
328
652
  # ---------------------------------------------------------------
@@ -459,10 +783,29 @@ end
459
783
  # ---------------------------------------------------------------
460
784
 
461
785
  class DateTime
786
+ # Issue #670. `DateTime < Date`, so every row the `Date` block above declares is INHERITED here —
787
+ # and for most of `DateAndTime::Calculations` the inherited return is WRONG, because the shared
788
+ # module's `first_hour` / `last_hour` helpers branch on `acts_like?(:time)` (false on `Date`, true
789
+ # on `DateTime`) and compose each class's own `advance` / `change`. `DateTime.now.beginning_of_month`
790
+ # answered `Date` on master while returning a `DateTime` at runtime. So most of this block is
791
+ # overrides, and declaring the missing surface on `Date` alone would have converted the
792
+ # undefined-method false positives into wrong-return ones instead of fixing them.
793
+ #
794
+ # Rows already correct through inheritance are deliberately absent: `today?` / `past?` / `before?` /
795
+ # `on_weekend?` (bool), `quarter` / `days_to_week_start` (Integer), `to_fs` / `to_formatted_s` /
796
+ # `readable_inspect` / `default_inspect` (String), `acts_like_date?` (true). The sharpest is at the
797
+ # singleton — `DateTime.yesterday` / `.tomorrow` really do answer a `Date` (their bodies are a
798
+ # hardcoded `::Date.current.…`), so the inherited rows are right and overriding them would have been
799
+ # the false positive. `DateTime.current` is the one that is wrong inherited, and it is overridden.
462
800
  def utc: () -> Time
463
- # NOTE: `DateTime#to_time` is NOT declared here — stdlib `date` already types it, and re-declaring it
464
- # collapsed the whole `DateTime` definition (see the `Time#utc?` note above).
465
- def in_time_zone: (?String | Symbol zone) -> untyped
801
+ def getgm: () -> Time
802
+ def getutc: () -> Time
803
+ def gmtime: () -> Time
804
+ # NOTE: `DateTime#to_time` is NOT declared here — stdlib `date` already types it, and re-declaring
805
+ # it collapsed the whole `DateTime` definition (see the `Time#utc?` note above). The
806
+ # `in_time_zone` PARAMETER widened from `String | Symbol` to `untyped` in #670: `Time.find_zone!`
807
+ # also accepts a `TimeZone`, a `TZInfo::Timezone` and an offset `Duration`.
808
+ def in_time_zone: (?untyped zone) -> untyped
466
809
  def yesterday: () -> DateTime
467
810
  def tomorrow: () -> DateTime
468
811
  def ago: (Numeric seconds) -> DateTime
@@ -474,4 +817,92 @@ class DateTime
474
817
  def beginning_of_minute: () -> DateTime
475
818
  def end_of_minute: () -> DateTime
476
819
  def acts_like_time?: () -> true
820
+ def advance: (untyped options) -> DateTime
821
+ def change: (untyped options) -> DateTime
822
+ def in: (Numeric seconds) -> DateTime
823
+ def midnight: () -> DateTime
824
+ def at_midnight: () -> DateTime
825
+ def at_beginning_of_day: () -> DateTime
826
+ def at_end_of_day: () -> DateTime
827
+ def middle_of_day: () -> DateTime
828
+ def midday: () -> DateTime
829
+ def noon: () -> DateTime
830
+ def at_midday: () -> DateTime
831
+ def at_noon: () -> DateTime
832
+ def at_middle_of_day: () -> DateTime
833
+ def at_beginning_of_hour: () -> DateTime
834
+ def at_end_of_hour: () -> DateTime
835
+ def at_beginning_of_minute: () -> DateTime
836
+ def at_end_of_minute: () -> DateTime
837
+ def seconds_since_midnight: () -> Integer
838
+ def seconds_until_end_of_day: () -> Integer
839
+ def subsec: () -> Rational
840
+ def to_f: () -> Float
841
+ def to_i: () -> Integer
842
+ def usec: () -> Integer
843
+ def nsec: () -> Integer
844
+ def utc?: () -> bool
845
+ def utc_offset: () -> Integer
846
+ def localtime: (?untyped utc_offset) -> Time
847
+ def getlocal: (?untyped utc_offset) -> Time
848
+ def days_ago: (Numeric days) -> DateTime
849
+ def days_since: (Numeric days) -> DateTime
850
+ def weeks_ago: (Numeric weeks) -> DateTime
851
+ def weeks_since: (Numeric weeks) -> DateTime
852
+ def months_ago: (Numeric months) -> DateTime
853
+ def months_since: (Numeric months) -> DateTime
854
+ def years_ago: (Numeric years) -> DateTime
855
+ def years_since: (Numeric years) -> DateTime
856
+ def beginning_of_month: () -> DateTime
857
+ def at_beginning_of_month: () -> DateTime
858
+ def end_of_month: () -> DateTime
859
+ def at_end_of_month: () -> DateTime
860
+ def beginning_of_year: () -> DateTime
861
+ def at_beginning_of_year: () -> DateTime
862
+ def end_of_year: () -> DateTime
863
+ def at_end_of_year: () -> DateTime
864
+ def beginning_of_quarter: () -> DateTime
865
+ def at_beginning_of_quarter: () -> DateTime
866
+ def end_of_quarter: () -> DateTime
867
+ def at_end_of_quarter: () -> DateTime
868
+ def next_quarter: () -> DateTime
869
+ def prev_quarter: () -> DateTime
870
+ def last_quarter: () -> DateTime
871
+ def last_month: () -> DateTime
872
+ def last_year: () -> DateTime
873
+ def prev_weekday: () -> DateTime
874
+ def last_weekday: () -> DateTime
875
+ def monday: () -> DateTime
876
+ def sunday: () -> DateTime
877
+ def next_occurring: (Symbol day_of_week) -> DateTime
878
+ def prev_occurring: (Symbol day_of_week) -> DateTime
879
+ def all_day: () -> Range[DateTime]
880
+ def all_month: () -> Range[DateTime]
881
+ def all_quarter: () -> Range[DateTime]
882
+ def all_year: () -> Range[DateTime]
883
+ def beginning_of_week: (?Symbol start_day) -> DateTime
884
+ def end_of_week: (?Symbol start_day) -> DateTime
885
+ def at_beginning_of_week: (?Symbol start_day) -> DateTime
886
+ def at_end_of_week: (?Symbol start_day) -> DateTime
887
+ def next_week: (?Symbol given_day_in_next_week, ?same_time: bool) -> DateTime
888
+ def prev_week: (?Symbol start_day, ?same_time: bool) -> DateTime
889
+ def last_week: (?Symbol start_day, ?same_time: bool) -> DateTime
890
+ def next_weekday: () -> DateTime
891
+ def all_week: (?Symbol start_day) -> Range[DateTime]
892
+ def formatted_offset: (?boolish colon, ?untyped alternate_utc_string) -> String
893
+ def utc_to_local_returns_utc_offset_times: () -> bool
894
+ def self.current: () -> DateTime
895
+ def self.civil_from_format: (untyped utc_or_local, Integer year, ?Integer month, ?Integer day, ?Integer hour, ?Integer min, ?Integer sec) -> DateTime
896
+ end
897
+
898
+ # ---------------------------------------------------------------
899
+ # ERB::Util — ActionView extends it with `html_escape_once`
900
+ # ---------------------------------------------------------------
901
+
902
+ # `ERB` is a CLASS in Ruby / RBS (not a module), so reopen it as a class — a `module ERB` wrapper
903
+ # collides with upstream `class ERB` once the `erb` stdlib is in scope. `ERB::Util` itself is a module.
904
+ class ERB
905
+ module Util
906
+ def self.html_escape_once: (untyped) -> String
907
+ end
477
908
  end
@@ -113,7 +113,7 @@ String, Symbol, Array, and Hash. The list is not in this
113
113
  handbook (it would fill several pages); see
114
114
  [`docs/types.md`](../types.md) and the per-class catalogues
115
115
  under
116
- [`data/builtins/ruby_core/`](../../data/builtins/ruby_core/).
116
+ [`data/builtins/ruby_core/`](../../data/builtins/ruby_core).
117
117
 
118
118
  When folding is **not** safe (because a method has side
119
119
  effects, depends on the environment, or is not in a
@@ -275,7 +275,7 @@ the underlying RBS sig advertises.
275
275
 
276
276
  If you prefer the TS spellings (`Pick<T, K>` etc.) in
277
277
  directives, opt into the
278
- [`rigor-typescript-utility-types`](../../plugins/rigor-typescript-utility-types/)
278
+ [`rigor-typescript-utility-types`](../../plugins/rigor-typescript-utility-types)
279
279
  plugin. The plugin registers a `Plugin::TypeNodeResolver` that
280
280
  translates each TS name onto the canonical projection:
281
281
 
@@ -236,6 +236,6 @@ deprecations, …). Most projects will never write one; the
236
236
  chapter exists so you know the option is there.
237
237
  [Chapter 10 — Coexisting with Sorbet](10-sorbet.md) is for
238
238
  projects arriving from a Sorbet codebase: the
239
- [`rigor-sorbet`](../../plugins/rigor-sorbet/) adapter reads
239
+ [`rigor-sorbet`](../../plugins/rigor-sorbet) adapter reads
240
240
  `sig { ... }` blocks, RBI files, and `T.let` / `T.cast` /
241
241
  `T.must` / `T.unsafe` assertions as type sources.
@@ -122,7 +122,7 @@ Reach for a plugin only when:
122
122
 
123
123
  If those are true, [`examples/README.md`](../../examples/README.md)
124
124
  is your starting point. The
125
- [`rigor-deprecations`](../../examples/rigor-deprecations/)
125
+ [`rigor-deprecations`](../../examples/rigor-deprecations)
126
126
  example is the smallest fully-shaped plugin — manifest +
127
127
  single per-file walk + a couple of diagnostic emissions —
128
128
  and is the recommended template for "I want to author my
@@ -146,7 +146,7 @@ From here:
146
146
  to deeper material in
147
147
  [`docs/type-specification/`](../type-specification/README.md),
148
148
  [`docs/internal-spec/`](../internal-spec/README.md), and
149
- [`docs/adr/`](../adr/).
149
+ [`docs/adr/`](../adr).
150
150
  - The [`CHANGELOG.md`](../../CHANGELOG.md) is the per-release
151
151
  truth for what shipped when.
152
152
 
@@ -1,7 +1,7 @@
1
1
  # Coexisting with Sorbet
2
2
 
3
3
  If your project already uses [Sorbet](https://sorbet.org/),
4
- the [`rigor-sorbet`](../../plugins/rigor-sorbet/) plugin
4
+ the [`rigor-sorbet`](../../plugins/rigor-sorbet) plugin
5
5
  lets Rigor read your existing `sig` blocks, RBI files, and
6
6
  `T.let` / `T.cast` / `T.must` / `T.unsafe` assertions as type
7
7
  sources. You do not have to rewrite anything in RBS to start
@@ -66,7 +66,7 @@ up the flag, key, or command that *acts* on it.
66
66
  landing page.
67
67
  10. [**Coexisting with Sorbet**](10-sorbet.md) — for users
68
68
  arriving from a Sorbet-using project: the
69
- [`rigor-sorbet`](../../plugins/rigor-sorbet/) adapter
69
+ [`rigor-sorbet`](../../plugins/rigor-sorbet) adapter
70
70
  reads `sig { ... }` blocks, RBI files, and
71
71
  `T.let` / `T.cast` / `T.must` / `T.unsafe` assertions
72
72
  as type sources without rewriting in RBS.
@@ -255,7 +255,7 @@ ADRs:
255
255
  - [`docs/internal-spec/`](../internal-spec/README.md) —
256
256
  analyzer-internal contracts (engine surface, type-object
257
257
  public API).
258
- - [`docs/adr/`](../adr/) — architecture decision records.
258
+ - [`docs/adr/`](../adr) — architecture decision records.
259
259
 
260
260
  ## Non-goals
261
261
 
@@ -105,7 +105,7 @@ type switches. Rigor has direct analogues.
105
105
  | `if x != nil` | `if x` (strips `nil`), or `unless x.nil?` |
106
106
  | `v, ok := x.(string)` (comma-ok assertion) | `x.is_a?(String)` narrowing in an `if` |
107
107
  | `switch v := x.(type) { case string: … }` | `case x; in String => v` |
108
- | `x.(T)` (assertion, panics on fail) | (no panicking assertion) — `is_a?` guard, or `T.cast` via [`rigor-sorbet`](../../plugins/rigor-sorbet/) |
108
+ | `x.(T)` (assertion, panics on fail) | (no panicking assertion) — `is_a?` guard, or `T.cast` via [`rigor-sorbet`](../../plugins/rigor-sorbet) |
109
109
  | user func returning `bool` | `%a{rigor:v1:predicate-if-true x is Foo}` directive |
110
110
 
111
111
  Go's type switch is *not* exhaustive — you can omit cases and
@@ -58,7 +58,7 @@ advisor that only speaks when it is sure.
58
58
  | `Set<T>` | `HashSet<T>` / `ISet<T>` | `Set[T]` | |
59
59
  | `record Point(int x, int y)` | `record Point(int X, int Y)` | `Point = Data.define(:x, :y)` | See [Records ↔ Data.define](#records--datadefine). |
60
60
  | `Optional<T>` | `T?` (nullable reference type) | `T?` (i.e. `T \| nil`) | Java models it as a *container*; C# as a *type modifier*. See [Nullability](#nullability). |
61
- | `enum Color { RED, GREEN }` | `enum Color { Red, Green }` | `Constant<:red> \| Constant<:green>` (Symbol union) | Ruby has no native enum; the [`rigor-mangrove`](../../plugins/) plugin types richer enum DSLs. |
61
+ | `enum Color { RED, GREEN }` | `enum Color { Red, Green }` | `Constant<:red> \| Constant<:green>` (Symbol union) | Ruby has no native enum; the [`rigor-mangrove`](../../plugins) plugin types richer enum DSLs. |
62
62
  | `sealed interface Shape permits …` | `abstract` base + sealed hierarchy | union of the subtypes | See [Sealed types & exhaustiveness](#sealed-types-and-exhaustiveness). |
63
63
  | `<T>` (generic) | `<T>` (generic) | RBS `[T]` type parameter | |
64
64
  | `? extends T` (use-site) | `out T` (declaration-site) | covariant type parameter | See [Generics & variance](#generics-and-variance). |
@@ -124,7 +124,7 @@ behaviour matches.
124
124
  | `x instanceof String` | `x is string` | `x.is_a?(String)` |
125
125
  | `x instanceof String s` (binding) | `x is string s` (binding) | `case x; in String => s` |
126
126
  | `switch (x) { case Foo f -> … }` | `switch (x) { case Foo f => … }` | `case x; in Foo => f` |
127
- | `(Foo) x` (cast) | `(Foo)x` (cast) | (no in-source cast) — `is_a?` guard, or `T.cast` via [`rigor-sorbet`](../../plugins/rigor-sorbet/) |
127
+ | `(Foo) x` (cast) | `(Foo)x` (cast) | (no in-source cast) — `is_a?` guard, or `T.cast` via [`rigor-sorbet`](../../plugins/rigor-sorbet) |
128
128
  | `Objects.requireNonNull(x)` | `x!` (null-forgiving) | (no in-source assertion) — `unless x.nil?`, or `T.must` via `rigor-sorbet` |
129
129
  | user method returning `boolean` | user method returning `bool` | `%a{rigor:v1:predicate-if-true x is Foo}` directive on the predicate |
130
130
 
@@ -296,7 +296,7 @@ draws the distinction in full.
296
296
  Pyright's "type alias narrowing" and mypy's overload stacks
297
297
  cover some cases; Rigor's plugin contract gives you full
298
298
  Ruby code at the dispatch point. The
299
- [`rigor-lisp-eval`](../../examples/rigor-lisp-eval/) example
299
+ [`rigor-lisp-eval`](../../examples/rigor-lisp-eval) example
300
300
  is the canonical demo — `Lisp.eval([:+, 1, 2])` returns
301
301
  `Integer`, `Lisp.eval([:<, 1, 2])` returns `bool`.
302
302
 
@@ -228,8 +228,8 @@ author):
228
228
  - The `missing-protocol-method` / `protocol-return-mismatch`
229
229
  diagnostics are **plugin diagnostics**, emitted under the
230
230
  plugin's `plugin.<id>.` provenance — not core Rigor rules. The
231
- worked references are [`examples/rigor-web/`](../../examples/rigor-web/)
232
- (the minimal tutorial) and [`plugins/rigor-hanami/`](../../plugins/rigor-hanami/)
231
+ worked references are [`examples/rigor-web/`](../../examples/rigor-web)
232
+ (the minimal tutorial) and [`plugins/rigor-hanami/`](../../plugins/rigor-hanami)
233
233
  (production Hanami 2 actions).
234
234
 
235
235
  ## Interface vs protocol contract