Feature #11588


Implement structured warnings

Added by djberg96 (Daniel Berger) about 6 years ago. Updated almost 6 years ago.

Target version:


Ruby’s current warning system is lacking. Warnings are controlled by the -W flag on the command line, and are generated via the Kernel#warn method within code. There are a host of problems with this approach to warnings.

First, warnings aren’t currently testable. With Test::Unit, for example, I can ensure that specific errors are raised in certain conditions via the assert_raise method. There is no analogue for warnings. It would be nice if there were so I could test them.

Second, there is no backtrace information provided with warnings. If I discover a warning I have to wade through the source and figure out where it was generated, because a Kernel#warn call does not provide a line number or method name that I can refer back to, unless it happened to be generated by rb_warn(). For large code bases that can be problematic.

Third, and most significantly, with warning flags it’s all or nothing. I cannot enable or disable specific kinds of warnings. Perl, for example, implements warning control through pragmas. So, for example, I can specify “no warnings uninitialized” in a Perl program and warnings about uninitialized variables go away. With Ruby it’s off, on, or even-more-on (-W0, -W1 or -W2).

What I would like to see are structured warnings. By "structured warnings" I mean a system analogous to the Error class, except that a warning would only emit text to STDERR, not cause the interpreter to exit. In our hypothetical Warning class you still have backtrace information available. And, like Exceptions, there would be a standard hierarchy, with Warning at the top, StandardWarning, UninitializedWarning, RedefinedMethodWarning, DeprecatedMethodWarning, etc. Whatever we can think of.

Such a system would allow you to raise specific warnings within your code:

   class Foo
      def old_method
         warn DeprecatedMethodWarning, 'This method is deprecated. Use new_method instead'
         # Do stuff

The ability to explicitly raise specific types of warnings then makes them testable:

   require 'test/unit'
   class TC_Foo_Tests < Test::Unit::TestCase
      def setup
         @foo =

      # Assume we've added an assert_warn method to Test::Unit
      def test_old_method
         assert_warn(DeprecatedMethodWarning){ @foo.old_method }

And, for sake of backwards compatibility and convenience, a call to Kernel#warn without an explicit warning type would simply raise a StandardWarning in the same way that "raise" without an explicit error type raises a StandardError. You may be wondering about rescue/retry semantics. My opinion on the matter is that warnings should not be rescuable. They are meant to be informational. They are not meant to control program flow. This also lets us avoid having to worry about retry semantics. Not that anyone would retry based on a warning in practice.

Unlike Exceptions you could permanately or temporarily disable warnings to suit your particular preferences in the system I have in mind. For example, in the win32-file library I'm well aware that I've gone and redefined some core File methods. When I run any code that uses win32-file with the -w flag, I get "method redefined" warnings. I don't want to see those because I neither need nor want to be reminded about them. So, using our hypothetical RedefinedMethodWarning class, I could disable them like so:

    RedefinedMethodWarning.disable # No more warnings about method redefinitions!

Or, with block syntax, we could disable a particular warning temporarily:

    # Don't bug me about deprecated method warnings within this block, I know what I'm doing.
       [1,2,3,4,5].indexes(1,3) # Array#indexes is a deprecated method
    # But here I would get a warning since it's outside the block:

Unlike the current warning system, this would allow users to still receive other types of warnings, instead of the on/off switch we have now. And, in case you were wondering why the structured_warnings library isn't quite sufficient, the answer is that it still can’t hook into the existing warnings being raised in core Ruby via rb_warn(), like uninitialized variables or redefined methods.

Related issues

Related to Ruby master - Feature #12026: Support warning processorClosedActions
Related to Ruby master - Feature #17122: Add category to Warning#warnClosedeileencodes (Eileen Uchitelle)Actions

Also available in: Atom PDF