DidactMacros
Regarding evaluation of arguments to ExUnit.Assertions.assert/2
This pertains to when a call is made to the function ExUnit.Assertions.assert/2 from within ExUnit.Assertions.translate_operator/6 (matched against the equality_check: false argument-set), and the first argument sent to it is unquote(call).
The call made to this function is from within a quote do: block, and the first argument sent is unquote(call), with the second being a keyword list.
In the ExUnit.Assertions.assert/2 function there is an unless expression that is to execute its code in the event that unquote(call), matched to the variable value, evaluates to false.
How can value ever be false?
When does unquote(call) have a chance to be evaluated?
Code for reference:-
assert/1
defmacro assert(assertion) do
if translated = translate_assertion(:assert, assertion, __CALLER__) do
translated
else
{args, value} = extract_args(assertion, __CALLER__)
quote generated: true do
if value = unquote(value) do
value
else
raise ExUnit.AssertionError,
args: unquote(args),
expr: unquote(escape_quoted(:assert, [], assertion)),
message: "Expected truthy, got #{inspect(value)}"
end
end
end
end
translate_assertion
defp translate_assertion(:assert, {operator, meta, [_, _]} = expr, caller)
when operator in @operator do
if match?([{_, Kernel}], Macro.Env.lookup_import(caller, {operator, 2})) do
left = Macro.var(:left, __MODULE__)
right = Macro.var(:right, __MODULE__)
call = {operator, meta, [left, right]}
equality_check? = operator in [:<, :>, :!==, :!=]
message = "Assertion with #{operator} failed"
translate_operator(:assert, expr, call, message, equality_check?, caller)
end
end
translate_operator
defp translate_operator(kind, {op, meta, [left, right]} = expr, call, message, false, _caller) do
expr = escape_quoted(kind, meta, expr)
context = if op in [:===, :!==], do: :===, else: :==
quote do
left = unquote(left)
right = unquote(right)
ExUnit.Assertions.assert(unquote(call),
left: left,
right: right,
expr: unquote(expr),
message: unquote(message),
context: unquote(context)
)
end
end
assert/2
def assert(value, opts) when is_list(opts) do
unless value, do: raise(ExUnit.AssertionError, opts)
true
end
Marked As Solved
DidactMacros
The quote do…unquote is located in a def do: block which is quoted, so the unquote is in a nested quote which means that is quoted rather than executed.
defp translate_operator(kind, {op, meta, [left, right]} = expr, call, message, false, _caller) do
expr = escape_quoted(kind, meta, expr)
context = if op in [:===, :!==], do: :===, else: :==
quote do
left = unquote(left)
right = unquote(right)
ExUnit.Assertions.assert(unquote(call),
left: left,
right: right,
expr: unquote(expr),
message: unquote(message),
context: unquote(context)
)
end
end
Results in…
quote do
left = unquote(left)
right = unquote(right)
ExUnit.Assertions.assert(unquote(call),
left: left,
right: right,
expr: unquote(expr),
message: unquote(message),
context: unquote(context)
)
end
…getting sent back to the macro that started the chain of function calls.
The macro will then subject the AST to a macro.escape-like processing that identifies :unquote operators in the AST and does not escape the args (children), leaving them in their current form with :unquote stripped or generating a valid AST to describe them. This occurs because the returned AST from translate_operator is the last reachable expression in the macro, and so it must be validated as a formal AST.
The macro will then insert this valid AST fragment into the over all AST at the location were it (the macro, defmacro assert(assertion)) was called.
I initial thought that evaluation was taking place at unquote and at escape, however this was corrected:
If anyone has an question about this I’d be happy to go into further detail.
If I got anything wrong please let me know.
Also Liked
al2o3cr
Code like:
assert 3 * 14 == 6 * 7
will expand to:
left = 3 * 14
right = 6 * 7
ExUnit.Assertions.assert(left == right,
left: left,
right: right,
expr: #<ast omitted>,
message: "Assertion with operator == failed",
context: #<__CALLER__ omitted>
)
al2o3cr
Functions like unq would need to be called from inside a defmacro to get the AST they generate turned into executable code.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









