Feature #22304
openAdd rb_warn_to_remove_at() for deprecation warnings shown by default
Description
Deprecations such as #22205 and #22276 are scheduled in phases: a warning shown only when Warning[:deprecated] is enabled, then a warning shown by default, then the removal.
For the first phase, rb_warn_deprecated_to_remove_at(X.Y, fmt, suggest, ...) (#17432) prints "... is deprecated and will be removed in Ruby X.Y", and a RUBY_DEBUG build fails to compile when the version reaches X.Y.
For the second phase, there is no equivalent API. A warning with the :deprecated category is suppressed by default by definition, so the warning must be emitted with rb_warn without a category, and the message and the version check have to be written by hand.
I propose to add rb_warn_to_remove_at(X.Y, fmt, suggest, ...) to internal/error.h: the same message and compile-time check, emitted with rb_warn. The name does not contain "deprecated" because rb_warn_deprecated* means a warning gated by Warning[:deprecated].
Updated by shugo (Shugo Maeda) 10 days ago
- Related to Feature #22205: Deprecate ruby2_keywords added
- Related to Bug #22276: alias in a module falls back to Object even in classes not inheriting from Object added
Updated by shugo (Shugo Maeda) 3 days ago
In addition, how about a higher-level macro that takes both versions of the schedule?
Before 4.2 it warns only when Warning[:deprecated] is enabled, from 4.2 it warns by default via rb_warn_to_remove, and at 4.3 a RUBY_DEBUG build fails to compile. The version check is done at compile time, so the phase switches automatically on a version bump and cannot be forgotten.
#22276 and #22273 can use it in 4.1 already, with no behavior change until 4.2.
Updated by shugo (Shugo Maeda) 3 days ago
I've created a pull request: https://github.com/ruby/ruby/pull/18904
Updated by Dan0042 (Daniel DeLorme) 3 days ago
ยท Edited
I very much like the idea, but the issue with version numbers is that major version jumps break linear assumptions.
For example, in 2023 (Ruby 3.3), if we want to deprecate something in 3 years, we might target 3.6. But as Ruby jumped from 3.4 to 4.0, then 3.6 never exists; 4.1 is the actual 3-year mark.
Because versions don't scale linearly with time, specifying a target year (e.g., 2026) or a relative year offset (e.g., 3) in rb_warn_scheduled_deprecation is much safer.
This also enables a useful ENV flag to filter warnings by horizon:
- RUBY_DEPRECATION_HORIZON=5: Show all deprecations coming up in the next 5 years.
- RUBY_DEPRECATION_HORIZON=1: Only show warnings due immediately next version. (default?)
- RUBY_DEPRECATION_HORIZON=0: No warnings.
Updated by shugo (Shugo Maeda) 3 days ago
Thank you for the idea. I think the API should keep the version numbers, because the warning message has to tell users the version ("will be removed in Ruby 4.3"), and a year cannot be converted to a version at compile time.
A major version jump does not cause a silent problem. For example, when the version jumps from 3.4 to 4.0, a schedule such as (3.5, 3.6) is reached at once, and a RUBY_DEBUG build fails to compile at the removal check, exactly as rb_warn_deprecated_to_remove_at does today. So each schedule has to be reviewed and rewritten by hand at a major bump, which we should do anyway.
A horizon filter like RUBY_DEPRECATION_HORIZON sounds useful, but it is a separate run-time mechanism (an environment variable and a version comparison), so how about proposing it in a separate ticket?
Updated by Dan0042 (Daniel DeLorme) 2 days ago
shugo (Shugo Maeda) wrote in #note-5:
a year cannot be converted to a version at compile time.
I don't understand why converting a year to a version at compile time would be needed. We would need to compare year >= RUBY_RELEASE_YEAR at compile time for the hard removal check. But for runtime deprecation warnings, displaying the target version is just a simple year-to-version mapping in a central place. When a major version bump happens, we update that single mapping, instead of editing N places across the codebase where someone hardcoded the wrong version. That seems much less error-prone.
(Also, as a C habit, I have a bias toward passing integers rather than floats or strings.)
each schedule has to be reviewed and rewritten by hand at a major bump, which we should do anyway.
Sorry for the trouble, but could you explain why "we should do anyway"? I might be missing something here.
A horizon filter like
RUBY_DEPRECATION_HORIZON... how about proposing it in a separate ticket?
Fair enough! I can open a separate ticket for that. Though it only works if we switch to year-based targets, as version numbers make dynamic horizons tricky to calculate.
Updated by shugo (Shugo Maeda) 2 days ago
Dan0042 (Daniel DeLorme) wrote in #note-6:
Sorry for the trouble, but could you explain why "we should do anyway"? I might be missing something here.
A major version bump is a chance to introduce incompatibilities, so I think we should decide for each deprecation whether to keep the schedule or to remove it at the major version.
My intention was not to make the schedule fully automatic, but to make it fail-safe.
I also see two problems with years:
RUBY_RELEASE_YEARis the date of the commit or the release, so it advances with every patch release, and a compile-time check on it would fail on stable branches.- The yearly release is a convention, not a guarantee. If a release is delayed or skipped, a year-based schedule silently changes its meaning.
If we introduce a new macro for the compile-time check instead of RUBY_RELEASE_YEAR, a serial number like RUBY_MINOR_CODE, which is bumped for each minor version, may be better than a year.
Updated by Dan0042 (Daniel DeLorme) about 10 hours ago
A major version bump is a chance to introduce incompatibilities, so I think we should decide for each deprecation whether to keep the schedule or to remove it at the major version.
Ok I see, that makes sense. It might also be useful to say "deprecate this at v5.0" no matter what year it turns out to be.
So what do you think should happen to
2023-12-25 Ruby 3.3.0 new message: feature X will be deprecated in Ruby 3.6
2025-12-25 Ruby 4.0.0 changed to: feature X will be deprecated in Ruby 4.1
2026-03-26 Ruby 3.3.11 keep incorrect "3.6" message or update to "4.1" ?
Does the updated target version need to be backported to every branch ?
Updated by shugo (Shugo Maeda) about 3 hours ago
Dan0042 (Daniel DeLorme) wrote in #note-8:
Does the updated target version need to be backported to every branch ?
There is a similar case. Ruby 3.3 still says "Process::Status#& is deprecated and will be removed in Ruby 3.4", although the removal was postponed to 3.5 on master and 3.4 was released without removing it. The message was not backported. But I don't know whether that was a deliberate decision, so I would like to hear the opinion of the stable branch maintainers.
@hsbt (Hiroshi SHIBATA) @nagachika (Tomoyuki Chikanaga) @k0kubun (Takashi Kokubun) What do you think?