Improving macro error messages

One of the big difficulties for Crystal users, IMO, is that macro error messages are often incomprehensible. I recently came up with a means of giving users a bit more clue, which is shown in the error report from --error-trace and does not add space in the compiled code. Here is some sample macro code to demonstrate what I’m doing:

macro delegate2(name, to, file = __FILE__, line = __LINE__)
# This method was generated through an invocation of
# `{{@type.name}}.delegate2 {{name}}, to: {{to}}`
# at {{file.id}}:{{line.id}}.

When I test the code with a deliberately-injected error, I get this output from crystal build --error-trace . Every ASTNode, and thus every macro argument, includes ASTNode#filename and ASTNode#line_number , you don’t have to pass FILE and LINE.

There was a problem expanding macro 'delegate2'

Called macro defined in delegate.cr:2:3

 2 | macro delegate2(name, to, file = __FILE__, line = __LINE__)

Which expanded to:

 >   1 |    
 ... (blank lines)
 >  31 |        
 >  32 |        # This method was generated through an invocation of
 >  33 |        # `A.delegate2 inspect, to: @a`
 >  34 |        # at /home/bruce/Crystal/delegate/src/delegate.cr:46.
 >  35 |        # The target method is at
 >  36 |        # /usr/share/crystal/src/reference.cr:71
 >  37 |        def inspect(io : IO) : Nil 
 >  38 |          @a.inspect(io) 
 >  39 |          @a.foobar
 >  40 |        end
 >  41 |        
... (blank lines)
 >  74 |        
 >  75 |        # This method was generated through an invocation of
 >  76 |        # `A.delegate2 inspect, to: @a`
 >  77 |        # at /home/bruce/Crystal/delegate/src/delegate.cr:46.
 >  78 |        # The target method is at
 >  79 |        # /usr/share/crystal/src/object.cr:155
 >  80 |        def inspect(io : IO) : Nil 
 >  81 |          @a.inspect(io) 
 >  82 |          @a.foobar
 >  83 |        end
 >  84 |        
... (blank lines)
 >  93 |        
 >  94 |        # This method was generated through an invocation of
 >  95 |        # `A.delegate2 inspect, to: @a`
 >  96 |        # at /home/bruce/Crystal/delegate/src/delegate.cr:46.
 >  97 |        # The target method is at
 >  98 |        # /usr/share/crystal/src/object.cr:143
 >  99 |        def inspect() : String 
 > 100 |          @a.inspect() 
 > 101 |          @a.foobar
 > 102 |        end

The progam containing this is at https://github.com/BrucePerens/delegate

Now, while comments work, especially with error trace on, we could improve on this:

  1. Assign an annotation that will be output when there is an error in a macro, even without --error-trace. Macro developers could put information about the macro arguments in it.
  2. Just print the filename and line number of all macro arguments in macro error messages.
6 Likes