mirror of
https://gitea.invidious.io/iv-org/shard-spectator.git
synced 2024-08-15 00:53:35 +00:00
Workaround compiler bug introduced by aa9ca7a
BUG: trying to upcast Nil (Crystal::NilType) <- NoReturn (Crystal::NoReturnType) (Exception) from /crystal/src/compiler/crystal/codegen/cast.cr:668:5 in 'upcast_distinct' from /crystal/src/compiler/crystal/codegen/cast.cr:523:15 in 'upcast' from /crystal/src/compiler/crystal/codegen/codegen.cr:1369:11 in 'accept' from /crystal/src/compiler/crystal/codegen/codegen.cr:2274:9 in 'visit' from /crystal/src/compiler/crystal/syntax/visitor.cr:27:12 in 'accept' ...
This commit is contained in:
parent
f1465c44d8
commit
9efb6c95dd
1 changed files with 50 additions and 26 deletions
|
@ -149,7 +149,14 @@ module Spectator
|
||||||
if %stub = _spectator_find_stub(%call)
|
if %stub = _spectator_find_stub(%call)
|
||||||
# Cast the stub or return value to the expected type.
|
# Cast the stub or return value to the expected type.
|
||||||
# This is necessary to match the expected return type of the original method.
|
# This is necessary to match the expected return type of the original method.
|
||||||
_spectator_cast_stub_value(%stub, %call, typeof({{original}}), {{method.return_type && method.return_type.resolve != NoReturn && method.return_type.resolve <= Nil || method.return_type.is_a?(Union) && method.return_type.types.map(&.resolve).includes?(Nil)}})
|
_spectator_cast_stub_value(%stub, %call, typeof({{original}}),
|
||||||
|
{{ if method.return_type && method.return_type.resolve == NoReturn
|
||||||
|
:no_return
|
||||||
|
elsif method.return_type && method.return_type.resolve <= Nil || method.return_type.is_a?(Union) && method.return_type.types.map(&.resolve).includes?(Nil)
|
||||||
|
:nil
|
||||||
|
else
|
||||||
|
:raise
|
||||||
|
end }})
|
||||||
else
|
else
|
||||||
# Delegate missing stub behavior to concrete type.
|
# Delegate missing stub behavior to concrete type.
|
||||||
_spectator_stub_fallback(%call, typeof({{original}})) do
|
_spectator_stub_fallback(%call, typeof({{original}})) do
|
||||||
|
@ -241,7 +248,14 @@ module Spectator
|
||||||
# This is necessary to match the expected return type of the original method.
|
# This is necessary to match the expected return type of the original method.
|
||||||
{% if method.return_type %}
|
{% if method.return_type %}
|
||||||
# Return type restriction takes priority since it can be a superset of the original implementation.
|
# Return type restriction takes priority since it can be a superset of the original implementation.
|
||||||
_spectator_cast_stub_value(%stub, %call, {{method.return_type}}, {{method.return_type.resolve != NoReturn && method.return_type.resolve <= Nil || method.return_type.is_a?(Union) && method.return_type.types.map(&.resolve).includes?(Nil)}})
|
_spectator_cast_stub_value(%stub, %call, {{method.return_type}},
|
||||||
|
{{ if method.return_type.resolve == NoReturn
|
||||||
|
:no_return
|
||||||
|
elsif method.return_type.resolve <= Nil || method.return_type.is_a?(Union) && method.return_type.types.map(&.resolve).includes?(Nil)
|
||||||
|
:nil
|
||||||
|
else
|
||||||
|
:raise
|
||||||
|
end }})
|
||||||
{% elsif !method.abstract? %}
|
{% elsif !method.abstract? %}
|
||||||
# The method isn't abstract, infer the type it returns without calling it.
|
# The method isn't abstract, infer the type it returns without calling it.
|
||||||
_spectator_cast_stub_value(%stub, %call, typeof({{original}}))
|
_spectator_cast_stub_value(%stub, %call, typeof({{original}}))
|
||||||
|
@ -376,8 +390,11 @@ module Spectator
|
||||||
# *stub* is the variable holding the stub.
|
# *stub* is the variable holding the stub.
|
||||||
# *call* is the variable holding the captured method call.
|
# *call* is the variable holding the captured method call.
|
||||||
# *type* is the expected type to cast the value to.
|
# *type* is the expected type to cast the value to.
|
||||||
# *nullable* indicates whether *type* can be nil or not.
|
# *fail_cast* indicates the behavior used when the value returned by the stub can't be cast to *type*.
|
||||||
private macro _spectator_cast_stub_value(stub, call, type, nullable = true)
|
# - `:nil` - return nil.
|
||||||
|
# - `:raise` - raise a `TypeCastError`.
|
||||||
|
# - `:no_return` - raise as no value should be returned.
|
||||||
|
private macro _spectator_cast_stub_value(stub, call, type, fail_cast = :nil)
|
||||||
# Attempt to cast the stub to the method's return type.
|
# Attempt to cast the stub to the method's return type.
|
||||||
# If successful, return the value of the stub.
|
# If successful, return the value of the stub.
|
||||||
# This is a common usage where the return type is simple and matches the stub type exactly.
|
# This is a common usage where the return type is simple and matches the stub type exactly.
|
||||||
|
@ -390,29 +407,36 @@ module Spectator
|
||||||
# Assert that it will (this should never fail).
|
# Assert that it will (this should never fail).
|
||||||
raise TypeCastError.new("Stub has no value") unless {{stub}}.responds_to?(:call)
|
raise TypeCastError.new("Stub has no value") unless {{stub}}.responds_to?(:call)
|
||||||
|
|
||||||
# Get the value as-is from the stub.
|
{% if fail_cast == :no_return %}
|
||||||
# This will be compiled as a union of all known stubbed value types.
|
{{stub}}.call({{call}})
|
||||||
%value = {{stub}}.call({{call}})
|
raise TypeCastError.new("#{_spectator_stubbed_name} received message #{ {{call}} } and is attempting to return a value, but it shouldn't have returned (`NoReturn`).")
|
||||||
|
{% else %}
|
||||||
|
# Get the value as-is from the stub.
|
||||||
|
# This will be compiled as a union of all known stubbed value types.
|
||||||
|
%value = {{stub}}.call({{call}})
|
||||||
|
|
||||||
# Attempt to cast the value to the method's return type.
|
# Attempt to cast the value to the method's return type.
|
||||||
# If successful, which it will be in most cases, return it.
|
# If successful, which it will be in most cases, return it.
|
||||||
# The caller will receive a properly typed value without unions or other side-effects.
|
# The caller will receive a properly typed value without unions or other side-effects.
|
||||||
if %cast = %value.as?({{type}})
|
if %cast = %value.as?({{type}})
|
||||||
%cast
|
%cast
|
||||||
else
|
else
|
||||||
{% if nullable %}
|
{% if fail_cast == :nil %}
|
||||||
nil
|
nil
|
||||||
{% else %}
|
{% elsif fail_cast == :raise %}
|
||||||
# The stubbed value was something else entirely and cannot be cast to the return type.
|
# The stubbed value was something else entirely and cannot be cast to the return type.
|
||||||
# There's something weird going on (compiler bug?) that sometimes causes this class lookup to fail.
|
# There's something weird going on (compiler bug?) that sometimes causes this class lookup to fail.
|
||||||
%type = begin
|
%type = begin
|
||||||
%value.class.to_s
|
%value.class.to_s
|
||||||
rescue
|
rescue
|
||||||
"<Unknown>"
|
"<Unknown>"
|
||||||
end
|
end
|
||||||
raise TypeCastError.new("#{_spectator_stubbed_name} received message #{ {{call}} } and is attempting to return a `#{%type}`, but returned type must be `#{ {{type}} }`.")
|
raise TypeCastError.new("#{_spectator_stubbed_name} received message #{ {{call}} } and is attempting to return a `#{%type}`, but returned type must be `#{ {{type}} }`.")
|
||||||
{% end %}
|
{% else %}
|
||||||
end
|
{% raise "fail_cast must be :nil, :raise, or :no_return, but got: #{fail_cast}" %}
|
||||||
|
{% end %}
|
||||||
|
end
|
||||||
|
{% end %}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue