Consider the following example:
defmodule NestedQuote do
defmacrop macro(arg) do
quote bind_quoted: [arg: arg] do
quote bind_quoted: [arg: arg], do: String.duplicate(arg, 2)
end
end
defmacro __using__(_) do
quote do
def test do
unquote(macro("foo"))
end
end
end
end
defmodule UseNestedQuote do
use NestedQuote
end
It has two macro expansions, one inside __using__/1 when it invokes
macro/1, and another when used. The issue, however, is that the
`arg` of `String.duplicate(arg, 2)` gets tagged with the context
of `macro/1` but it should be tagged with the context of `__using__`
since it is a quote inside a quote. This patch addresses this.
We use this option in ExUnit as we are escaping
the code to eventually convert it to a string
representation and therefore the metadata is not
relevant.
This makes defining guards easier, since it does the boilerplate
for you and raises at compile time in the defguard macro when
you do something not allowed in guards (as opposed to whenever
the new guard happens to be used as a guard).
It takes a `defguard name(args) when implementation` invocation,
passes the implementation through `:elixir_expand.expand/2` in
a guard context, and if nothing is raised, transforms the impl
into a macro that works both inside and outside of guard contexts.
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
Prepositions can be tricky as there is no definite rule or formula for
choosing. In general we use `in` when talking about something inside the
file or module, such as a function.
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
As of today, in many cases we got inspection of functions wrong. For example,
Macro.to_string/2 failed to properly escape functions resulting in cases like:
iex> Macro.to_string(quote(do: Foo."42"()))
"Foo.42()"
where we output a representation of the code that is not syntactically valid. A
similar scenario happened with Exception.format_mfa/3 (which prints functions as
well) and the implementation of the Inspect protocol for functions. With this
commit, we:
* introduce the exported but hidden Macro.classify_identifier/1 function to
classify an identifier (atom or binary) as one of :callable, :non_callable,
:alias, or other
* rework the implementation of Inspect.Function.inspect/1,
Exception.format_mfa/1, and Macro.to_string/2 to properly inspect functions
The screaming snake case was keeping its original format after being
camelized. As mentioned in issue #5627. This change allows it to behave
like the all lower snake case when camelized.
Module attributes that are imported from other
contexts are not warned. This keeps the same
semantics regarding variables and function definition
ordering which are not warned when they come from
other contexts.
Right now, application of anonymous function literals in
Macro.to_string/2 is represented like this:
iex> Macro.to_string(quote do: (fn x -> x end).(1))
"fn x -> x end.(1)"
While this is valid, I think it's far more common to have such calls
with the literal fn wrapped in parens:
(fn x -> x end).(1)
This commit makes Macro.to_string/2 behave like this.
We were only asserting that some code raised an error (ArgumentError),
but we weren't asserting on the message, thus making the test's context
hard to grasp. I added the error message in the assertion and reworded
the test name a bit.
Right now, this nasty error pops out:
iex> :foo |> (fn x -> x end)
** (CompileError) iex:1: expected -> clauses in fn
This happens because of what we do in `Macro.pipe/3`, which is: we take
the AST of a function call as the second argument and insert the first
argument in the list of arguments for the function call, at the position
specified by the third argument. The AST for a `fn` looks exactly like
the AST of a function call (to the `:fn` function):
iex> quote do: fn :ok -> :ok; :err -> :err end
{:fn, [], [{:->, [], [[:ok], :ok]}, {:->, [], [[:error], :error]}]}
It's easy to see that the "arguments" to `:fn` are the `->` clauses in
the function. This means that doing `:foo |> (fn x -> x end)` basically
adds another clause to the function:
iex> Macro.pipe(:foo, quote(do: fn :ok -> :ok; :err -> :err end), 0)
{:fn, [], [:foo, {:->, [], [[:ok], :ok]}, {:->, [], [[:err], :err]}]}
and this makes `:elixir_fn.expand/3` blow up because it doesn't know how
to handle the `:foo` clause (because it's not a clause!).
With this commit, we blow up early in `Macro.pipe/3` when we see a
`{:fn, _, _}` call:
iex> :foo |> (fn x -> x end)
** (ArgumentError) cannot pipe :foo into an anonymous function
without calling the function; use :foo |> (fn x -> x end).()
Right now, this funny thing happens:
iex> Macro |> Env
Macro.Env
This happens because `|>/2` calls `Macro.pipe/3` under the hood, and
`Macro.pipe/3` inserts the left-hand side at the beginning of the list
of arguments in the AST for the function call on the right-hand
side. Aliases, however, look exactly like a call to the __aliases__
function:
iex> quote do: Macro.Env
{:__aliases__, [alias: false], [:Macro, :Env]}
and that's why `Macro |> Env` becomes `Macro.Env`. With this commit, we
explicitly take care of `:__aliases__` in a `Macro.pipe/3` call, so that
we can error out in such cases:
iex> Macro |> Env
** (ArgumentError) cannot pipe Macro into Env, ...