Skip to content

Commit d33bac5

Browse files
author
José Valim
committed
Allow :deprecated to be given as doc metadata
1 parent 8d082ad commit d33bac5

File tree

6 files changed

+51
-72
lines changed

6 files changed

+51
-72
lines changed

lib/elixir/lib/kernel/typespec.ex

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -170,31 +170,9 @@ defmodule Kernel.Typespec do
170170
end
171171

172172
defp get_doc_meta(spec_meta, doc_kind, set) do
173-
doc_meta =
174-
case :ets.take(set, {doc_kind, :meta}) do
175-
[{{^doc_kind, :meta}, metadata, _}] -> Map.merge(metadata, spec_meta)
176-
[] -> spec_meta
177-
end
178-
179-
doc_meta
180-
|> get_since_info(set)
181-
|> get_deprecated_info(set)
182-
end
183-
184-
defp get_since_info(doc_meta, set) do
185-
case :ets.take(set, :since) do
186-
[{:since, since, _}] when is_binary(since) -> Map.put_new(doc_meta, :since, since)
187-
_ -> doc_meta
188-
end
189-
end
190-
191-
defp get_deprecated_info(doc_meta, set) do
192-
case :ets.take(set, :deprecated) do
193-
[{:deprecated, reason, _}] when is_binary(reason) ->
194-
Map.put_new(doc_meta, :deprecated, reason)
195-
196-
_ ->
197-
doc_meta
173+
case :ets.take(set, {doc_kind, :meta}) do
174+
[{{^doc_kind, :meta}, metadata, _}] -> Map.merge(metadata, spec_meta)
175+
[] -> spec_meta
198176
end
199177
end
200178

lib/elixir/lib/module.ex

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,26 @@ defmodule Module do
125125
The Mix compiler automatically looks for calls to deprecated modules
126126
and emit warnings during compilation, computed via `mix xref warnings`.
127127
128-
The `@deprecated` attribute may also be used to annotate callbacks or
129-
types. In these cases the annotation is only informational and doesn't
130-
come with compile time checks. In any case, the annotation will also
131-
become part of the documentation metadata to be used by tools like
132-
ExDoc or IEx.
128+
Using the `@deprecated` attribute will also be reflected in the
129+
documentation of the given function and macro. You can choose between
130+
the `@deprecated` attribute and the documentation metadata to provide
131+
hard-deprecations (with warnings) and soft-deprecations (with warnings):
132+
133+
This is a soft-deprecation as it simply annotates the documentation
134+
as deprecated:
135+
136+
@doc deprecated: "Use Kernel.length/1 instead"
137+
def size(keyword)
138+
139+
This is a hard-deprecation as it emits warnings and annotates the
140+
documentation as deprecated:
141+
142+
@deprecated "Use Kernel.length/1 instead"
143+
def size(keyword)
144+
145+
Currently `@deprecated` only supports functions and macros. However
146+
you can use the `:deprecated` key in the annotation metadata to
147+
annotate the docs of modules, types and callbacks too.
133148
134149
We recommend using this feature with care, especially library authors.
135150
Deprecating code always pushes the burden towards library users. We
@@ -181,7 +196,7 @@ defmodule Module do
181196
182197
Note that since the compiler also defines some additional metadata,
183198
there are a few reserved keys that will be ignored and warned if used.
184-
Currently these are: `:opaque`, `:defaults`, and `:deprecated`.
199+
Currently these are: `:opaque` and `:defaults`.
185200
186201
### `@dialyzer`
187202
@@ -1336,8 +1351,7 @@ defmodule Module do
13361351
defp doc_key(:defmacro), do: :macro
13371352

13381353
defp compile_doc_meta(set, bag, name, arity, defaults) do
1339-
doc_meta = compile_since(%{}, set)
1340-
doc_meta = compile_deprecated(doc_meta, set, bag, name, arity, defaults)
1354+
doc_meta = compile_deprecated(%{}, set, bag, name, arity, defaults)
13411355
doc_meta = get_doc_meta(doc_meta, set)
13421356
add_defaults_count(doc_meta, defaults)
13431357
end
@@ -1349,13 +1363,6 @@ defmodule Module do
13491363
end
13501364
end
13511365

