Skip to content

Commit da50584

Browse files
author
José Valim
committed
Deprecate def/4 and friends in favor of def/2 with unquote and friends
1 parent cae9b05 commit da50584

File tree

9 files changed

+37
-73
lines changed

9 files changed

+37
-73
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
* [IO] `IO.stream(device)` and `IO.binstream(device)` are deprecated in favor of `IO.stream(device, :line)` and `IO.binstream(device, :line)`
4343
* [Kernel] `list_to_binary/1`, `binary_to_list/1` and `binary_to_list/3` are deprecated in favor of `String.from_char_list!/1` and `String.to_char_list!/1` for characters and `:binary.list_to_bin/1`, `:binary.bin_to_list/1` and `:binary.bin_to_list/3` for bytes
4444
* [Kernel] `to_binary/1` is deprecated in favor of `to_string/1`
45+
* [Kernel] Deprecate `def/4` and friends in favor of `def/2` with unquote and friends
4546
* [Macro] `Macro.unescape_binary/1` and `Macro.unescape_binary/2` are deprecated in favor of `Macro.unescape_string/1` and `Macro.unescape_string/2`
4647
* [Mix] `:umbrella` option for umbrella paths has been deprecated in favor of `:in_umbrella`
4748

lib/elixir/lib/kernel.ex

