rigortype 0.3.6 → 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.
- 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/manual/02-cli-reference.md +16 -7
- 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 +116 -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 +44 -9
- 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 +93 -1
- data/lib/rigor/analysis/runner/pool_coordinator.rb +34 -0
- data/lib/rigor/analysis/runner/project_pre_passes.rb +15 -11
- data/lib/rigor/analysis/runner/run_snapshots.rb +7 -2
- data/lib/rigor/analysis/runner.rb +147 -10
- data/lib/rigor/analysis/worker_session.rb +10 -1
- data/lib/rigor/bleeding_edge.rb +7 -6
- data/lib/rigor/builtins/hkt_builtins.rb +45 -6
- data/lib/rigor/builtins/predefined_constant_refinements.rb +108 -59
- data/lib/rigor/builtins/static_return_refinements.rb +20 -1
- 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 +31 -8
- data/lib/rigor/cache/store.rb +10 -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 +18 -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/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 +525 -46
- data/lib/rigor/environment.rb +38 -15
- 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 +46 -0
- data/lib/rigor/inference/hkt_sugar_translator.rb +93 -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/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/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 +254 -5
- data/lib/rigor/sig_gen/renderer.rb +1 -6
- 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 +3 -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 +69 -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
|
# ---------------------------------------------------------------
|
|
@@ -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`
|
|
@@ -1001,7 +1010,7 @@ Queued today:
|
|
|
1001
1010
|
|
|
1002
1011
|
| Feature id | Kind | What it changes |
|
|
1003
1012
|
| --- | --- | --- |
|
|
1004
|
-
| `reject-unparseable-signatures` | severity |
|
|
1013
|
+
| `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
1014
|
| `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
1015
|
| `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
1016
|
| `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. |
|
|
@@ -63,19 +63,31 @@ syntax-highlighted for a tty — through
|
|
|
63
63
|
colorizer; `--no-color` (and the `NO_COLOR` environment
|
|
64
64
|
variable) disable the colour.
|
|
65
65
|
|
|
66
|
-
## `rigor type-of` —
|
|
66
|
+
## `rigor type-of` — exact positions or a whole line
|
|
67
67
|
|
|
68
|
-
When you
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
When you need a few expression types — typically while chasing
|
|
69
|
+
down why a diagnostic did or did not fire — query the exact
|
|
70
|
+
positions together so Rigor loads the project and each source
|
|
71
|
+
file once:
|
|
71
72
|
|
|
72
73
|
```sh
|
|
73
|
-
rigor type-of lib/example.rb:12:8
|
|
74
|
+
rigor type-of lib/example.rb:12:8 lib/example.rb:12:14
|
|
74
75
|
```
|
|
75
76
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
Leave off the column to avoid counting it by hand. Rigor prints
|
|
78
|
+
a table of the first 40 expressions starting on the line,
|
|
79
|
+
outermost first at each 1-based column, and marks a truncated
|
|
80
|
+
table:
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
rigor type-of lib/example.rb:12
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`--format=json` emits a machine-readable result for tooling: one
|
|
87
|
+
result stays a flat object, while several results use a `results`
|
|
88
|
+
array. Line queries add `line_enumerations` metadata with the
|
|
89
|
+
shown and total expression counts. An exact position is the same
|
|
90
|
+
query the editor integration answers on hover.
|
|
79
91
|
|
|
80
92
|
## `rigor trace` — watch the inference happen
|
|
81
93
|
|
data/docs/manual/07-plugins.md
CHANGED
|
@@ -49,6 +49,10 @@ each plugin's options — but the families today are:
|
|
|
49
49
|
`rigor-factorybot`.
|
|
50
50
|
- **dry-rb** — `rigor-dry-types`, `rigor-dry-schema`,
|
|
51
51
|
`rigor-dry-struct`, `rigor-dry-validation`.
|
|
52
|
+
- **FFI** — `rigor-ffi` and sub-plugins (`rigor-ffi-rzmq`,
|
|
53
|
+
`rigor-rbnacl`, `rigor-sassc`, `rigor-ethon`). For plugin authors,
|
|
54
|
+
`rigor-ffi` provides the `ffi_binding_recognizer` class DSL on
|
|
55
|
+
`Rigor::Plugin::Base` to recognize custom binding definitions.
|
|
52
56
|
- **Other ecosystems** — `rigor-sinatra`, `rigor-hanami`,
|
|
53
57
|
`rigor-devise`, `rigor-pundit`, `rigor-sidekiq`,
|
|
54
58
|
`rigor-graphql`, `rigor-statesman`, `rigor-sorbet`,
|
|
@@ -56,6 +56,43 @@ plugins:
|
|
|
56
56
|
view_search_paths: ["app/views"] # default
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
+
## What it types
|
|
60
|
+
|
|
61
|
+
Inside a controller, `params`, `request`, `session`, `flash` and
|
|
62
|
+
`cookies` type as their Action Pack classes, and so do the chains
|
|
63
|
+
built on them:
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
request.post? # bool — and so do get? / put? / patch? / delete? /
|
|
67
|
+
# head? / options? / trace? / link? / unlink? /
|
|
68
|
+
# xhr? / xml_http_request? / ssl? / local? / form_data?
|
|
69
|
+
flash.now # ActionDispatch::Flash::FlashNow
|
|
70
|
+
flash.keep # ActionDispatch::Flash::FlashHash
|
|
71
|
+
flash[:notice] = "hi" # "hi" — an assignment is its right-hand side
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Rigor ships **no signature** for these Action Pack classes, on
|
|
75
|
+
purpose: the receiver becomes concrete (so `rigor coverage
|
|
76
|
+
--protection` counts the site) while the method surface stays
|
|
77
|
+
lenient, so `request.headers`, `flash.now[:alert] = x` and anything
|
|
78
|
+
else the framework adds resolve without a diagnostic. A partial
|
|
79
|
+
signature would be worse than none — every member it omitted would
|
|
80
|
+
become a false `call.undefined-method`.
|
|
81
|
+
|
|
82
|
+
The predicates are typed `bool` — the union of `true` and `false` —
|
|
83
|
+
which is both the real contract (every one of them is an `==`,
|
|
84
|
+
`match?` or `include?` in Rails or Rack) and the reason they are safe
|
|
85
|
+
to type at all: a condition that folds needs to prove *one* constant,
|
|
86
|
+
and a union of both never does. `return unless request.post?` and
|
|
87
|
+
`mode = request.get? ? :a : :b` read exactly as they did before.
|
|
88
|
+
|
|
89
|
+
`request.format` is **not** typed. That inertness argument is
|
|
90
|
+
narrower than it looks — it holds for a union of the two boolean
|
|
91
|
+
constants, not for a union of ordinary classes, which is nil-free and
|
|
92
|
+
so *can* fold a condition — and `Mime::NullType`, the value `format`
|
|
93
|
+
returns when there is no format, answers `nil?` with `true` while
|
|
94
|
+
being a real object. Typing it needs a nil-aware answer.
|
|
95
|
+
|
|
59
96
|
## Limitations
|
|
60
97
|
|
|
61
98
|
- **Implicit-self helpers only.** `*_path` / `*_url` calls with an
|
|
@@ -69,6 +106,30 @@ plugins:
|
|
|
69
106
|
knows what `rigor-rails-routes` published, and `permit`
|
|
70
107
|
validation only what `rigor-activerecord` published — enabling
|
|
71
108
|
those producers widens what this plugin can check.
|
|
109
|
+
- **`params[:key]` stays untyped.** Inside a controller, `params`
|
|
110
|
+
types as `ActionController::Parameters`, and so does the result
|
|
111
|
+
of every builder method that always returns one — `require`,
|
|
112
|
+
`permit`, `permit!`, `expect`, `slice`, `slice!`, `except`,
|
|
113
|
+
`without`, `extract!`, `merge`, `merge!`, `reverse_merge`,
|
|
114
|
+
`reverse_merge!`, `with_defaults`, `with_defaults!`, `compact`,
|
|
115
|
+
`compact_blank`, `deep_dup` — so a chain built from them keeps a
|
|
116
|
+
concrete receiver throughout. A subscript read is deliberately
|
|
117
|
+
left untyped: `params[:missing]` is `nil` at runtime, and a type
|
|
118
|
+
that says otherwise would let the flow rules fold live
|
|
119
|
+
conditions (`if params[:q]`, `url.nil?`) to a constant and report
|
|
120
|
+
working code. Methods whose result depends on the call — `dig`,
|
|
121
|
+
`fetch`, `compact!`, and the block-less `select` / `reject` /
|
|
122
|
+
`transform_keys` / `transform_values` — are untyped for the same
|
|
123
|
+
reason.
|
|
124
|
+
- **`flash[:key]` and `session[:key]` stay untyped too**, for that
|
|
125
|
+
same reason and measured the same way. Both are leaf reads that
|
|
126
|
+
return whatever was stored — or `nil` for a key that is not set.
|
|
127
|
+
A non-nil type folds `mode = flash[:notice] ? … : …` to one arm and
|
|
128
|
+
reports the live guard after it; a nullable one puts
|
|
129
|
+
`call.possible-nil-receiver` on `note = flash[:notice];
|
|
130
|
+
note.upcase`. Writing through them is unaffected: `flash[:k] = v`
|
|
131
|
+
is `v` because that is what an assignment expression means in Ruby,
|
|
132
|
+
with no rule needed.
|
|
72
133
|
|
|
73
134
|
## Plugin internals
|
|
74
135
|
|