1352-
defp compile_since(doc_meta, table) do
1353-
case :ets.take(table, :since) do
1354-
[{:since, since, _}] when is_binary(since) -> Map.put(doc_meta, :since, since)
1355-
_ -> doc_meta
1356-
end
1357-
end
1358-
13591366
defp compile_deprecated(doc_meta, set, bag, name, arity, defaults) do
13601367
case :ets.take(set, :deprecated) do
13611368
[{:deprecated, reason, _}] when is_binary(reason) ->
@@ -1764,7 +1771,7 @@ defmodule Module do
17641771
# We do not insert into the :attributes key in the bag table
17651772
# because those attributes are deleted on every definition.
17661773
defp put_attribute(module, key, value, line, set, _bag)
1767-
when key in [:doc, :typedoc, :moduledoc, :impl, :since, :deprecated] do
1774+
when key in [:doc, :typedoc, :moduledoc, :impl, :deprecated] do
17681775
try do
17691776
:ets.lookup_element(set, key, 3)
17701777
catch
@@ -1812,7 +1819,7 @@ defmodule Module do
18121819
{line, doc} when is_integer(line) ->
18131820
raise ArgumentError,
18141821
"@#{key} is a built-in module attribute for documentation. It should be " <>
1815-
"a binary, boolean, keyword list, or nil, got: #{inspect(doc)}"
1822+
"a string, boolean, keyword list, or nil, got: #{inspect(doc)}"
18161823

18171824
_other ->
18181825
raise ArgumentError,
@@ -1878,13 +1885,6 @@ defmodule Module do
18781885
"dependencies. It should be a string the path to a file, got: #{inspect(value)}"
18791886
end
18801887

1881-
defp preprocess_attribute(:since, value) when not is_binary(value) do
1882-
raise ArgumentError,
1883-
"@since is a built-in module attribute used for documentation purposes. " <>
1884-
"It should be a string representing the version a function, macro, type or " <>
1885-
"callback was added, got: #{inspect(value)}"
1886-
end
1887-
18881888
defp preprocess_attribute(:deprecated, value) when not is_binary(value) do
18891889
raise ArgumentError,
18901890
"@deprecated is a built-in module attribute that annotates a definition as deprecated. " <>
@@ -1902,7 +1902,7 @@ defmodule Module do
19021902
_ ->
19031903
raise ArgumentError,
19041904
"@file is a built-in module attribute that annotates the file and line the next " <>
1905-
"definition comes from. It should be a string or a {string, line} tuple as value, " <>
1905+
"definition comes from. It should be a string or {string, line} tuple as value, " <>
19061906
"got: #{inspect(value)}"
19071907
end
19081908
end
@@ -1914,8 +1914,8 @@ defmodule Module do
19141914
defp preprocess_doc_meta([], _module, _line, map), do: map
19151915

19161916
defp preprocess_doc_meta([{key, _} | tail], module, line, map)
1917-
when key in [:opaque, :defaults, :deprecated] do
1918-
message = "ignoring reserved documentation metadata key: :#{key}"
1917+
when key in [:opaque, :defaults] do
1918+
message = "ignoring reserved documentation metadata key: #{inspect(key)}"
19191919
IO.warn(message, attribute_stack(module, line))
19201920
preprocess_doc_meta(tail, module, line, map)
19211921
end
@@ -1927,9 +1927,14 @@ defmodule Module do
19271927

19281928
defp validate_doc_meta(:since, value) when not is_binary(value) do
19291929
raise ArgumentError,
1930-
":since is a built-in documentation metadata key. " <>
1931-
"It should be a string representing the version a function, macro, type or " <>
1932-
"callback was added, got: #{inspect(value)}"
1930+
":since is a built-in documentation metadata key. It should be a string representing " <>
1931+
"the version in which the documented entity was added, got: #{inspect(value)}"
1932+
end
1933+
1934+
defp validate_doc_meta(:deprecated, value) when not is_binary(value) do
1935+
raise ArgumentError,
1936+
":deprecated is a built-in documentation metadata key. It should be a string " <>
1937+
"representing the replacement for the deprecated entity, got: #{inspect(value)}"
19331938
end
19341939

19351940
defp validate_doc_meta(_, _), do: :ok