Lines changed: 10 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,38 +1317,7 @@ defmodule Kernel do
13171317
"""
13181318
defmacro def(name, do: contents)
13191319

1320-
@doc """
1321-
This macro allows a function to be defined more explicitly
1322-
by accepting the name, args and guards as different entries.
1323-
1324-
Unlike `def/2`, the macro arguments are evaluated
1325-
and therefore require quoting.
1326-
1327-
The `name` must be an atom, the `arguments` a list where each
1328-
element represents another argument and `guards` a list of
1329-
clauses, where each clause is disjunct.
1330-
1331-
## Examples
1332-
1333-
The most common mistake when using this macro is to pass the
1334-
arguments without quoting:
1335-
1336-
def :some_function, [first_arg, second_arg], is_list(first_arg) do
1337-
# ...
1338-
end
1339-
1340-
However, the example above will fail because it will attempt to
1341-
evaluate `[first_arg, second_arg]` and fail because the variable
1342-
`first_arg` is not defined. Therefore, we need to use quote:
1343-
1344-
name = :some_function
1345-
args = quote(do: [first_arg, second_arg])
1346-
guards = quote(do: is_list(first_arg))
1347-
exprs = quote(do: ...)
1348-
1349-
def name, args, guards, do: exprs
1350-
1351-
"""
1320+
@doc false
13521321
defmacro def(name, args, guards, do: contents)
13531322

13541323
@doc """
@@ -1372,9 +1341,7 @@ defmodule Kernel do
13721341
"""
13731342
defmacro defp(name, do: contents)
13741343

1375-
@doc """
1376-
The same as `def/4` but generates a private function.
1377-
"""
1344+
@doc false
13781345
defmacro defp(name, args, guards, do: contents)
13791346

13801347
@doc """
@@ -1398,9 +1365,7 @@ defmodule Kernel do
13981365
"""
13991366
defmacro defmacro(name, do: contents)
14001367

1401-
@doc """
1402-
The same as `def/4` but generates a macro.
1403-
"""
1368+
@doc false
14041369
defmacro defmacro(name, args, guards, do: contents)
14051370

14061371
@doc """
@@ -1411,9 +1376,7 @@ defmodule Kernel do
14111376
"""
14121377
defmacro defmacrop(name, do: contents)
14131378

1414-
@doc """
1415-
The same as `def/4` but generates a private macro.
1416-
"""
1379+
@doc false
14171380
defmacro defmacrop(name, args, guards, do: contents)
14181381

14191382
@doc %B"""
@@ -3381,10 +3344,8 @@ defmodule Kernel do
33813344
33823345
"""
33833346
defmacro defdelegate(funs, opts) do
3384-
quote do
3385-
funs = unquote(Macro.escape(funs, unquote: true))
3386-
opts = unquote(opts)
3387-
3347+
funs = Macro.escape(funs, unquote: true)
3348+
quote bind_quoted: [funs: funs, opts: opts] do
33883349
target = Keyword.get(opts, :to) ||
33893350
raise(ArgumentError, message: "Expected to: to be given as argument")
33903351

@@ -3402,10 +3363,11 @@ defmodule Kernel do
34023363
false -> args
34033364
end
34043365

3405-
fun = Keyword.get(opts, :as, name)
3406-
body = quote do: unquote(target).unquote(fun)(unquote_splicing(actual_args))
3366+
fun = Keyword.get(opts, :as, name)
34073367

3408-
def name, args, [], do: body
3368+
def unquote(name)(unquote_splicing(args)) do
3369+
unquote(target).unquote(fun)(unquote_splicing(actual_args))
3370+
end
34093371
end
34103372
end
34113373
end

lib/elixir/lib/protocol.ex

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ defmodule Protocol.DSL do
406406
type_args = lc _ inlist :lists.seq(2, arity), do: quote(do: term)
407407
type_args = [quote(do: t) | type_args]
408408

409-
quote do
409+
meta = quote do
410410
name = unquote(name)
411411
arity = unquote(arity)
412412

@@ -416,13 +416,17 @@ defmodule Protocol.DSL do
416416
# signature that will be used by docs
417417
Kernel.def unquote(name)(unquote_splicing(args))
418418

419-
{ args, body } = Protocol.DSL.args_and_body(__MODULE__, name, arity)
420-
Kernel.def unquote(name), args, [], do: body
421-
422419
# Convert the spec to callback if possible,
423420
# otherwise generate a dummy callback
424421
Protocol.DSL.callback_from_spec(__MODULE__, name, arity) ||
425422
@callback unquote(name)(unquote_splicing(type_args)) :: term
426423
end
424+
425+
impl = quote bind_quoted: [name: name, arity: arity] do
426+
{ args, body } = Protocol.DSL.args_and_body(__MODULE__, name, arity)
427+
Kernel.def unquote(name)(unquote_splicing(args)), do: unquote(body)
428+
end
429+
430+
[meta, impl]
427431
end
428432
end

lib/elixir/src/elixir_macros.erl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ translate({Kind, Meta, [Call, Expr]}, S) when ?defs(Kind) ->
190190
{ elixir_def:wrap_definition(Kind, Meta, TCall, TExpr, CheckClauses, SE), SE };
191191

192192
translate({Kind, Meta, [Name, Args, Guards, Expr]}, S) when ?defs(Kind) ->
193+
elixir_errors:deprecation(Meta, S#elixir_scope.file, "~ts/4 is deprecated, please use ~ts/2 instead", [Kind, Kind]),
193194
assert_module_scope(Meta, Kind, S),
194195
assert_no_function_scope(Meta, Kind, S),
195196
{ TName, SN } = translate_each(Name, S),

lib/elixir/test/elixir/module_test.exs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,13 @@ defmodule ModuleTest.ToBeUsed do
2323
defmacro callback(env) do
2424
value = Module.get_attribute(env.module, :has_callback)
2525
quote do
26-
name = :original_value
27-
args = [1]
28-
guard = []
29-
def name, args, guard, do: unquote(value)
26+
def original_value(1), do: unquote(value)
3027
end
3128
end
3229
end
3330

3431
defmodule ModuleTest.ToUse do
35-
35 = __ENV__.line # Moving the next line around can make tests fail
32+
32 = __ENV__.line # Moving the next line around can make tests fail
3633
def original_value(2), do: true
3734
use ModuleTest.ToBeUsed
3835
end
@@ -62,7 +59,7 @@ defmodule ModuleTest do
6259
end
6360

6461
test :line_from_macro do
65-
assert ModuleTest.ToUse.line == 37
62+
assert ModuleTest.ToUse.line == 34
6663
end
6764

6865
## Callbacks

lib/ex_unit/lib/ex_unit/callbacks.ex

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,9 @@ defmodule ExUnit.Callbacks do
107107
Called before the start of each test.
108108
"""
109109
defmacro setup(var // quote(do: _), block) do
110-
quote do
110+
quote bind_quoted: [var: escape(var), block: escape(block)] do
111111
name = :"__ex_unit_setup_#{length(@ex_unit_setup)}"
112-
defp name, [unquote(escape var)], [], unquote(escape block)
112+
defp unquote(name)(unquote(var)), unquote(block)
113113
@ex_unit_setup [name|@ex_unit_setup]
114114
end
115115
end
@@ -119,9 +119,9 @@ defmodule ExUnit.Callbacks do
119119
message, `teardown` will not be run.
120120
"""
121121
defmacro teardown(var // quote(do: _), block) do
122-
quote do
122+
quote bind_quoted: [var: escape(var), block: escape(block)] do
123123
name = :"__ex_unit_teardown_#{length(@ex_unit_teardown)}"
124-
defp name, [unquote(escape var)], [], unquote(escape block)
124+
defp unquote(name)(unquote(var)), unquote(block)
125125
@ex_unit_teardown [name|@ex_unit_teardown]
126126
end
127127
end
@@ -131,9 +131,9 @@ defmodule ExUnit.Callbacks do
131131
the current module and before any `setup` callbacks.
132132
"""
133133
defmacro setup_all(var // quote(do: _), block) do
134-
quote do
134+
quote bind_quoted: [var: escape(var), block: escape(block)] do
135135
name = :"__ex_unit_setup_all_#{length(@ex_unit_setup_all)}"
136-
defp name, [unquote(escape var)], [], unquote(escape block)
136+
defp unquote(name)(unquote(var)), unquote(block)
137137
@ex_unit_setup_all [name|@ex_unit_setup_all]
138138
end
139139
end
@@ -142,9 +142,9 @@ defmodule ExUnit.Callbacks do
142142
Called once after the last test finishes without emitting an :exit message.
143143
"""
144144
defmacro teardown_all(var // quote(do: _), block) do
145-
quote do
145+
quote bind_quoted: [var: escape(var), block: escape(block)] do
146146
name = :"__ex_unit_teardown_all_#{length(@ex_unit_teardown_all)}"
147-
defp name, [unquote(escape var)], [], unquote(escape block)
147+
defp unquote(name)(unquote(var)), unquote(block)
148148
@ex_unit_teardown_all [name|@ex_unit_teardown_all]
149149
end
150150
end

lib/ex_unit/lib/ex_unit/case.ex

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,7 @@ defmodule ExUnit.Case do
9696
:"test_#{message}"
9797
end
9898

99-
def unquote(message)(unquote(var)), do:
100-
unquote(contents)
99+
def unquote(message)(unquote(var)), do: unquote(contents)
101100
end
102101
end
103102
end

lib/ex_unit/lib/ex_unit/doc_test.ex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,9 @@ defmodule ExUnit.DocTest do
130130
This macro is auto-imported with every `ExUnit.Case`.
131131
"""
132132
defmacro doctest(mod, opts // []) do
133-
quote do
134-
lc { name, test } inlist unquote(__MODULE__).__doctests__(unquote(mod), unquote(opts)) do
135-
def name, quote(do: [_]), [], do: test
133+
quote bind_quoted: binding do
134+
lc { name, test } inlist ExUnit.DocTest.__doctests__(mod, opts) do
135+
def unquote(name)(_), do: unquote(test)
136136
end
137137
end
138138
end

lib/mix/lib/mix/generator.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ defmodule Mix.Generator do
7575
`_text` that expects no argument.
7676
"""
7777
defmacro embed_text(name, contents) do
78-
quote do
79-
defp :"#{unquote(name)}_text", [], [], do: unquote(contents)
78+
quote bind_quoted: [name: name, contents: Macro.escape(contents)] do
79+
defp unquote(:"#{name}_text")(), do: unquote(contents)
8080
end
8181
end
8282
end

0 commit comments

Comments
 (0)