Hi guys, I just wanted to report to you a very not meaningful message that generate crystal in some circumstances.
You can reproduce easily that.
When you assign a wrong type to a var and then use it badly, the message is just not helpful at all.
Look at that code:
size = "wrong type"
(0..size).each do |number|
puts "hello"
end
That is the error I have got:
zohran@alienware-m17-r3:~/Downloads$ crystal test.cr
Showing last frame. Use --error-trace for full trace.
There was a problem expanding macro 'macro_140325733465824'
Code in /usr/share/crystal/src/range.cr:120:5
120 | {% if E == Nil %}
^
Called macro defined in /usr/share/crystal/src/range.cr:120:5
120 | {% if E == Nil %}
Which expanded to:
> 1 |
> 2 | end_value = @end
> 3 | while end_value.nil? || current < end_value
^--------
Error: expected argument #1 to 'Int32#<' to be Float32, Float64, Int128, Int16, Int32, Int64, Int8, Number, UInt128, UInt16, UInt32, UInt64 or UInt8, not String
Overloads are:
- Int32#<(other : Int8)
- Int32#<(other : Int16)
- Int32#<(other : Int32)
- Int32#<(other : Int64)
- Int32#<(other : Int128)
- Int32#<(other : UInt8)
- Int32#<(other : UInt16)
- Int32#<(other : UInt32)
- Int32#<(other : UInt64)
- Int32#<(other : UInt128)
- Int32#<(other : Float32)
- Int32#<(other : Float64)
- Comparable(T)#<(other : T)
That was fine because I know perfectly my project, but good luck to track that in a big project 
I think will be helpful if we can just get the line when the problem occured
An other example to show sometime crystal will compile a program even obviously a variable is not declared at all in a function:
def returnBug
text = "hello"
return "#{fakevar}"
end
That code is totally compilable
I wanted to report that before but I didn’t have the time, sorry for that.
I just try to show some weakness of the compiler output. Because if a beginner want to use crystal, it can be a big wall I think
If you run with --error-trace
, you’ll be able to see the source of the error:
3 | (0..size).each do |number|
^---
Error: instantiating 'Range(Int32, String)#each()'
What’s going on here is here is there is no enforcement that both sides of a Range
are the same type. Would be curious to see if anyone has any use cases for this, as I’d imagine most have both sides being the same. Otherwise may be worth exploring if there’s something that could be done to prevent these kinds of issues at compile time. Like how we have the strict_multi_assign
flag.
What is likely happening is if you never call the method, the compiler excludes it from type checking since it’s never used. But it most certainly results in a error if you were to try and use it. E.g.
def returnBug
text = "hello"
return "#{fakevar}"
end
pp returnBug
Results in:
3 | return "#{fakevar}"
^------
Error: undefined local variable or method 'fakevar' for top-level
So in that case, I think that output for the log should be not an option but returned by default like that. But okay I didn’t know we can do like that 
It’s just I think undeclared variable should be detected before the compilation, without extra code
It’s called out in the first line of the default trace ;)
zohran@alienware-m17-r3:~/Downloads$ crystal test.cr
Showing last frame. Use --error-trace for full trace.
Related: Improve compile time error with --error-trace disabled · Issue #8410 · crystal-lang/crystal · GitHub
Compilation has to happen for there to be a compiler error. This specific case just doesn’t get caught due to the “feature” of the compiler I mentioned earlier. This is why it’s good to have test coverage on your methods/types. Because it’s better to have the tests detect typos or something that would cause compile time errors than the end user when they go to use something for the first time.