lib/elixir/src/elixir_module.erl

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ is_open(Module) ->
3333
delete_definition_attributes(#{module := Module}, _, _, _, _, _) ->
3434
{DataSet, _} = data_tables(Module),
3535
ets:delete(DataSet, doc),
36-
ets:delete(DataSet, since),
3736
ets:delete(DataSet, deprecated),
3837
ets:delete(DataSet, impl).
3938

@@ -332,7 +331,7 @@ warn_unused_attributes(File, DataSet, DataBag, PersistedAttrs) ->
332331
StoredAttrs = bag_lookup_element(DataBag, attributes, 2),
333332
%% This is the same list as in Module.put_attribute
334333
%% without moduledoc which are never warned on.
335-
Attrs = [doc, typedoc, impl, since, deprecated | StoredAttrs -- PersistedAttrs],
334+
Attrs = [doc, typedoc, impl, deprecated | StoredAttrs -- PersistedAttrs],
336335
Query = [{{Attr, '_', '$1'}, [{is_integer, '$1'}], [[Attr, '$1']]} || Attr <- Attrs],
337336
[elixir_errors:form_warn([{line, Line}], File, ?MODULE, {unused_attribute, Key})
338337
|| [Key, Line] <- ets:select(DataSet, Query)].
@@ -405,8 +404,8 @@ format_error({unused_attribute, impl}) ->
405404
"module attribute @impl was set but no definition follows it";
406405
format_error({unused_attribute, deprecated}) ->
407406
"module attribute @deprecated was set but no definition follows it";
408-
format_error({unused_attribute, since}) ->
409-
"module attribute @since was set but no definition follows it";
407+
format_error({unused_attribute, since}) -> %% TODO: Remove this on v1.8
408+
"module attribute @since is deprecated, please use \"@doc since: ...\" instead";
410409
format_error({unused_attribute, Attr}) ->
411410
io_lib:format("module attribute @~ts was set but never used", [Attr]);
412411
format_error({invalid_module, Module}) ->

lib/elixir/test/elixir/kernel/docs_test.exs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ defmodule Kernel.DocsTest do
7070
end
7171
end
7272

73-
assert_raise ArgumentError, ~r/should be a binary, boolean, keyword list, or nil/, fn ->
73+
assert_raise ArgumentError, ~r/should be a string, boolean, keyword list, or nil/, fn ->
7474
defmodule AtSyntaxDocAttributesFormat do
7575
@moduledoc :not_a_binary
7676
end
@@ -155,9 +155,8 @@ defmodule Kernel.DocsTest do
155155
@opaque bar(any) :: any
156156

157157
@doc "Callback doc"
158-
@doc since: "1.2.3", color: :red
158+
@doc since: "1.2.3", color: :red, deprecated: "use baz/2 instead"
159159
@doc color: :blue, stable: true
160-
@deprecated "use baz/2 instead"
161160
@callback foo(any) :: any
162161

163162
@doc false
@@ -170,13 +169,12 @@ defmodule Kernel.DocsTest do
170169

171170
@doc "Function doc"
172171
@doc since: "1.2.3", color: :red
173-
@since "1.2-doc-meta-takes-precedence"
174172
@doc color: :blue, stable: true
175173
@deprecated "use baz/2 instead"
176174
def foo(arg \\ 0), do: arg + 1
177175

178176
@doc "Multiple bodiless clause doc"
179-
@since "1.2.3"
177+
@deprecated "something else"
180178
def bar(_arg)
181179
def bar(_arg)
182180
def bar(arg), do: arg + 1
@@ -235,7 +233,7 @@ defmodule Kernel.DocsTest do
235233
assert {{:function, :__struct__, 1}, _, ["__struct__(kv)"], :none, %{}} = function_struct_1
236234

237235
assert {{:function, :bar, 1}, _, ["bar(arg)"], %{"en" => "Multiple bodiless clause doc"},
238-
%{since: "1.2.3"}} = function_bar
236+
%{deprecated: "something else"}} = function_bar
239237

240238
assert {{:function, :baz, 1}, _, ["baz(arg)"],
241239
%{"en" => "Multiple bodiless clause and docs"}, %{since: "1.2.3"}} = function_baz

lib/elixir/test/elixir/kernel/warning_test.exs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,7 +1142,7 @@ defmodule Kernel.WarningTest do
11421142
capture_err(fn ->
11431143
Code.eval_string("""
11441144
defmodule Sample do
1145-
@typedoc opaque: false, deprecated: "do not use"
1145+
@typedoc opaque: false
11461146
@type t :: binary
11471147
11481148
@doc defaults: 3, since: "1.2.3"
@@ -1152,7 +1152,6 @@ defmodule Kernel.WarningTest do
11521152
end)
11531153

11541154
assert output =~ "ignoring reserved documentation metadata key: :opaque"
1155-
assert output =~ "ignoring reserved documentation metadata key: :deprecated"
11561155
assert output =~ "ignoring reserved documentation metadata key: :defaults"
11571156
refute output =~ ":since"
11581157
after

lib/ex_unit/lib/ex_unit/doc_test.ex

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -189,18 +189,18 @@ defmodule ExUnit.DocTest do
189189
"""
190190
defmacro doctest(module, opts \\ []) do
191191
require =
192-
if is_atom(Macro.expand(mod, __CALLER__)) do
192+
if is_atom(Macro.expand(module, __CALLER__)) do
193193
quote do
194-
require unquote(mod)
194+
require unquote(module)
195195
end
196196
end
197197

198198
tests =
199-
quote bind_quoted: [mod: mod, opts: opts] do
199+
quote bind_quoted: [module: module, opts: opts] do
200200
env = __ENV__
201-
file = ExUnit.DocTest.__file__(mod)
201+
file = ExUnit.DocTest.__file__(module)
202202

203-
for {name, test} <- ExUnit.DocTest.__doctests__(mod, opts) do
203+
for {name, test} <- ExUnit.DocTest.__doctests__(module, opts) do
204204
@file file
205205
doc = ExUnit.Case.register_test(env, :doctest, name, [])
206206
def unquote(doc)(_), do: unquote(test)

0 commit comments

Comments
 (0)