Macro.expand
only expands the specific AST node passed in, not every node within it. I generally do this to fully expand something as much as Elixir’s AST goes (won’t touch special forms and so forth):
quote do
defmodule Hello do
defmacrop foo, do: {:y, [], nil}
def bar(x, foo()), do: x + y
end
end |> Macro.prewalk(&Macro.expand(&1, __ENV__)) |> Macro.to_string |> Code.format_string! |> IO.puts
Results in:
╰─➤ iex
Erlang/OTP 21 [erts-10.0.5] [source] [64-bit] [smp:1:1] [ds:1:1:10] [async-threads:1] [hipe]
Interactive Elixir (1.7.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> quote do
...(1)> defmodule Hello do
...(1)> defmacrop foo, do: {:y, [], nil}
...(1)> def bar(x, foo()), do: x + y
...(1)> end
...(1)> end |> Macro.prewalk(&Macro.expand(&1, __ENV__)) |> Macro.to_string |> Code.format_string! |> IO.puts
alias(Hello, as: nil, warn: false)
:elixir_module.compile(
Hello,
{:__block__, [],
[
{:=, [],
[
{:result, [], Kernel},
{:__block__, [],
[
{:defmacrop, [context: Elixir, import: Kernel],
[{:foo, [context: Elixir], Elixir}, [do: {:{}, [], [:y, [], nil]}]]},
{:def, [context: Elixir, import: Kernel],
[
{:bar, [context: Elixir], [{:x, [], Elixir}, {:foo, [], []}]},
[do: {:+, [context: Elixir, import: Kernel], [{:x, [], Elixir}, {:y, [], Elixir}]}]
]}
]}
]},
{{:., [], [:elixir_utils, :noop]}, [], []},
{:result, [], Kernel}
]},
[{:x1, :elixir_fn, :_@0, x1}],
%{
__struct__: Macro.Env,
aliases: [],
context: nil,
context_modules: [],
contextual_vars: [],
current_vars: %{{:x1, :elixir_fn} => {0, :term}},
file: "iex",
function: nil,
functions: [
{IEx.Helpers,
[
break!: 3,
break!: 4,
breaks: 0,
c: 1,
c: 2,
cd: 1,
clear: 0,
continue: 0,
exports: 0,
exports: 1,
flush: 0,
h: 0,
i: 0,
i: 1,
l: 1,
ls: 0,
ls: 1,
nl: 1,
nl: 2,
open: 0,
pid: 1,
pid: 3,
pwd: 0,
r: 1,
recompile: 0,
recompile: 1,
ref: 1,
ref: 4,
remove_breaks: 0,
remove_breaks: 1,
reset_break: 1,
reset_break: 3,
respawn: 0,
runtime_info: 0,
runtime_info: 1,
v: 0,
v: 1,
whereami: 0,
whereami: 1
]},
{Kernel,
[
!=: 2,
!==: 2,
*: 2,
+: 1,
+: 2,
++: 2,
-: 1,
-: 2,
--: 2,
/: 2,
<: 2,
<=: 2,
==: 2,
===: 2,
=~: 2,
>: 2,
>=: 2,
abs: 1,
apply: 2,
apply: 3,
binary_part: 3,
bit_size: 1,
byte_size: 1,
div: 2,
elem: 2,
exit: 1,
function_exported?: 3,
get_and_update_in: 3,
get_in: 2,
hd: 1,
inspect: 1,
inspect: 2,
is_atom: 1,
is_binary: 1,
is_bitstring: 1,
is_boolean: 1,
is_float: 1,
is_function: 1,
is_function: 2,
is_integer: 1,
is_list: 1,
is_map: 1,
is_number: 1,
is_pid: 1,
is_port: 1,
is_reference: 1,
is_tuple: 1,
length: 1,
macro_exported?: 3,
make_ref: 0,
map_size: 1,
max: 2,
min: 2,
node: 0,
node: 1,
not: 1,
pop_in: 2,
put_elem: 3,
put_in: 3,
rem: 2,
round: 1,
self: 0,
send: 2,
spawn: 1,
spawn: 3,
spawn_link: 1,
spawn_link: 3,
spawn_monitor: 1,
spawn_monitor: 3,
struct: 1,
struct: 2,
struct!: 1,
struct!: 2,
throw: 1,
tl: 1,
trunc: 1,
tuple_size: 1,
update_in: 3
]}
],
lexical_tracker: nil,
line: 6,
macro_aliases: [],
macros: [
{IEx.Helpers,
[
b: 1,
break!: 1,
break!: 2,
h: 1,
import_file: 1,
import_file: 2,
import_file_if_available: 1,
import_if_available: 1,
import_if_available: 2,
open: 1,
t: 1,
use_if_available: 1,
use_if_available: 2
]},
{Kernel,
[
!: 1,
&&: 2,
..: 2,
<>: 2,
@: 1,
alias!: 1,
and: 2,
binding: 0,
binding: 1,
def: 1,
def: 2,
defdelegate: 2,
defexception: 1,
defguard: 1,
defguardp: 1,
defimpl: 2,
defimpl: 3,
defmacro: 1,
defmacro: 2,
defmacrop: 1,
defmacrop: 2,
defmodule: 2,
defoverridable: 1,
defp: 1,
defp: 2,
defprotocol: 2,
defstruct: 1,
destructure: 2,
get_and_update_in: 2,
if: 2,
in: 2,
is_nil: 1,
match?: 2,
or: 2,
pop_in: 1,
put_in: 2,
raise: 1,
raise: 2,
reraise: 2,
reraise: 3,
sigil_C: 2,
sigil_D: 2,
sigil_N: 2,
sigil_R: 2,
sigil_S: 2,
sigil_T: 2,
sigil_W: 2,
sigil_c: 2,
sigil_r: 2,
sigil_s: 2,
sigil_w: 2,
to_char_list: 1,
to_charlist: 1,
to_string: 1,
unless: 2,
update_in: 2,
use: 1,
use: 2,
var!: 1,
var!: 2,
|>: 2,
||: 2
]}
],
module: nil,
prematch_vars: :apply,
requires: [IEx.Helpers, Kernel, Kernel.Typespec],
unused_vars: %{},
vars: [x1: :elixir_fn]
}
)
:ok
I.E. pipe like |> Macro.prewalk(&Macro.expand(&1, __ENV__))
instead. 
Do note, when compiling a module it is the actual :elixir_module.compile
call that expands macros ‘within’ a module, so when you are quoting an entire module definition then macro’s defined within it aren’t expanded until that itself is run. It will expand things in proper scope though otherwise.