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