rigortype 0.3.6 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/data/builtins/ruby_core/date.yml +6 -3
- data/data/builtins/ruby_core/enumerable.yml +1 -0
- data/data/builtins/ruby_core/exception.yml +2 -1
- data/data/builtins/ruby_core/file.yml +28 -14
- data/data/builtins/ruby_core/hash.yml +5 -2
- data/data/builtins/ruby_core/io.yml +33 -16
- data/data/builtins/ruby_core/random.yml +2 -1
- data/data/builtins/ruby_core/re.yml +6 -3
- data/data/builtins/ruby_core/struct.yml +2 -0
- data/data/builtins/ruby_core/time.yml +2 -1
- data/data/gem_overlay/activesupport/core_ext.rbs +409 -11
- data/docs/handbook/11-sig-gen.md +24 -14
- data/docs/manual/02-cli-reference.md +23 -7
- data/docs/manual/04-diagnostics.md +1 -1
- data/docs/manual/05-inspecting-types.md +20 -8
- data/docs/manual/07-plugins.md +4 -0
- data/docs/manual/plugins/rigor-actionpack.md +61 -0
- data/docs/manual/plugins/rigor-activerecord.md +74 -1
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +114 -12
- data/docs/manual/plugins/rigor-sidekiq.md +22 -0
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +12 -0
- data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +98 -0
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +3 -3
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +199 -0
- data/lib/rigor/analysis/check_rules/rule_walk.rb +1 -2
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +2 -2
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +6 -12
- data/lib/rigor/analysis/check_rules.rb +383 -48
- data/lib/rigor/analysis/crash_signature.rb +188 -0
- data/lib/rigor/analysis/dependency_recorder.rb +18 -3
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +2 -1
- data/lib/rigor/analysis/incremental.rb +28 -0
- data/lib/rigor/analysis/incremental_session.rb +63 -16
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +1 -2
- data/lib/rigor/analysis/reachability/graph.rb +14 -5
- data/lib/rigor/analysis/reachability/scan.rb +7 -3
- data/lib/rigor/analysis/result.rb +32 -0
- data/lib/rigor/analysis/run_cache_key.rb +41 -1
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +233 -16
- data/lib/rigor/analysis/runner/pool_coordinator.rb +266 -47
- data/lib/rigor/analysis/runner/project_pre_passes.rb +15 -11
- data/lib/rigor/analysis/runner/run_snapshots.rb +12 -2
- data/lib/rigor/analysis/runner.rb +212 -16
- data/lib/rigor/analysis/worker_session.rb +37 -3
- data/lib/rigor/bleeding_edge.rb +7 -6
- data/lib/rigor/builtins/hkt_builtins.rb +45 -6
- data/lib/rigor/builtins/imported_refinements.rb +11 -9
- data/lib/rigor/builtins/predefined_constant_refinements.rb +108 -59
- data/lib/rigor/builtins/static_return_refinements.rb +20 -1
- data/lib/rigor/cache/annotation_location.rb +72 -0
- data/lib/rigor/cache/descriptor.rb +42 -2
- data/lib/rigor/cache/incremental_snapshot.rb +40 -3
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +70 -8
- data/lib/rigor/cache/store.rb +17 -1
- data/lib/rigor/cli/coverage_command.rb +23 -18
- data/lib/rigor/cli/coverage_mutation.rb +13 -17
- data/lib/rigor/cli/coverage_scan.rb +47 -6
- data/lib/rigor/cli/fused_protection_renderer.rb +19 -2
- data/lib/rigor/cli/fused_protection_report.rb +23 -2
- data/lib/rigor/cli/measurement_integrity_warning.rb +57 -0
- data/lib/rigor/cli/mutation_protection_renderer.rb +22 -2
- data/lib/rigor/cli/mutation_protection_report.rb +31 -2
- data/lib/rigor/cli/sig_gen_command.rb +47 -0
- data/lib/rigor/cli/type_of_command.rb +188 -41
- data/lib/rigor/cli/type_of_renderer.rb +72 -3
- data/lib/rigor/cli/type_scan_command.rb +10 -1
- data/lib/rigor/cli.rb +1 -1
- data/lib/rigor/configuration.rb +7 -4
- data/lib/rigor/effects/plugin_facts.rb +1 -1
- data/lib/rigor/effects/scanner.rb +2 -3
- data/lib/rigor/environment/bundle_sig_discovery.rb +64 -13
- data/lib/rigor/environment/failure_slot.rb +28 -0
- data/lib/rigor/environment/lockfile_resolver.rb +15 -5
- data/lib/rigor/environment/missing_gem_constant_index.rb +45 -4
- data/lib/rigor/environment/rbs_hierarchy.rb +16 -22
- data/lib/rigor/environment/rbs_loader.rb +743 -93
- data/lib/rigor/environment.rb +125 -25
- data/lib/rigor/inference/acceptance.rb +172 -20
- data/lib/rigor/inference/captured_locals.rb +62 -0
- data/lib/rigor/inference/content_join.rb +347 -0
- data/lib/rigor/inference/def_handle.rb +15 -3
- data/lib/rigor/inference/def_node_resolver.rb +62 -3
- data/lib/rigor/inference/expression_typer.rb +1020 -141
- data/lib/rigor/inference/fork_map.rb +6 -1
- data/lib/rigor/inference/hkt_reducer.rb +12 -1
- data/lib/rigor/inference/hkt_registry.rb +61 -3
- data/lib/rigor/inference/hkt_sugar_translator.rb +87 -0
- data/lib/rigor/inference/index_write_widening.rb +48 -0
- data/lib/rigor/inference/indexed_narrowing.rb +14 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +7 -5
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +10 -0
- data/lib/rigor/inference/method_dispatcher/json_folding.rb +58 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +81 -38
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +54 -17
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +63 -0
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +63 -10
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +104 -0
- data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +81 -0
- data/lib/rigor/inference/method_dispatcher.rb +155 -14
- data/lib/rigor/inference/method_parameter_binder.rb +3 -3
- data/lib/rigor/inference/mutation_widening.rb +244 -174
- data/lib/rigor/inference/narrowing.rb +331 -27
- data/lib/rigor/inference/parameter_inference_collector.rb +1 -0
- data/lib/rigor/inference/pre_eval_constants.rb +15 -0
- data/lib/rigor/inference/precision_scanner.rb +5 -2
- data/lib/rigor/inference/project_patched_scanner.rb +2 -1
- data/lib/rigor/inference/rbs_type_translator.rb +89 -39
- data/lib/rigor/inference/scope_indexer.rb +1493 -140
- data/lib/rigor/inference/statement_evaluator.rb +276 -91
- data/lib/rigor/inference/struct_fold_safety.rb +186 -27
- data/lib/rigor/inference/version_guard.rb +229 -0
- data/lib/rigor/language_server/selection_range_provider.rb +1 -1
- data/lib/rigor/plugin/base.rb +3 -2
- data/lib/rigor/plugin/inflector.rb +14 -5
- data/lib/rigor/plugin/io_boundary.rb +107 -4
- data/lib/rigor/plugin/loader.rb +14 -0
- data/lib/rigor/plugin/registry.rb +25 -2
- data/lib/rigor/protection/analysis_guard.rb +60 -0
- data/lib/rigor/protection/closure_kill_oracle.rb +43 -38
- data/lib/rigor/protection/diagnostic_oracle.rb +12 -4
- data/lib/rigor/protection/discovery_seed.rb +1 -1
- data/lib/rigor/protection/measurement_integrity.rb +36 -0
- data/lib/rigor/protection/mutation_scanner.rb +61 -6
- data/lib/rigor/protection/mutator.rb +121 -18
- data/lib/rigor/rbs_extended/envelope_scanner.rb +8 -7
- data/lib/rigor/rbs_extended/hkt_directives.rb +16 -1
- data/lib/rigor/rbs_extended/reporter.rb +93 -13
- data/lib/rigor/rbs_extended.rb +6 -1
- data/lib/rigor/reflection.rb +182 -30
- data/lib/rigor/scope/discovery_index.rb +39 -1
- data/lib/rigor/scope.rb +385 -4
- data/lib/rigor/sig_gen/classification.rb +12 -1
- data/lib/rigor/sig_gen/generator.rb +320 -85
- data/lib/rigor/sig_gen/renderer.rb +14 -12
- data/lib/rigor/sig_gen/writer.rb +3 -0
- data/lib/rigor/source/constant_path.rb +79 -2
- data/lib/rigor/triage/catalogue.rb +1 -1
- data/lib/rigor/type/combinator.rb +10 -0
- data/lib/rigor/type/maybe.rb +47 -0
- data/lib/rigor/type/refined.rb +1 -2
- data/lib/rigor/type/result.rb +53 -0
- data/lib/rigor/type.rb +2 -0
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +3 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +58 -8
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +15 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +4 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +3 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +39 -14
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +36 -3
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -2
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +21 -5
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +47 -8
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +13 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +242 -4
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +3 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +7 -3
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +62 -11
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +15 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +3 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +5 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +798 -22
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +156 -13
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +140 -40
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +2 -2
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +20 -6
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +14 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +6 -3
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +23 -6
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +264 -9
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +876 -23
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +99 -0
- data/plugins/rigor-dry-monads/lib/rigor-dry-monads.rb +5 -0
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +34 -0
- data/plugins/rigor-ethon/lib/rigor-ethon.rb +3 -0
- data/plugins/rigor-ethon/sig/ethon.rbs +27 -0
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +3 -2
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +198 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/binding_recognizer.rb +75 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/catalog.rb +64 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +173 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/target_detector.rb +46 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +161 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +121 -0
- data/plugins/rigor-ffi/lib/rigor-ffi.rb +3 -0
- data/plugins/rigor-ffi/sig/ffi.rbs +67 -0
- data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +19 -0
- data/plugins/rigor-ffi-rzmq/lib/rigor-ffi-rzmq.rb +3 -0
- data/plugins/rigor-ffi-rzmq/sig/ffi_rzmq.rbs +29 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +2 -4
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +2 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +2 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +2 -1
- data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +156 -11
- data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +30 -0
- data/plugins/rigor-rbnacl/lib/rigor-rbnacl.rb +3 -0
- data/plugins/rigor-rbnacl/sig/rbnacl.rbs +22 -0
- data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +55 -0
- data/plugins/rigor-sassc/lib/rigor-sassc.rb +3 -0
- data/plugins/rigor-sassc/sig/sassc.rbs +24 -0
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +32 -11
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +3 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +4 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +63 -10
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +15 -2
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +61 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +4 -2
- data/sig/rigor/analysis/baseline.rbs +1 -1
- data/sig/rigor/environment.rbs +9 -0
- data/sig/rigor/inference.rbs +4 -1
- data/sig/rigor/plugin/io_boundary.rbs +2 -0
- data/sig/rigor/reflection.rbs +3 -1
- data/sig/rigor/scope.rbs +25 -1
- data/sig/rigor/type.rbs +95 -1
- data/sig/rigor.rbs +16 -0
- metadata +71 -3
|
@@ -78,6 +78,8 @@ class NilClass
|
|
|
78
78
|
def presence: () -> nil
|
|
79
79
|
def try: (*untyped) -> nil
|
|
80
80
|
def try!: (*untyped) -> nil
|
|
81
|
+
def duplicable?: () -> true
|
|
82
|
+
def instance_values: () -> Hash[String, untyped]
|
|
81
83
|
end
|
|
82
84
|
|
|
83
85
|
class TrueClass
|
|
@@ -244,6 +246,77 @@ class Float
|
|
|
244
246
|
def gigabytes: () -> Float
|
|
245
247
|
end
|
|
246
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
|
+
|
|
247
320
|
# ---------------------------------------------------------------
|
|
248
321
|
# Time — singleton + instance extensions
|
|
249
322
|
# ---------------------------------------------------------------
|
|
@@ -253,6 +326,26 @@ class Time
|
|
|
253
326
|
def self.zone: () -> untyped # ActiveSupport::TimeZone | nil
|
|
254
327
|
def self.zone=: (String | Symbol | untyped) -> untyped
|
|
255
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
|
+
|
|
256
349
|
# `core_ext/time/conversions` — nothing to declare. `Time.parse`, `Time.httpdate` and `Time.iso8601`
|
|
257
350
|
# all come from stdlib `time`, which rbs types; ActiveSupport only re-opens them. Declaring them
|
|
258
351
|
# here raised `RBS::DuplicatedMethodDefinitionError` and collapsed the whole `Time` definition to
|
|
@@ -279,8 +372,11 @@ class Time
|
|
|
279
372
|
def change: (**untyped) -> Time
|
|
280
373
|
def at_beginning_of_day: () -> Time
|
|
281
374
|
def at_end_of_day: () -> Time
|
|
282
|
-
|
|
283
|
-
|
|
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
|
|
284
380
|
def at_midnight: () -> Time
|
|
285
381
|
def at_noon: () -> Time
|
|
286
382
|
def midday: () -> Time
|
|
@@ -293,6 +389,131 @@ class Time
|
|
|
293
389
|
def advance: (untyped options) -> Time
|
|
294
390
|
def all_day: () -> Range[Time]
|
|
295
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
|
|
296
517
|
end
|
|
297
518
|
|
|
298
519
|
# ---------------------------------------------------------------
|
|
@@ -300,15 +521,16 @@ end
|
|
|
300
521
|
# ---------------------------------------------------------------
|
|
301
522
|
|
|
302
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.
|
|
303
531
|
def self.current: () -> Date
|
|
304
532
|
def self.yesterday: () -> Date
|
|
305
533
|
def self.tomorrow: () -> Date
|
|
306
|
-
def self.beginning_of_week: () -> Date
|
|
307
|
-
def self.end_of_week: () -> Date
|
|
308
|
-
def self.beginning_of_month: () -> Date
|
|
309
|
-
def self.end_of_month: () -> Date
|
|
310
|
-
def self.beginning_of_year: () -> Date
|
|
311
|
-
def self.end_of_year: () -> Date
|
|
312
534
|
|
|
313
535
|
def yesterday: () -> Date
|
|
314
536
|
def tomorrow: () -> Date
|
|
@@ -344,6 +566,87 @@ class Date
|
|
|
344
566
|
# without it `date.to_time(:utc)` — correct Rails code — draws an arity diagnostic (#449). `| ...`
|
|
345
567
|
# appends this overload ahead of the stdlib one, so both arities resolve.
|
|
346
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
|
|
347
650
|
end
|
|
348
651
|
|
|
349
652
|
# ---------------------------------------------------------------
|
|
@@ -480,10 +783,29 @@ end
|
|
|
480
783
|
# ---------------------------------------------------------------
|
|
481
784
|
|
|
482
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.
|
|
483
800
|
def utc: () -> Time
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
def
|
|
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
|
|
487
809
|
def yesterday: () -> DateTime
|
|
488
810
|
def tomorrow: () -> DateTime
|
|
489
811
|
def ago: (Numeric seconds) -> DateTime
|
|
@@ -495,6 +817,82 @@ class DateTime
|
|
|
495
817
|
def beginning_of_minute: () -> DateTime
|
|
496
818
|
def end_of_minute: () -> DateTime
|
|
497
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
|
|
498
896
|
end
|
|
499
897
|
|
|
500
898
|
# ---------------------------------------------------------------
|
data/docs/handbook/11-sig-gen.md
CHANGED
|
@@ -95,10 +95,13 @@ states:
|
|
|
95
95
|
|
|
96
96
|
The `sig.skipped.*` reasons are:
|
|
97
97
|
|
|
98
|
-
- `sig.skipped.complex-shape` —
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
- `sig.skipped.complex-shape` — reserved for a parameter
|
|
99
|
+
shape the renderer cannot spell. Every shape a `def` can
|
|
100
|
+
declare renders today (optional, rest, trailing, keyword,
|
|
101
|
+
keyword-rest, `...` forwarding, `&block`), so the
|
|
102
|
+
generator does not produce this reason; it stayed
|
|
103
|
+
reserved when the gate that used to fire it for every
|
|
104
|
+
such method was retired (#778).
|
|
102
105
|
- `sig.skipped.untyped-return` — the method body's last
|
|
103
106
|
expression types as `Dynamic[top]`. Emitting `untyped` as
|
|
104
107
|
a tightening would be noise rather than help.
|
|
@@ -119,16 +122,25 @@ The `sig.skipped.*` reasons are:
|
|
|
119
122
|
The three `sig.generated.*` identifiers
|
|
120
123
|
(`sig.generated.new-file` / `new-method` / `tighter-return`)
|
|
121
124
|
are emitted as JSON fields under `--format=json` so CI
|
|
122
|
-
gating consumers can route them.
|
|
125
|
+
gating consumers can route them. Every `skipped` row is part
|
|
126
|
+
of the same payload, carrying its `sig.skipped.*` identifier
|
|
127
|
+
as `skip_reason`, so a method missing from your `sig/` has
|
|
128
|
+
its reason next to the rows that did emit. In text mode a
|
|
129
|
+
one-line stderr summary counts the skipped methods per
|
|
130
|
+
reason instead; stdout stays paste-clean.
|
|
123
131
|
|
|
124
132
|
## What method shapes the generator covers
|
|
125
133
|
|
|
126
134
|
Slice-by-slice (each shipped via a CHANGELOG entry — this
|
|
127
135
|
list is the current state):
|
|
128
136
|
|
|
129
|
-
- **Plain instance `def foo`**
|
|
130
|
-
|
|
131
|
-
|
|
137
|
+
- **Plain instance `def foo`** of any parameter shape:
|
|
138
|
+
required, optional, rest, trailing, keyword, keyword-rest,
|
|
139
|
+
`...` forwarding and `&block`. The parameter list mirrors
|
|
140
|
+
the runtime shape with `untyped` in every position (the
|
|
141
|
+
observed union under `--params=observed`), and a block
|
|
142
|
+
renders as `?{ (*untyped) -> untyped }`. Both new-method
|
|
143
|
+
and tighter-return paths apply.
|
|
132
144
|
- **Singleton-side `def self.foo`** and
|
|
133
145
|
`class << self; def foo; end`. Rendered as
|
|
134
146
|
`def self.foo: ...`; matched against
|
|
@@ -143,10 +155,8 @@ list is the current state):
|
|
|
143
155
|
are recognised as user-authored and never produce a
|
|
144
156
|
duplicate `def` insertion.
|
|
145
157
|
|
|
146
|
-
Method shapes the generator does **not** cover yet
|
|
147
|
-
silently skips):
|
|
158
|
+
Method shapes the generator does **not** cover yet:
|
|
148
159
|
|
|
149
|
-
- Optional / rest / keyword / block / forwarding parameters.
|
|
150
160
|
- `define_method(:name) { ... }`.
|
|
151
161
|
- Methods whose body types as `Dynamic[top]` (the body
|
|
152
162
|
inference cannot prove a useful return type).
|
|
@@ -303,9 +313,9 @@ inference, not a separate analysis.
|
|
|
303
313
|
|
|
304
314
|
## Limits today
|
|
305
315
|
|
|
306
|
-
-
|
|
307
|
-
|
|
308
|
-
|
|
316
|
+
- A block parameter always renders as the lenient
|
|
317
|
+
`?{ (*untyped) -> untyped }`; a typed block signature
|
|
318
|
+
waits on the engine tracking yield shapes end-to-end.
|
|
309
319
|
- `define_method` and `Data.define`-specific emission are
|
|
310
320
|
deferred follow-ups (`Data.define`-derived readers come
|
|
311
321
|
through if a method body exists).
|
|
@@ -114,16 +114,25 @@ missing file.
|
|
|
114
114
|
|
|
115
115
|
## `rigor type-of`
|
|
116
116
|
|
|
117
|
-
Print
|
|
117
|
+
Print inferred types at one or more source positions.
|
|
118
118
|
|
|
119
119
|
```sh
|
|
120
|
-
rigor type-of FILE:LINE:COL
|
|
121
|
-
rigor type-of FILE LINE COL
|
|
120
|
+
rigor type-of [options] FILE:LINE[:COL] [FILE:LINE[:COL] ...]
|
|
121
|
+
rigor type-of [options] FILE LINE COL
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
The colon form is repeatable and keeps argument order while
|
|
125
|
+
parsing and scope-indexing each file once. Omit `COL` to print a
|
|
126
|
+
table of up to 40 expressions that start on that line, outermost
|
|
127
|
+
first at each 1-based column; the table marks when further
|
|
128
|
+
expressions were omitted. The legacy three-argument form accepts
|
|
129
|
+
one exact position.
|
|
130
|
+
|
|
131
|
+
`--format=json` keeps one result as the original flat object and
|
|
132
|
+
wraps several results in a `results` array. Line queries add a
|
|
133
|
+
`line_enumerations` array whose `shown` and `total` counts make
|
|
134
|
+
truncation explicit. `--trace` records fail-soft fallbacks,
|
|
135
|
+
after the rows of a line table in text output. The editor-mode
|
|
127
136
|
`--tmp-file` / `--instead-of` pair is accepted as on `check`.
|
|
128
137
|
|
|
129
138
|
## `rigor trace`
|
|
@@ -466,6 +475,13 @@ exits `1`: you asked for a write and did not get one. Such a skip
|
|
|
466
475
|
is a bug in Rigor's RBS rendering, not in your code — please
|
|
467
476
|
report it.
|
|
468
477
|
|
|
478
|
+
A method the generator declined for any other reason (a body
|
|
479
|
+
that proves only `untyped`, a user-authored declaration it will
|
|
480
|
+
not overwrite) is never a silent absence: under `--format=json`
|
|
481
|
+
it is a `skipped` row of the `candidates` array with its
|
|
482
|
+
`sig.skipped.*` identifier in `skip_reason`, and in text mode a
|
|
483
|
+
one-line stderr summary counts the skipped methods per reason.
|
|
484
|
+
|
|
469
485
|
## `rigor lsp`
|
|
470
486
|
|
|
471
487
|
Run the Language Server over stdio. See
|
|
@@ -1001,7 +1017,7 @@ Queued today:
|
|
|
1001
1017
|
|
|
1002
1018
|
| Feature id | Kind | What it changes |
|
|
1003
1019
|
| --- | --- | --- |
|
|
1004
|
-
| `reject-unparseable-signatures` | severity |
|
|
1020
|
+
| `reject-unparseable-signatures` | severity | A broken RBS set **fails the run** instead of degrading it silently: an unparseable `.rbs` under `signature_paths:` (`rbs.coverage.quarantined-signature` → `error`), a declaration that collides on resolve and collapses the whole environment (`rbs.coverage.environment-build-failed` → `error`), and a duplicate method definition that collapses one class's method surface (`rbs.coverage.definition-build-failed` → `error`). |
|
|
1005
1021
|
| `use-of-void-value` | severity | Using a value recovered from an author-declared `-> void` return in value context is reported as `static.value-use.void` (`warning`). |
|
|
1006
1022
|
| `discovery-seeded-mutation-sites` | behaviour | [`rigor coverage --protection --mutation`](15-type-protection-coverage.md) measures against the same cross-file project discovery Tier 1 already uses — both when picking the sites and when deciding whether a breakage was caught — so a call on a project class declared in a *sibling* file is measured instead of dropped, and a breakage there can actually be caught. **Adds sites to the denominator, so the reported effectiveness ratio moves** — check it against any `--threshold` you pin in CI before adopting. |
|
|
1007
1023
|
| `dependent-closure-kill-oracle` | behaviour | [`rigor coverage --protection --mutation`](15-type-protection-coverage.md) counts a breakage as caught when the diagnostic appears anywhere in the mutated file **or the files that depend on it**, instead of in the mutated file alone — so changing what a method returns counts as caught when the error lands in its callers. Can only **add** kills, so the ratio moves up or not at all; it costs about a third more wall time per mutant, and a ratio measured under it is not comparable with one measured without it. |
|
|
@@ -74,7 +74,7 @@ carries no `documentation_url`.
|
|
|
74
74
|
| <a id="rule-effect-envelope-exceeded"></a>`effect.envelope-exceeded` | A method performs an effect its declared envelope does not admit — its proven effect labels (its own body plus everything it calls) are not covered by the `%a{pure}` or `%a{rigor:v1:effect …}` bound written on it or on its class. Opt-in twice over: it needs an `effects:` block in `.rigor.yml` and an envelope you wrote. Positioned at the Ruby `def`. Unproven ("and possibly more") effects never fire, and `mutate.local` is tolerated by every envelope. | high |
|
|
75
75
|
| <a id="rule-effect-liskov-widened"></a>`effect.liskov-widened` | An override escapes the envelope written on the method it overrides. A `PgRepo` is usable wherever a `Repo` is, so a `%a{rigor:v1:effect io.db}` on `Repo#find` binds `PgRepo#find` too: an implementation may be purer than the bound it inherits, never less pure. Either what the override *does* exceeds the inherited bound, or the envelope the override *declares for itself* is wider than it. Both sides must be authored — nothing fires unless someone wrote an envelope on the ancestor — and only subclassing counts, not `include`. Positioned at the override's `def`. Needs an `effects:` block. | high |
|
|
76
76
|
| <a id="rule-effect-unknown-label"></a>`effect.unknown-label` | An effect declaration names a label the registry does not know — a typo in an envelope (`%a{rigor:v1:effect io.bd}`), or a member of `effects.tolerated:`. The whole tag then reads as unbounded, so the declaration quietly stops doing anything; this says so. Positioned at the declaration: the `.rbs` line, the `.rb` line for an rbs-inline annotation, or `.rigor.yml` for a config value. `# rigor:disable` comments are not read out of `.rbs` or `.rigor.yml`, so use `disable:` or the baseline there. Only fires where the spelling is evidently meant to be a label (close to a known one, next to a known one, dotted, or retired) — a word nothing resembles stays silent, because you may be opening your own vocabulary. Needs an `effects:` block. | high |
|
|
77
|
-
| <a id="rule-effect-annotations-unchecked"></a>`effect.annotations-unchecked` | Your signatures carry `%a{pure}` / `%a{rigor:v1:effect …}` but `.rigor.yml` has no `effects:` block, so nothing checks them. One `:info` per run, positioned at the first annotation. An annotation never turns effect collection on by itself — that would make one line in one file more expensive for every run — so this is how it tells you instead. Add `effects: {}` to opt in, or `disable:` it to keep the annotations documentary. Reads both annotation lanes — `sig/*.rbs` and rbs-inline comments —
|
|
77
|
+
| <a id="rule-effect-annotations-unchecked"></a>`effect.annotations-unchecked` | Your signatures carry `%a{pure}` / `%a{rigor:v1:effect …}` but `.rigor.yml` has no `effects:` block, so nothing checks them. One `:info` per run, positioned at the first annotation. An annotation never turns effect collection on by itself — that would make one line in one file more expensive for every run — so this is how it tells you instead. Add `effects: {}` to opt in, or `disable:` it to keep the annotations documentary. Reads both annotation lanes — `sig/*.rbs` and rbs-inline comments — on every run, a warm `--incremental` with nothing changed included. | — |
|
|
78
78
|
| <a id="rule-suppression-unknown-rule"></a>`suppression.unknown-rule` | A `# rigor:disable[-file]` comment names a rule that does not exist (typically a typo), so the suppression silently does nothing. `plugin.`-prefixed tokens are never flagged. | high |
|
|
79
79
|
| <a id="rule-suppression-empty"></a>`suppression.empty` | A `# rigor:disable[-file]` comment lists no rules, so it suppresses nothing. | high |
|
|
80
80
|
| <a id="rule-suppression-unknown-marker"></a>`suppression.unknown-marker` | A comment uses a suppression marker Rigor does not recognise — typically the RuboCop reflex `# rigor:disable-next-line <rule>` or `# rigor:enable <rule>`. Rigor's only markers are `# rigor:disable <rules>` (suppresses on its own line) and `# rigor:disable-file <rules>`, so the comment suppresses nothing. | high |
|