summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLászló Bácsi <lackac@lackac.hu>2018-07-11 10:19:38 +0200
committerJosé Valim <jose.valim@gmail.com>2018-07-11 10:19:38 +0200
commit0267ae2c7eff6064876c780d7a9083a78d26700d (patch)
treedf4931d2bf2de1947959cbde7560cd49822a38f7
parentec0435b84f19eff70cbf8ea29ae0c167212f6e2e (diff)
downloadelixir-0267ae2c7eff6064876c780d7a9083a78d26700d.tar.gz
Support adding arbitrary docs metadata with `@doc keyword()` (#7852)
-rw-r--r--lib/elixir/lib/access.ex2
-rw-r--r--lib/elixir/lib/agent.ex2
-rw-r--r--lib/elixir/lib/calendar.ex4
-rw-r--r--lib/elixir/lib/calendar/date.ex23
-rw-r--r--lib/elixir/lib/calendar/datetime.ex16
-rw-r--r--lib/elixir/lib/calendar/iso.ex26
-rw-r--r--lib/elixir/lib/calendar/naive_datetime.ex14
-rw-r--r--lib/elixir/lib/calendar/time.ex14
-rw-r--r--lib/elixir/lib/code.ex12
-rw-r--r--lib/elixir/lib/dynamic_supervisor.ex18
-rw-r--r--lib/elixir/lib/enum.ex14
-rw-r--r--lib/elixir/lib/exception.ex4
-rw-r--r--lib/elixir/lib/file.ex10
-rw-r--r--lib/elixir/lib/float.ex2
-rw-r--r--lib/elixir/lib/function.ex6
-rw-r--r--lib/elixir/lib/inspect/algebra.ex18
-rw-r--r--lib/elixir/lib/integer.ex6
-rw-r--r--lib/elixir/lib/kernel.ex4
-rw-r--r--lib/elixir/lib/kernel/parallel_compiler.ex8
-rw-r--r--lib/elixir/lib/kernel/typespec.ex27
-rw-r--r--lib/elixir/lib/keyword.ex2
-rw-r--r--lib/elixir/lib/list.ex18
-rw-r--r--lib/elixir/lib/macro.ex8
-rw-r--r--lib/elixir/lib/macro/env.ex4
-rw-r--r--lib/elixir/lib/map.ex2
-rw-r--r--lib/elixir/lib/module.ex110
-rw-r--r--lib/elixir/lib/port.ex4
-rw-r--r--lib/elixir/lib/regex.ex6
-rw-r--r--lib/elixir/lib/registry.ex36
-rw-r--r--lib/elixir/lib/stream.ex12
-rw-r--r--lib/elixir/lib/supervisor.ex2
-rw-r--r--lib/elixir/lib/system.ex2
-rw-r--r--lib/elixir/lib/task.ex8
-rw-r--r--lib/elixir/lib/task/supervisor.ex8
-rw-r--r--lib/elixir/src/elixir_erl.erl9
-rw-r--r--lib/elixir/test/elixir/kernel/docs_test.exs40
-rw-r--r--lib/elixir/test/elixir/kernel/warning_test.exs22
-rw-r--r--lib/ex_unit/lib/ex_unit/callbacks.ex6
-rw-r--r--lib/iex/lib/iex.ex4
-rw-r--r--lib/iex/lib/iex/helpers.ex30
-rw-r--r--lib/mix/lib/mix/project.ex6
41 files changed, 351 insertions, 218 deletions
diff --git a/lib/elixir/lib/access.ex b/lib/elixir/lib/access.ex
index b4fd8dc15..18279dfc6 100644
--- a/lib/elixir/lib/access.ex
+++ b/lib/elixir/lib/access.ex
@@ -774,7 +774,7 @@ defmodule Access do
** (RuntimeError) Access.filter/1 expected a list, got: %{}
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec filter((term -> boolean)) :: access_fun(data :: list, get_value :: list)
def filter(func) when is_function(func) do
fn op, data, next -> filter(op, data, func, next) end
diff --git a/lib/elixir/lib/agent.ex b/lib/elixir/lib/agent.ex
index 432d84e02..887514e8e 100644
--- a/lib/elixir/lib/agent.ex
+++ b/lib/elixir/lib/agent.ex
@@ -144,7 +144,7 @@ defmodule Agent do
See `Supervisor`.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
def child_spec(arg) do
%{
id: Agent,
diff --git a/lib/elixir/lib/calendar.ex b/lib/elixir/lib/calendar.ex
index 2fc032ce5..2beb1ff2e 100644
--- a/lib/elixir/lib/calendar.ex
+++ b/lib/elixir/lib/calendar.ex
@@ -234,7 +234,7 @@ defmodule Calendar do
between them. If they are compatible, this means that we can also convert
dates as well as naive datetimes between them.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec compatible_calendars?(Calendar.calendar(), Calendar.calendar()) :: boolean
def compatible_calendars?(calendar, calendar), do: true
@@ -247,7 +247,7 @@ defmodule Calendar do
Returns a microsecond tuple truncated to a given precision (`:microsecond`,
`:millisecond` or `:second`).
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec truncate(Calendar.microsecond(), :microsecond | :millisecond | :second) ::
Calendar.microsecond()
def truncate(microsecond_tuple, :microsecond), do: microsecond_tuple
diff --git a/lib/elixir/lib/calendar/date.ex b/lib/elixir/lib/calendar/date.ex
index 3b09bad6e..802cfc5c8 100644
--- a/lib/elixir/lib/calendar/date.ex
+++ b/lib/elixir/lib/calendar/date.ex
@@ -87,8 +87,7 @@ defmodule Date do
-366
"""
-
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec range(Date.t(), Date.t()) :: Date.Range.t()
def range(%Date{calendar: calendar} = first, %Date{calendar: calendar} = last) do
{first_days, _} = to_iso_days(first)
@@ -116,7 +115,7 @@ defmodule Date do
true
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec utc_today(Calendar.calendar()) :: t
def utc_today(calendar \\ Calendar.ISO)
@@ -148,7 +147,7 @@ defmodule Date do
true
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec leap_year?(Calendar.date()) :: boolean()
def leap_year?(date)
@@ -169,7 +168,7 @@ defmodule Date do
29
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec days_in_month(Calendar.date()) :: Calendar.day()
def days_in_month(date)
@@ -186,7 +185,7 @@ defmodule Date do
12
"""
- @since "1.7.0"
+ @doc since: "1.7.0"
@spec months_in_year(Calendar.date()) :: Calendar.month()
def months_in_year(date)
@@ -436,7 +435,7 @@ defmodule Date do
:eq
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec compare(Calendar.date(), Calendar.date()) :: :lt | :eq | :gt
def compare(%{calendar: calendar} = date1, %{calendar: calendar} = date2) do
%{year: year1, month: month1, day: day1} = date1
@@ -484,7 +483,7 @@ defmodule Date do
{:ok, %Date{calendar: Calendar.Holocene, year: 12000, month: 1, day: 1}}
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec convert(Calendar.date(), Calendar.calendar()) ::
{:ok, t} | {:error, :incompatible_calendars}
def convert(%{calendar: calendar, year: year, month: month, day: day}, calendar) do
@@ -518,7 +517,7 @@ defmodule Date do
%Date{calendar: Calendar.Holocene, year: 12000, month: 1, day: 1}
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec convert!(Calendar.date(), Calendar.calendar()) :: t
def convert!(date, calendar) do
case convert(date, calendar) do
@@ -550,7 +549,7 @@ defmodule Date do
~D[-0011-12-30]
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec add(Calendar.date(), integer()) :: t
def add(%{calendar: calendar} = date, days) do
{iso_days, fraction} = to_iso_days(date)
@@ -576,7 +575,7 @@ defmodule Date do
-2
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec diff(Calendar.date(), Calendar.date()) :: integer
def diff(%{calendar: Calendar.ISO} = date1, %{calendar: Calendar.ISO} = date2) do
%{year: year1, month: month1, day: day1} = date1
@@ -634,7 +633,7 @@ defmodule Date do
3
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec day_of_week(Calendar.date()) :: non_neg_integer()
def day_of_week(date)
diff --git a/lib/elixir/lib/calendar/datetime.ex b/lib/elixir/lib/calendar/datetime.ex
index a6b11b5a5..dc8019953 100644
--- a/lib/elixir/lib/calendar/datetime.ex
+++ b/lib/elixir/lib/calendar/datetime.ex
@@ -186,7 +186,7 @@ defmodule DateTime do
#DateTime<2016-05-24 13:26:08.003Z>
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec from_naive(NaiveDateTime.t(), Calendar.time_zone()) :: {:ok, t}
def from_naive(naive_datetime, time_zone)
@@ -232,7 +232,7 @@ defmodule DateTime do
#DateTime<2016-05-24 13:26:08.003Z>
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec from_naive!(NaiveDateTime.t(), Calendar.time_zone()) :: t
def from_naive!(naive_datetime, time_zone) do
case from_naive(naive_datetime, time_zone) do
@@ -502,7 +502,7 @@ defmodule DateTime do
{:error, :invalid_format}
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec from_iso8601(String.t(), Calendar.calendar()) ::
{:ok, t, Calendar.utc_offset()} | {:error, atom}
def from_iso8601(string, calendar \\ Calendar.ISO) when is_binary(string) do
@@ -634,7 +634,7 @@ defmodule DateTime do
:gt
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec compare(Calendar.datetime(), Calendar.datetime()) :: :lt | :eq | :gt
def compare(
%{calendar: _, utc_offset: utc_offset1, std_offset: std_offset1} = datetime1,
@@ -685,7 +685,7 @@ defmodule DateTime do
-18000
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec diff(Calendar.datetime(), Calendar.datetime()) :: integer()
def diff(
%{utc_offset: utc_offset1, std_offset: std_offset1} = datetime1,
@@ -725,7 +725,7 @@ defmodule DateTime do
#DateTime<2017-11-07 11:45:18+01:00 CET Europe/Paris>
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec truncate(t(), :microsecond | :millisecond | :second) :: t()
def truncate(%DateTime{microsecond: microsecond} = datetime, precision) do
%{datetime | microsecond: Calendar.truncate(microsecond, precision)}
@@ -754,7 +754,7 @@ defmodule DateTime do
zone_abbr: "AMT"}}
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec convert(Calendar.datetime(), Calendar.calendar()) ::
{:ok, t} | {:error, :incompatible_calendars}
@@ -829,7 +829,7 @@ defmodule DateTime do
zone_abbr: "AMT"}
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec convert!(Calendar.datetime(), Calendar.calendar()) :: t | no_return
def convert!(datetime, calendar) do
case convert(datetime, calendar) do
diff --git a/lib/elixir/lib/calendar/iso.ex b/lib/elixir/lib/calendar/iso.ex
index 19e8150df..c93bbcbaa 100644
--- a/lib/elixir/lib/calendar/iso.ex
+++ b/lib/elixir/lib/calendar/iso.ex
@@ -51,8 +51,8 @@ defmodule Calendar.ISO do
{-365, {0, 86400000000}}
"""
+ @doc since: "1.5.0"
@impl true
- @since "1.5.0"
@spec naive_datetime_to_iso_days(
Calendar.year(),
Calendar.month(),
@@ -81,6 +81,7 @@ defmodule Calendar.ISO do
{-1, 1, 1, 0, 0, 0, {0, 6}}
"""
+ @doc since: "1.5.0"
@spec naive_datetime_from_iso_days(Calendar.iso_days()) :: {
Calendar.year(),
Calendar.month(),
@@ -91,7 +92,6 @@ defmodule Calendar.ISO do
Calendar.microsecond()
}
@impl true
- @since "1.5.0"
def naive_datetime_from_iso_days({days, day_fraction}) do
{year, month, day} = date_from_iso_days(days)
{hour, minute, second, microsecond} = time_from_day_fraction(day_fraction)
@@ -109,8 +109,8 @@ defmodule Calendar.ISO do
{45296000123, 86400000000}
"""
+ @doc since: "1.5.0"
@impl true
- @since "1.5.0"
@spec time_to_day_fraction(
Calendar.hour(),
Calendar.minute(),
@@ -137,8 +137,8 @@ defmodule Calendar.ISO do
{13, 0, 0, {0, 6}}
"""
+ @doc since: "1.5.0"
@impl true
- @since "1.5.0"
@spec time_from_day_fraction(Calendar.day_fraction()) ::
{Calendar.hour(), Calendar.minute(), Calendar.second(), Calendar.microsecond()}
def time_from_day_fraction({parts_in_day, parts_per_day}) do
@@ -156,7 +156,7 @@ defmodule Calendar.ISO do
# Converts year, month, day to count of days since 0000-01-01.
@doc false
- @since "1.5.0"
+ @doc since: "1.5.0"
def date_to_iso_days(0, 1, 1) do
0
end
@@ -174,7 +174,7 @@ defmodule Calendar.ISO do
# Converts count of days since 0000-01-01 to {year, month, day} tuple.
@doc false
- @since "1.5.0"
+ @doc since: "1.5.0"
def date_from_iso_days(days) when days in 0..3_652_424 do
{year, day_of_year} = days_to_year(days)
extra_day = if leap_year?(year), do: 1, else: 0
@@ -243,8 +243,8 @@ defmodule Calendar.ISO do
12
"""
+ @doc since: "1.7.0"
@impl true
- @since "1.7.0"
@spec months_in_year(year) :: 12
def months_in_year(_year) do
@months_in_year
@@ -462,8 +462,8 @@ defmodule Calendar.ISO do
false
"""
+ @doc since: "1.5.0"
@impl true
- @since "1.5.0"
@spec valid_date?(year, month, day) :: boolean
def valid_date?(year, month, day) do
month in 1..12 and year in -9999..9999 and day in 1..days_in_month(year, month)
@@ -484,8 +484,8 @@ defmodule Calendar.ISO do
false
"""
+ @doc since: "1.5.0"
@impl true
- @since "1.5.0"
@spec valid_time?(Calendar.hour(), Calendar.minute(), Calendar.secon(), Calendar.microsecond()) ::
boolean
def valid_time?(hour, minute, second, {microsecond, precision}) do
@@ -496,8 +496,8 @@ defmodule Calendar.ISO do
@doc """
See `c:Calendar.day_rollover_relative_to_midlight_utc/0` for documentation.
"""
+ @doc since: "1.5.0"
@impl true
- @since "1.5.0"
@spec day_rollover_relative_to_midnight_utc() :: {0, 1}
def day_rollover_relative_to_midnight_utc() do
{0, 1}
@@ -566,7 +566,7 @@ defmodule Calendar.ISO do
do: precision_for_unit(div(number, 10), precision + 1)
@doc false
- @since "1.5.0"
+ @doc since: "1.5.0"
def date_to_iso8601(year, month, day, format \\ :extended) do
date_to_string(year, month, day, format)
end
@@ -671,7 +671,7 @@ defmodule Calendar.ISO do
end
@doc false
- @since "1.5.0"
+ @doc since: "1.5.0"
def iso_days_to_unit({days, {parts, ppd}}, unit) do
day_microseconds = days * @parts_per_day
microseconds = div(parts * @parts_per_day, ppd)
@@ -679,7 +679,7 @@ defmodule Calendar.ISO do
end
@doc false
- @since "1.5.0"
+ @doc since: "1.5.0"
def add_day_fraction_to_iso_days({days, {parts, ppd}}, add, ppd) do
normalize_iso_days(days, parts + add, ppd)
end
diff --git a/lib/elixir/lib/calendar/naive_datetime.ex b/lib/elixir/lib/calendar/naive_datetime.ex
index db0985cfb..bf5d83d29 100644
--- a/lib/elixir/lib/calendar/naive_datetime.ex
+++ b/lib/elixir/lib/calendar/naive_datetime.ex
@@ -96,7 +96,7 @@ defmodule NaiveDateTime do
true
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec utc_now(Calendar.calendar()) :: t
def utc_now(calendar \\ Calendar.ISO)
@@ -243,7 +243,7 @@ defmodule NaiveDateTime do
~N[2014-10-02 00:29:10]
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec add(t, integer, System.time_unit()) :: t
def add(%NaiveDateTime{} = naive_datetime, integer, unit \\ :second)
when is_integer(integer) do
@@ -282,7 +282,7 @@ defmodule NaiveDateTime do
63579428950
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec diff(t, t, System.time_unit()) :: integer
def diff(
%NaiveDateTime{} = naive_datetime1,
@@ -317,7 +317,7 @@ defmodule NaiveDateTime do
~N[2017-11-06 00:23:51]
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec truncate(t(), :microsecond | :millisecond | :second) :: t()
def truncate(%NaiveDateTime{microsecond: microsecond} = naive_datetime, precision) do
%{naive_datetime | microsecond: Calendar.truncate(microsecond, precision)}
@@ -702,7 +702,7 @@ defmodule NaiveDateTime do
:lt
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec compare(Calendar.naive_datetime(), Calendar.naive_datetime()) :: :lt | :eq | :gt
def compare(%{calendar: calendar1} = naive_datetime1, %{calendar: calendar2} = naive_datetime2) do
if Calendar.compatible_calendars?(calendar1, calendar2) do
@@ -739,7 +739,7 @@ defmodule NaiveDateTime do
hour: 13, minute: 30, second: 15, microsecond: {0, 0}}}
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec convert(Calendar.naive_datetime(), Calendar.calendar()) ::
{:ok, t} | {:error, :incompatible_calendars}
@@ -801,7 +801,7 @@ defmodule NaiveDateTime do
hour: 13, minute: 30, second: 15, microsecond: {0, 0}}
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec convert!(Calendar.naive_datetime(), Calendar.calendar()) :: t
def convert!(naive_datetime, calendar) do
case convert(naive_datetime, calendar) do
diff --git a/lib/elixir/lib/calendar/time.ex b/lib/elixir/lib/calendar/time.ex
index b6a77f095..d8a385f22 100644
--- a/lib/elixir/lib/calendar/time.ex
+++ b/lib/elixir/lib/calendar/time.ex
@@ -55,7 +55,7 @@ defmodule Time do
true
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec utc_now(Calendar.calendar()) :: t
def utc_now(calendar \\ Calendar.ISO) do
{:ok, _, time, microsecond} = Calendar.ISO.from_unix(:os.system_time(), :native)
@@ -397,7 +397,7 @@ defmodule Time do
~T[22:59:00.000000]
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec add(Calendar.time(), integer, System.time_unit()) :: t
def add(%{calendar: calendar} = time, number, unit \\ :second) when is_integer(number) do
number = System.convert_time_unit(number, unit, :microsecond)
@@ -444,7 +444,7 @@ defmodule Time do
:gt
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec compare(Calendar.time(), Calendar.time()) :: :lt | :eq | :gt
def compare(%{calendar: calendar} = time1, %{calendar: calendar} = time2) do
%{hour: hour1, minute: minute1, second: second1, microsecond: {microsecond1, _}} = time1
@@ -484,7 +484,7 @@ defmodule Time do
{:ok, %Time{calendar: Calendar.Holocene, hour: 13, minute: 30, second: 15, microsecond: {0, 0}}}
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec convert(Calendar.time(), Calendar.calendar()) :: {:ok, t} | {:error, atom}
# Keep it multiline for proper function clause errors.
@@ -540,7 +540,7 @@ defmodule Time do
%Time{calendar: Calendar.Holocene, hour: 13, minute: 30, second: 15, microsecond: {0, 0}}
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec convert!(Calendar.time(), Calendar.calendar()) :: t
def convert!(time, calendar) do
case convert(time, calendar) do
@@ -591,7 +591,7 @@ defmodule Time do
-2_000_000
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec diff(Calendar.time(), Calendar.time(), System.time_unit()) :: integer
def diff(time1, time2, unit \\ :second) do
fraction1 = to_day_fraction(time1)
@@ -617,7 +617,7 @@ defmodule Time do
~T[01:01:01]
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec truncate(t(), :microsecond | :millisecond | :second) :: t()
def truncate(%Time{microsecond: microsecond} = time, precision) do
%{time | microsecond: Calendar.truncate(microsecond, precision)}
diff --git a/lib/elixir/lib/code.ex b/lib/elixir/lib/code.ex
index 270d2a016..10b7d8811 100644
--- a/lib/elixir/lib/code.ex
+++ b/lib/elixir/lib/code.ex
@@ -40,7 +40,7 @@ defmodule Code do
#=> true
"""
- @since "1.7.0"
+ @doc since: "1.7.0"
@spec required_files() :: [binary]
def required_files do
:elixir_code_server.call(:required)
@@ -72,8 +72,8 @@ defmodule Code do
#=> true
"""
+ @doc since: "1.7.0"
@spec unrequire_files([binary]) :: :ok
- @since "1.7.0"
def unrequire_files(files) do
:elixir_code_server.cast({:unrequire_files, files})
end
@@ -531,7 +531,7 @@ defmodule Code do
user formatting). In such cases, the code formatter will always format to
the latter.
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec format_string!(binary, keyword) :: iodata
def format_string!(string, opts \\ []) when is_binary(string) and is_list(opts) do
line_length = Keyword.get(opts, :line_length, 98)
@@ -545,7 +545,7 @@ defmodule Code do
See `format_string!/2` for more information on code formatting and
available options.
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec format_file!(binary, keyword) :: iodata
def format_file!(file, opts \\ []) when is_binary(file) and is_list(opts) do
string = File.read!(file)
@@ -818,7 +818,7 @@ defmodule Code do
It returns `{:ok, number_of_modules_purged}`.
"""
- @since "1.7.0"
+ @doc since: "1.7.0"
@spec purge_compiler_modules() :: {:ok, non_neg_integer()}
def purge_compiler_modules() do
:elixir_code_server.call(:purge_compiler_modules)
@@ -1070,6 +1070,7 @@ defmodule Code do
{:error, :module_not_found}
"""
+ @doc since: "1.7.0"
@spec fetch_docs(module | String.t()) ::
{:docs_v1, anno, beam_language, format, module_doc :: doc, metadata,
docs :: [{{kind, name, arity}, anno, signature, doc, metadata}]}
@@ -1084,7 +1085,6 @@ defmodule Code do
signature: [binary],
metadata: map,
future_formats: term
- @since "1.7.0"
def fetch_docs(module) when is_atom(module) do
case :code.get_object_code(module) do
{_module, bin, _beam_path} -> do_fetch_docs(bin)
diff --git a/lib/elixir/lib/dynamic_supervisor.ex b/lib/elixir/lib/dynamic_supervisor.ex
index bb6c576cc..ceee31c27 100644
--- a/lib/elixir/lib/dynamic_supervisor.ex
+++ b/lib/elixir/lib/dynamic_supervisor.ex
@@ -188,7 +188,7 @@ defmodule DynamicSupervisor do
See `Supervisor`.
"""
- @since "1.6.1"
+ @doc since: "1.6.1"
def child_spec(opts) when is_list(opts) do
id =
case Keyword.get(opts, :name, DynamicSupervisor) do
@@ -249,7 +249,7 @@ defmodule DynamicSupervisor do
process and exits not only on crashes but also if the parent process exits
with `:normal` reason.
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec start_link(options) :: Supervisor.on_start()
def start_link(options) when is_list(options) do
keys = [:extra_arguments, :max_children, :max_seconds, :max_restarts, :strategy]
@@ -275,7 +275,7 @@ defmodule DynamicSupervisor do
name, the supported values are described in the "Name registration"
section in the `GenServer` module docs.
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec start_link(module, term, GenServer.options()) :: Supervisor.on_start()
def start_link(mod, args, opts \\ []) do
GenServer.start_link(__MODULE__, {mod, args, opts[:name]}, opts)
@@ -304,7 +304,7 @@ defmodule DynamicSupervisor do
of `:max_children` set on the supervisor initialization (see `init/1`), then
this function returns `{:error, :max_children}`.
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec start_child(Supervisor.supervisor(), :supervisor.child_spec() | {module, term} | module) ::
on_start_child()
def start_child(supervisor, {_, _, _, _, _, _} = child_spec) do
@@ -383,7 +383,7 @@ defmodule DynamicSupervisor do
If successful, this function returns `:ok`. If there is no process with
the given PID, this function returns `{:error, :not_found}`.
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec terminate_child(Supervisor.supervisor(), pid) :: :ok | {:error, :not_found}
def terminate_child(supervisor, pid) when is_pid(pid) do
call(supervisor, {:terminate_child, pid})
@@ -409,7 +409,7 @@ defmodule DynamicSupervisor do
* `modules` - as defined in the child specification
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec which_children(Supervisor.supervisor()) :: [
{:undefined, pid | :restarting, :worker | :supervisor, :supervisor.modules()}
]
@@ -434,7 +434,7 @@ defmodule DynamicSupervisor do
is still alive
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec count_children(Supervisor.supervisor()) :: %{
specs: non_neg_integer,
active: non_neg_integer,
@@ -455,7 +455,7 @@ defmodule DynamicSupervisor do
If the reason is any other than `:normal`, `:shutdown` or
`{:shutdown, _}`, an error report is logged.
"""
- @since "1.7.0"
+ @doc since: "1.7.0"
@spec stop(Supervisor.supervisor(), reason :: term, timeout) :: :ok
def stop(supervisor, reason \\ :normal, timeout \\ :infinity) do
GenServer.stop(supervisor, reason, timeout)
@@ -501,7 +501,7 @@ defmodule DynamicSupervisor do
an empty list.
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec init([init_option]) :: {:ok, sup_flags()}
def init(options) when is_list(options) do
unless strategy = options[:strategy] do
diff --git a/lib/elixir/lib/enum.ex b/lib/elixir/lib/enum.ex
index 340307f97..531896dbf 100644
--- a/lib/elixir/lib/enum.ex
+++ b/lib/elixir/lib/enum.ex
@@ -402,7 +402,7 @@ defmodule Enum do
@doc """
Shortcut to `chunk_every(enumerable, count, count)`.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec chunk_every(t, pos_integer) :: [list]
def chunk_every(enumerable, count), do: chunk_every(enumerable, count, count, [])
@@ -442,7 +442,7 @@ defmodule Enum do
[[1, 2], [4, 5]]
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec chunk_every(t, pos_integer, pos_integer, t | :discard) :: [list]
def chunk_every(enumerable, count, step, leftover \\ [])
when is_integer(count) and count > 0 and is_integer(step) and step > 0 do
@@ -479,7 +479,7 @@ defmodule Enum do
[[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec chunk_while(
t,
acc,
@@ -1345,7 +1345,7 @@ defmodule Enum do
[1001, 1002, 1003]
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec map_every(t, non_neg_integer, (element -> any)) :: list
def map_every(enumerable, nth, fun)
@@ -1750,7 +1750,7 @@ defmodule Enum do
{[], []}
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec split_with(t, (element -> any)) :: {list, list}
def split_with(enumerable, fun) do
{acc1, acc2} =
@@ -2158,7 +2158,7 @@ defmodule Enum do
[]
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec slice(t, Range.t()) :: list
def slice(enumerable, first..last) do
{count, fun} = slice_count_and_fun(enumerable)
@@ -2756,7 +2756,7 @@ defmodule Enum do
[{1, :a}, {2, :b}, {3, :c}]
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec zip([t]) :: t
@spec zip(t) :: t
diff --git a/lib/elixir/lib/exception.ex b/lib/elixir/lib/exception.ex
index 70adb5b68..9e78ef89a 100644
--- a/lib/elixir/lib/exception.ex
+++ b/lib/elixir/lib/exception.ex
@@ -156,7 +156,7 @@ defmodule Exception do
If the exception module implements the optional `c:blame/2`
callback, it will be invoked to perform the computation.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec blame(:error, any, stacktrace) :: {t, stacktrace}
@spec blame(non_error_kind, payload, stacktrace) :: {payload, stacktrace} when payload: var
def blame(kind, error, stacktrace)
@@ -189,7 +189,7 @@ defmodule Exception do
Note this functionality requires Erlang/OTP 20, otherwise `:error`
is always returned.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec blame_mfa(module, function, args :: [term]) ::
{:ok, :def | :defp | :defmacro | :defmacrop, [{args :: [term], guards :: [term]}]}
| :error
diff --git a/lib/elixir/lib/file.ex b/lib/elixir/lib/file.ex
index 334e2cc29..3fc783906 100644
--- a/lib/elixir/lib/file.ex
+++ b/lib/elixir/lib/file.ex
@@ -437,7 +437,7 @@ defmodule File do
* `:enotsup` - symbolic links are not supported on the current platform
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec read_link(Path.t()) :: {:ok, binary} | {:error, posix}
def read_link(path) do
case path |> IO.chardata_to_string() |> :file.read_link() do
@@ -450,7 +450,7 @@ defmodule File do
Same as `read_link/1` but returns the target directly or throws `File.Error` if an error is
returned.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec read_link!(Path.t()) :: binary | no_return
def read_link!(path) do
case read_link(path) do
@@ -536,7 +536,7 @@ defmodule File do
If the operating system does not support hard links, returns
`{:error, :enotsup}`.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec ln(Path.t(), Path.t()) :: :ok | {:error, posix}
def ln(existing, new) do
:file.make_link(IO.chardata_to_string(existing), IO.chardata_to_string(new))
@@ -547,7 +547,7 @@ defmodule File do
Returns `:ok` otherwise
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec ln!(Path.t(), Path.t()) :: :ok | no_return
def ln!(existing, new) do
case ln(existing, new) do
@@ -570,7 +570,7 @@ defmodule File do
If the operating system does not support symlinks, returns
`{:error, :enotsup}`.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec ln_s(Path.t(), Path.t()) :: :ok | {:error, posix}
def ln_s(existing, new) do
:file.make_symlink(IO.chardata_to_string(existing), IO.chardata_to_string(new))
diff --git a/lib/elixir/lib/float.ex b/lib/elixir/lib/float.ex
index 2e2103bee..fe503e1dc 100644
--- a/lib/elixir/lib/float.ex
+++ b/lib/elixir/lib/float.ex
@@ -387,7 +387,7 @@ defmodule Float do
{-16, 1}
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec ratio(float) :: {pos_integer | neg_integer, pos_integer}
def ratio(float) when is_float(float) do
<<sign::1, exp::11, significant::52-bitstring>> = <<float::float>>
diff --git a/lib/elixir/lib/function.ex b/lib/elixir/lib/function.ex
index bcf8b37a5..b6a0ce3ff 100644
--- a/lib/elixir/lib/function.ex
+++ b/lib/elixir/lib/function.ex
@@ -32,7 +32,7 @@ defmodule Function do
&String.length/1
"""
- @since "1.7.0"
+ @doc since: "1.7.0"
@spec capture(module, atom, arity) :: fun
def capture(module, function_name, arity) do
:erlang.make_fun(module, function_name, arity)
@@ -93,7 +93,7 @@ defmodule Function do
:length
"""
- @since "1.7.0"
+ @doc since: "1.7.0"
@spec info(fun) :: [{information, term}]
def info(fun), do: :erlang.fun_info(fun)
@@ -131,7 +131,7 @@ defmodule Function do
{:pid, :undefined}
"""
- @since "1.7.0"
+ @doc since: "1.7.0"
@spec info(fun, item) :: {item, term} when item: information
def info(fun, item), do: :erlang.fun_info(fun, item)
end
diff --git a/lib/elixir/lib/inspect/algebra.ex b/lib/elixir/lib/inspect/algebra.ex
index dfde5bd92..322fde270 100644
--- a/lib/elixir/lib/inspect/algebra.ex
+++ b/lib/elixir/lib/inspect/algebra.ex
@@ -340,7 +340,7 @@ defmodule Inspect.Algebra do
"[1! 2! 3! ...]"
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec container_doc(t, [any], t, Inspect.Opts.t(), (term, Inspect.Opts.t() -> t), keyword()) ::
t
def container_doc(left, collection, right, inspect, fun, opts \\ [])
@@ -468,7 +468,7 @@ defmodule Inspect.Algebra do
["olá", " ", "mundo"]
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec string(String.t()) :: doc_string
def string(string) when is_binary(string) do
doc_string(string, String.length(string))
@@ -507,7 +507,7 @@ defmodule Inspect.Algebra do
@doc ~S"""
Colors a document if the `color_key` has a color in the options.
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec color(t, Inspect.Opts.color_key(), Inspect.Opts.t()) :: doc_color
def color(doc, color_key, %Inspect.Opts{syntax_colors: syntax_colors}) when is_doc(doc) do
if precolor = Keyword.get(syntax_colors, color_key) do
@@ -592,7 +592,7 @@ defmodule Inspect.Algebra do
Collapse any new lines and whitespace following this
node, emitting up to `max` new lines.
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec collapse_lines(pos_integer) :: doc_collapse
def collapse_lines(max) when is_integer(max) and max > 0 do
doc_collapse(max)
@@ -639,7 +639,7 @@ defmodule Inspect.Algebra do
})
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec next_break_fits(t) :: doc_fits
def next_break_fits(doc, mode \\ @next_break_fits)
when is_doc(doc) and mode in [:enabled, :disabled] do
@@ -649,7 +649,7 @@ defmodule Inspect.Algebra do
@doc """
Forces the current group to be unfit.
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec force_unfit(t) :: doc_force
def force_unfit(doc) when is_doc(doc) do
doc_force(doc)
@@ -683,7 +683,7 @@ defmodule Inspect.Algebra do
This function is used by `container_doc/4` and friends to the
maximum number of entries on the same line.
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec flex_break(binary) :: doc_break
def flex_break(string \\ " ") when is_binary(string) do
doc_break(string, :flex)
@@ -696,7 +696,7 @@ defmodule Inspect.Algebra do
This function is used by `container_doc/6` and friends
to the maximum number of entries on the same line.
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec flex_glue(t, binary, t) :: t
def flex_glue(doc1, break_string \\ " ", doc2) when is_binary(break_string) do
concat(doc1, concat(flex_break(break_string), doc2))
@@ -793,7 +793,7 @@ defmodule Inspect.Algebra do
["Hughes", "\n", "Wadler"]
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec line() :: t
def line(), do: :doc_line
diff --git a/lib/elixir/lib/integer.ex b/lib/elixir/lib/integer.ex
index d41d0f5a9..08ac04acf 100644
--- a/lib/elixir/lib/integer.ex
+++ b/lib/elixir/lib/integer.ex
@@ -81,7 +81,7 @@ defmodule Integer do
-2
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec mod(integer, neg_integer | pos_integer) :: integer
def mod(dividend, divisor) do
remainder = rem(dividend, divisor)
@@ -115,7 +115,7 @@ defmodule Integer do
-50
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec floor_div(integer, neg_integer | pos_integer) :: integer
def floor_div(dividend, divisor) do
if dividend * divisor < 0 and rem(dividend, divisor) != 0 do
@@ -398,7 +398,7 @@ defmodule Integer do
0
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec gcd(0, 0) :: 0
@spec gcd(integer, integer) :: pos_integer
def gcd(integer1, integer2) when is_integer(integer1) and is_integer(integer2) do
diff --git a/lib/elixir/lib/kernel.ex b/lib/elixir/lib/kernel.ex
index 8f83fb95f..7aa274d34 100644
--- a/lib/elixir/lib/kernel.ex
+++ b/lib/elixir/lib/kernel.ex
@@ -4630,7 +4630,7 @@ defmodule Kernel do
end
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec defguard(Macro.t()) :: Macro.t()
defmacro defguard(guard) do
define_guard(:defmacro, guard, __CALLER__)
@@ -4646,7 +4646,7 @@ defmodule Kernel do
Similar to `defmacrop/2`, `defguardp/1` must be defined before its use
in the current module.
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec defguardp(Macro.t()) :: Macro.t()
defmacro defguardp(guard) do
define_guard(:defmacrop, guard, __CALLER__)
diff --git a/lib/elixir/lib/kernel/parallel_compiler.ex b/lib/elixir/lib/kernel/parallel_compiler.ex
index e77ae787e..67fc4882d 100644
--- a/lib/elixir/lib/kernel/parallel_compiler.ex
+++ b/lib/elixir/lib/kernel/parallel_compiler.ex
@@ -14,7 +14,7 @@ defmodule Kernel.ParallelCompiler do
See `Task.async/1` for more information. The task spawned must be
always awaited on by calling `Task.await/1`
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
def async(fun) when is_function(fun) do
if parent = :erlang.get(:elixir_compiler_pid) do
file = :erlang.get(:elixir_compiler_file)
@@ -72,12 +72,12 @@ defmodule Kernel.ParallelCompiler do
`dest`, use `compile_to_path/3` instead.
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
def compile(files, options \\ []) when is_list(options) do
spawn_workers(files, :compile, options)
end
- @since "1.6.0"
+ @doc since: "1.6.0"
def compile_to_path(files, path, options \\ []) when is_binary(path) and is_list(options) do
spawn_workers(files, {:compile, path}, options)
end
@@ -102,7 +102,7 @@ defmodule Kernel.ParallelCompiler do
the file, module and the module bytecode
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
def require(files, options \\ []) when is_list(options) do
spawn_workers(files, :require, options)
end
diff --git a/lib/elixir/lib/kernel/typespec.ex b/lib/elixir/lib/kernel/typespec.ex
index 33d8c5d92..679fb9273 100644
--- a/lib/elixir/lib/kernel/typespec.ex
+++ b/lib/elixir/lib/kernel/typespec.ex
@@ -110,7 +110,7 @@ defmodule Kernel.Typespec do
case spec_to_signature(expr) do
{name, arity} ->
{line, doc} = get_doc_info(set, :doc, line)
- store_doc(set, kind, name, arity, line, doc, %{})
+ store_doc(set, kind, name, arity, line, :doc, doc, %{})
:error ->
:error
@@ -136,8 +136,8 @@ defmodule Kernel.Typespec do
{name, arity} ->
{line, doc} = get_doc_info(set, :typedoc, line)
- doc_meta = if kind == :opaque, do: %{opaque: true}, else: %{}
- store_doc(set, :type, name, arity, line, doc, doc_meta)
+ spec_meta = if kind == :opaque, do: %{opaque: true}, else: %{}
+ store_doc(set, :type, name, arity, line, :typedoc, doc, spec_meta)
:error ->
:error
@@ -157,8 +157,8 @@ defmodule Kernel.Typespec do
:ok
end
- defp store_doc(set, kind, name, arity, line, doc, doc_meta) do
- doc_meta = get_doc_meta(doc_meta, set)
+ defp store_doc(set, kind, name, arity, line, doc_kind, doc, spec_meta) do
+ doc_meta = get_doc_meta(spec_meta, doc_kind, set)
:ets.insert(set, {{kind, name, arity}, line, doc, doc_meta})
end
@@ -169,7 +169,13 @@ defmodule Kernel.Typespec do
end
end
- defp get_doc_meta(doc_meta, set) do
+ defp get_doc_meta(spec_meta, doc_kind, set) do
+ doc_meta =
+ case :ets.take(set, {doc_kind, :meta}) do
+ [{{^doc_kind, :meta}, metadata, _}] -> Map.merge(metadata, spec_meta)
+ [] -> spec_meta
+ end
+
doc_meta
|> get_since_info(set)
|> get_deprecated_info(set)
@@ -177,15 +183,18 @@ defmodule Kernel.Typespec do
defp get_since_info(doc_meta, set) do
case :ets.take(set, :since) do
- [{:since, since, _}] when is_binary(since) -> Map.put(doc_meta, :since, since)
+ [{:since, since, _}] when is_binary(since) -> Map.put_new(doc_meta, :since, since)
_ -> doc_meta
end
end
defp get_deprecated_info(doc_meta, set) do
case :ets.take(set, :deprecated) do
- [{:deprecated, reason, _}] when is_binary(reason) -> Map.put(doc_meta, :deprecated, reason)
- _ -> doc_meta
+ [{:deprecated, reason, _}] when is_binary(reason) ->
+ Map.put_new(doc_meta, :deprecated, reason)
+
+ _ ->
+ doc_meta
end
end
diff --git a/lib/elixir/lib/keyword.ex b/lib/elixir/lib/keyword.ex
index 49784767f..41a5c0f6b 100644
--- a/lib/elixir/lib/keyword.ex
+++ b/lib/elixir/lib/keyword.ex
@@ -648,7 +648,7 @@ defmodule Keyword do
** (KeyError) key :b not found in: [a: 1]
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec replace!(t, key, value) :: t
def replace!(keywords, key, value) when is_list(keywords) and is_atom(key) do
case :lists.keyfind(key, 1, keywords) do
diff --git a/lib/elixir/lib/list.ex b/lib/elixir/lib/list.ex
index 50d1bf94e..3d3666561 100644
--- a/lib/elixir/lib/list.ex
+++ b/lib/elixir/lib/list.ex
@@ -2,7 +2,7 @@ defmodule List do
@moduledoc """
Functions that work on (linked) lists.
- Many of the functions provided for lists, which implement
+ Many of the functions provided for lists, which implement
the `Enumerable` protocol, are found in the `Enum` module.
Additionally, the following functions and operators for lists are
@@ -62,10 +62,10 @@ defmodule List do
[0, 1, 2, 3]
iex> list ++ [4] # slow
[1, 2, 3, 4]
-
- Additonally, getting a list's length and accessing it by index are
- linear time operations. Negative indexes are also supported but
- they imply the list will be iterated twice, once to calculate the
+
+ Additonally, getting a list's length and accessing it by index are
+ linear time operations. Negative indexes are also supported but
+ they imply the list will be iterated twice, once to calculate the
proper index and another time to perform the operation.
## Charlists
@@ -468,7 +468,7 @@ defmodule List do
false
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
def ascii_printable?(list, counter \\ :infinity)
def ascii_printable?(_, 0) do
@@ -650,7 +650,7 @@ defmodule List do
{3, [1, 2]}
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec pop_at(list, integer, any) :: {any, list}
def pop_at(list, index, default \\ nil) when is_integer(index) do
if index < 0 do
@@ -680,7 +680,7 @@ defmodule List do
false
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec starts_with?(list, list) :: boolean
@spec starts_with?(list, []) :: true
@spec starts_with?([], nonempty_list) :: false
@@ -867,7 +867,7 @@ defmodule List do
[eq: [1], del: [4], eq: [2, 3], ins: [4]]
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec myers_difference(list, list) :: [{:eq | :ins | :del, list}]
def myers_difference(list1, list2) when is_list(list1) and is_list(list2) do
path = {0, list1, list2, []}
diff --git a/lib/elixir/lib/macro.ex b/lib/elixir/lib/macro.ex
index 8e729e422..e7e2bdb5b 100644
--- a/lib/elixir/lib/macro.ex
+++ b/lib/elixir/lib/macro.ex
@@ -201,7 +201,7 @@ defmodule Macro do
[{:var1, [], __MODULE__}, {:var2, [], __MODULE__}]
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
def generate_arguments(0, _), do: []
def generate_arguments(amount, context)
@@ -1275,7 +1275,7 @@ defmodule Macro do
@doc """
Returns `true` if the given name and arity is a special form.
"""
- @since "1.7.0"
+ @doc since: "1.7.0"
@spec special_form?(name :: atom(), arity()) :: boolean()
def special_form?(name, arity) when is_atom(name) and is_integer(arity) do
:elixir_import.special_form(name, arity)
@@ -1284,7 +1284,7 @@ defmodule Macro do
@doc """
Returns `true` if the given name and arity is an operator.
"""
- @since "1.7.0"
+ @doc since: "1.7.0"
@spec operator?(name :: atom(), arity()) :: boolean()
def operator?(name, 2) when is_atom(name), do: Identifier.binary_op(name) != :error
def operator?(name, 1) when is_atom(name), do: Identifier.unary_op(name) != :error
@@ -1293,7 +1293,7 @@ defmodule Macro do
@doc """
Returns `true` if the given quoted expression is an AST literal.
"""
- @since "1.7.0"
+ @doc since: "1.7.0"
@spec quoted_literal?(literal) :: true
@spec quoted_literal?(expr) :: false
def quoted_literal?(term)
diff --git a/lib/elixir/lib/macro/env.ex b/lib/elixir/lib/macro/env.ex
index 9663130d6..748ccdc51 100644
--- a/lib/elixir/lib/macro/env.ex
+++ b/lib/elixir/lib/macro/env.ex
@@ -130,8 +130,8 @@ defmodule Macro.Env do
and the second element is its context, which may be an
atom or an integer.
"""
+ @doc since: "1.7.0"
@spec vars(t) :: [var]
- @since "1.7.0"
def vars(env)
def vars(%{__struct__: Macro.Env, current_vars: current_vars}) do
@@ -141,8 +141,8 @@ defmodule Macro.Env do
@doc """
Checks if a variable belongs to the environment.
"""
+ @doc since: "1.7.0"
@spec has_var?(t, var) :: boolean()
- @since "1.7.0"
def has_var?(env, var)
def has_var?(%{__struct__: Macro.Env, current_vars: current_vars}, var) do
diff --git a/lib/elixir/lib/map.ex b/lib/elixir/lib/map.ex
index fd8896f7f..f9069267d 100644
--- a/lib/elixir/lib/map.ex
+++ b/lib/elixir/lib/map.ex
@@ -325,7 +325,7 @@ defmodule Map do
** (KeyError) key :b not found in: %{a: 1}
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec replace!(map, key, value) :: map
def replace!(map, key, value) do
:maps.update(key, value, map)
diff --git a/lib/elixir/lib/module.ex b/lib/elixir/lib/module.ex
index 490b35d4c..81e744e0b 100644
--- a/lib/elixir/lib/module.ex
+++ b/lib/elixir/lib/module.ex
@@ -125,6 +125,12 @@ defmodule Module do
The Mix compiler automatically looks for calls to deprecated modules
and emit warnings during compilation, computed via `mix xref warnings`.
+ The `@deprecated` attribute may also be used to annotate callbacks or
+ types. In these cases the annotation is only informational and doesn't
+ come with compile time checks. In any case, the annotation will also
+ become part of the documentation metadata to be used by tools like
+ ExDoc or IEx.
+
We recommend using this feature with care, especially library authors.
Deprecating code always pushes the burden towards library users. We
also recommend for deprecated functionality to be maintained for long
@@ -132,18 +138,23 @@ defmodule Module do
time to update (except for cases where keeping the deprecated API is
undesired, such as in the presence of security issues).
- ### `@doc` (and `@since`)
+ ### `@doc` and `@typedoc`
- Provides documentation for the function or macro that follows the
- attribute.
+ Provides documentation for the entity that follows the attribute.
+ `@doc` is to be used with a function, macro, callback, or
+ macrocallback, while `@typedoc` with a type (public or opaque).
Accepts a string (often a heredoc) or `false` where `@doc false` will
- make the function/macro invisible to documentation extraction tools
- like ExDoc. For example:
+ make the entity invisible to documentation extraction tools like
+ ExDoc. For example:
defmodule MyModule do
+ @typedoc "This type"
+ @typedoc since: "1.1.0"
+ @type t :: term
+
@doc "Hello world"
- @since "1.1.0"
+ @doc since: "1.1.0"
def hello do
"world"
end
@@ -156,8 +167,21 @@ defmodule Module do
end
end
- `@since` is an optional attribute that annotates which version the
- function was introduced.
+ As can be seen in the example above, `@doc` and `@typedoc` also accept
+ a keyword list that serves as a way to provide arbitrary metadata
+ about the entity. Tools like ExDoc and IEx may use this information to
+ display annotations. A common use case is `since` that may be used
+ to annotate in which version the function was introduced.
+
+ As illustrated in the example, it is possible to use these attributes
+ more than once before an entity. However, the compiler will warn if
+ used twice with binaries as that replaces the documentation text from
+ the preceding use. Multiple uses with keyword lists will merge the
+ lists into one.
+
+ Note that since the compiler also defines some additional metadata,
+ there are a few reserved keys that will be ignored and warned if used.
+ Currently these are: `:opaque`, `:defaults`, and `:deprecated`.
### `@dialyzer`
@@ -212,11 +236,16 @@ defmodule Module do
@moduledoc """
A very useful module.
"""
+ @moduledoc authors: ["Alice", "Bob"]
end
- Accepts a string (often a heredoc) or `false` where
- `@moduledoc false` will make the module invisible to
- documentation extraction tools like ExDoc.
+ Accepts a string (often a heredoc) or `false` where `@moduledoc false`
+ will make the module invisible to documentation extraction tools like
+ ExDoc.
+
+ Similarly to `@doc` also accepts a keyword list to provide metadata
+ about the module. For more details, see the documentation of `@doc`
+ above.
### `@on_definition`
@@ -886,7 +915,7 @@ defmodule Module do
This function is only available for modules being compiled.
"""
- @since "1.7.0"
+ @doc since: "1.7.0"
@spec defines_type?(module, definition) :: boolean
def defines_type?(module, definition) do
Kernel.Typespec.defines_type?(module, definition)
@@ -897,7 +926,7 @@ defmodule Module do
Returns `true` if there is such a spec and it was converted to a callback.
"""
- @since "1.7.0"
+ @doc since: "1.7.0"
@spec spec_to_callback(module, definition) :: boolean
def spec_to_callback(module, definition) do
Kernel.Typespec.spec_to_callback(module, definition)
@@ -1309,9 +1338,17 @@ defmodule Module do
defp compile_doc_meta(set, bag, name, arity, defaults) do
doc_meta = compile_since(%{}, set)
doc_meta = compile_deprecated(doc_meta, set, bag, name, arity, defaults)
+ doc_meta = get_doc_meta(doc_meta, set)
add_defaults_count(doc_meta, defaults)
end
+ defp get_doc_meta(existing_meta, set) do
+ case :ets.take(set, {:doc, :meta}) do
+ [{{:doc, :meta}, metadata, _}] -> Map.merge(existing_meta, metadata)
+ [] -> existing_meta
+ end
+ end
+
defp compile_since(doc_meta, table) do
case :ets.take(table, :since) do
[{:since, since, _}] when is_binary(since) -> Map.put(doc_meta, :since, since)
@@ -1706,6 +1743,23 @@ defmodule Module do
:ok
end
+ # If any of the doc attributes are called with a keyword list that
+ # will become documentation metadata. Multiple calls will be merged
+ # into the same map overriding duplicate keys.
+ defp put_attribute(module, key, {_, metadata}, line, set, _bag)
+ when key in [:doc, :typedoc, :moduledoc] and is_list(metadata) do
+ metadata_map = preprocess_doc_meta(metadata, module, line, %{})
+
+ case :ets.insert_new(set, {{key, :meta}, metadata_map, line}) do
+ true ->
+ :ok
+
+ false ->
+ current_metadata = :ets.lookup_element(set, {key, :meta}, 2)
+ :ets.update_element(set, {key, :meta}, {2, Map.merge(current_metadata, metadata_map)})
+ end
+ end
+
# This is the same list of attributes as in :elixir_module.
# We do not insert into the :attributes key in the bag table
# because those attributes are deleted on every definition.
@@ -1752,15 +1806,18 @@ defmodule Module do
{line, doc} when is_integer(line) and (is_binary(doc) or is_boolean(doc) or is_nil(doc)) ->
value
+ {line, [{key, _} | _]} when is_integer(line) and is_atom(key) ->
+ value
+
{line, doc} when is_integer(line) ->
raise ArgumentError,
"@#{key} is a built-in module attribute for documentation. It should be " <>
- "a binary, a boolean, or nil, got: #{inspect(doc)}"
+ "a binary, boolean, keyword list, or nil, got: #{inspect(doc)}"
_other ->
raise ArgumentError,
"@#{key} is a built-in module attribute for documentation. When set dynamically, " <>
- "it should be {line, doc} (where \"doc\" is a string, boolean, or nil), " <>
+ "it should be {line, doc} (where \"doc\" is a string, boolean, keyword list, or nil), " <>
"got: #{inspect(value)}"
end
end
@@ -1854,6 +1911,29 @@ defmodule Module do
value
end
+ defp preprocess_doc_meta([], _module, _line, map), do: map
+
+ defp preprocess_doc_meta([{key, _} | tail], module, line, map)
+ when key in [:opaque, :defaults, :deprecated] do
+ message = "ignoring reserved documentation metadata key: :#{key}"
+ IO.warn(message, attribute_stack(module, line))
+ preprocess_doc_meta(tail, module, line, map)
+ end
+
+ defp preprocess_doc_meta([{key, value} | tail], module, line, map) when is_atom(key) do
+ validate_doc_meta(key, value)
+ preprocess_doc_meta(tail, module, line, Map.put(map, key, value))
+ end
+
+ defp validate_doc_meta(:since, value) when not is_binary(value) do
+ raise ArgumentError,
+ ":since is a built-in documentation metadata key. " <>
+ "It should be a string representing the version a function, macro, type or " <>
+ "callback was added, got: #{inspect(value)}"
+ end
+
+ defp validate_doc_meta(_, _), do: :ok
+
defp get_doc_info(table, env) do
case :ets.take(table, :doc) do
[{:doc, {_, _} = pair, _}] ->
diff --git a/lib/elixir/lib/port.ex b/lib/elixir/lib/port.ex
index b416a53aa..5c83ce11b 100644
--- a/lib/elixir/lib/port.ex
+++ b/lib/elixir/lib/port.ex
@@ -265,7 +265,7 @@ defmodule Port do
Inlined by the compiler.
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec monitor(port | {name :: atom, node :: atom} | name :: atom) :: reference
def monitor(port) do
:erlang.monitor(:port, port)
@@ -282,7 +282,7 @@ defmodule Port do
Inlined by the compiler.
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec demonitor(reference, options :: [:flush | :info]) :: boolean
defdelegate demonitor(monitor_ref, options \\ []), to: :erlang
diff --git a/lib/elixir/lib/regex.ex b/lib/elixir/lib/regex.ex
index 32c21cf83..1183f2ba8 100644
--- a/lib/elixir/lib/regex.ex
+++ b/lib/elixir/lib/regex.ex
@@ -179,7 +179,7 @@ defmodule Regex do
This checks the version stored in the regular expression
and recompiles the regex in case of version mismatch.
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec recompile(t) :: t
def recompile(%Regex{} = regex) do
version = version()
@@ -197,7 +197,7 @@ defmodule Regex do
@doc """
Recompiles the existing regular expression and raises `Regex.CompileError` in case of errors.
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec recompile!(t) :: t
def recompile!(regex) do
case recompile(regex) do
@@ -209,7 +209,7 @@ defmodule Regex do
@doc """
Returns the version of the underlying Regex engine.
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec version :: term()
# TODO: No longer check for function_exported? on OTP 20+.
def version do
diff --git a/lib/elixir/lib/registry.ex b/lib/elixir/lib/registry.ex
index 431189f0c..b014ac63d 100644
--- a/lib/elixir/lib/registry.ex
+++ b/lib/elixir/lib/registry.ex
@@ -183,7 +183,7 @@ defmodule Registry do
## Via callbacks
@doc false
- @since "1.4.0"
+ @doc since: "1.4.0"
def whereis_name({registry, key}) do
case key_info!(registry) do
{:unique, partitions, key_ets} ->
@@ -203,7 +203,7 @@ defmodule Registry do
end
@doc false
- @since "1.4.0"
+ @doc since: "1.4.0"
def register_name({registry, key}, pid) when pid == self() do
case register(registry, key, nil) do
{:ok, _} -> :yes
@@ -212,7 +212,7 @@ defmodule Registry do
end
@doc false
- @since "1.4.0"
+ @doc since: "1.4.0"
def send({registry, key}, msg) do
case lookup(registry, key) do
[{pid, _}] -> Kernel.send(pid, msg)
@@ -221,7 +221,7 @@ defmodule Registry do
end
@doc false
- @since "1.4.0"
+ @doc since: "1.4.0"
def unregister_name({registry, key}) do
unregister(registry, key)
end
@@ -272,6 +272,7 @@ defmodule Registry do
* `:meta` - a keyword list of metadata to be attached to the registry.
"""
+ @doc since: "1.5.0"
@spec start_link(
keys: keys,
name: registry,
@@ -280,7 +281,6 @@ defmodule Registry do
meta: meta
) :: {:ok, pid} | {:error, term}
when meta: [{meta_key, meta_value}]
- @since "1.5.0"
def start_link(options) do
keys = Keyword.get(options, :keys)
@@ -335,7 +335,7 @@ defmodule Registry do
See `Supervisor`.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
def child_spec(opts) do
%{
id: Keyword.get(opts, :name, Registry),
@@ -364,7 +364,7 @@ defmodule Registry do
[{self(), 2}]
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec update_value(registry, key, (value -> value)) ::
{new_value :: term, old_value :: term} | :error
def update_value(registry, key, callback) when is_atom(registry) and is_function(callback, 1) do
@@ -408,7 +408,7 @@ defmodule Registry do
See the module documentation for examples of using the `dispatch/3`
function for building custom dispatching or a pubsub system.
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec dispatch(registry, key, dispatcher, keyword) :: :ok
when dispatcher: (entries :: [{pid, value}] -> term) | {module(), atom(), [any()]}
def dispatch(registry, key, mfa_or_fun, opts \\ [])
@@ -523,7 +523,7 @@ defmodule Registry do
[{self(), :another}, {self(), :world}]
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec lookup(registry, key) :: [{pid, value}]
def lookup(registry, key) when is_atom(registry) do
case key_info!(registry) do
@@ -589,7 +589,7 @@ defmodule Registry do
[{self(), {1, :atom, 1}}, {self(), {2, :atom, 2}}]
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec match(registry, key, match_pattern, guards) :: [{pid, term}]
def match(registry, key, pattern, guards \\ []) when is_atom(registry) and is_list(guards) do
guards = [{:"=:=", {:element, 1, :"$_"}, {:const, key}} | guards]
@@ -642,7 +642,7 @@ defmodule Registry do
["hello", "hello"]
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec keys(registry, pid) :: [key]
def keys(registry, pid) when is_atom(registry) and is_pid(pid) do
{kind, partitions, _, pid_ets, _} = info!(registry)
@@ -715,7 +715,7 @@ defmodule Registry do
[]
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec unregister(registry, key) :: :ok
def unregister(registry, key) when is_atom(registry) do
self = self()
@@ -776,7 +776,7 @@ defmodule Registry do
[{self(), :world_b}, {self(), :world_c}]
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
def unregister_match(registry, key, pattern, guards \\ []) when is_list(guards) do
self = self()
@@ -869,7 +869,7 @@ defmodule Registry do
["hello", "hello"]
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec register(registry, key, value) :: {:ok, pid} | {:error, {:already_registered, pid}}
def register(registry, key, value) when is_atom(registry) do
self = self()
@@ -942,7 +942,7 @@ defmodule Registry do
:error
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec meta(registry, meta_key) :: {:ok, meta_value} | :error
def meta(registry, key) when is_atom(registry) and (is_atom(key) or is_tuple(key)) do
try do
@@ -974,7 +974,7 @@ defmodule Registry do
{:ok, "tuple_value"}
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec put_meta(registry, meta_key, meta_value) :: :ok
def put_meta(registry, key, value) when is_atom(registry) and (is_atom(key) or is_tuple(key)) do
try do
@@ -1013,7 +1013,7 @@ defmodule Registry do
2
"""
- @since "1.7.0"
+ @doc since: "1.7.0"
@spec count(registry) :: non_neg_integer()
def count(registry) when is_atom(registry) do
case key_info!(registry) do
@@ -1077,7 +1077,7 @@ defmodule Registry do
2
"""
- @since "1.7.0"
+ @doc since: "1.7.0"
@spec count_match(registry, key, match_pattern, guards) :: non_neg_integer()
def count_match(registry, key, pattern, guards \\ [])
when is_atom(registry) and is_list(guards) do
diff --git a/lib/elixir/lib/stream.ex b/lib/elixir/lib/stream.ex
index 9e8c405f8..899f427fc 100644
--- a/lib/elixir/lib/stream.ex
+++ b/lib/elixir/lib/stream.ex
@@ -145,7 +145,7 @@ defmodule Stream do
@doc """
Shortcut to `chunk_every(enum, count, count)`.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec chunk_every(Enumerable.t(), pos_integer) :: Enumerable.t()
def chunk_every(enum, count), do: chunk_every(enum, count, count, [])
@@ -179,7 +179,7 @@ defmodule Stream do
[[1, 2, 3], [4, 5, 6]]
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec chunk_every(Enumerable.t(), pos_integer, pos_integer, Enumerable.t() | :discard) ::
Enumerable.t()
def chunk_every(enum, count, step, leftover \\ [])
@@ -233,7 +233,7 @@ defmodule Stream do
[[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec chunk_while(
Enumerable.t(),
acc,
@@ -585,7 +585,7 @@ defmodule Stream do
[1, 2, 3, 4, 5]
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec map_every(Enumerable.t(), non_neg_integer, (element -> any)) :: Enumerable.t()
def map_every(enum, nth, fun)
@@ -1112,7 +1112,7 @@ defmodule Stream do
[{1, :a, "foo"}, {2, :b, "bar"}, {3, :c, "baz"}]
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec zip([Enumerable.t()]) :: Enumerable.t()
@spec zip(Enumerable.t()) :: Enumerable.t()
def zip(enumerables) do
@@ -1500,7 +1500,7 @@ defmodule Stream do
[]
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec intersperse(Enumerable.t(), any) :: Enumerable.t()
def intersperse(enumerable, intersperse_element) do
Stream.transform(enumerable, false, fn
diff --git a/lib/elixir/lib/supervisor.ex b/lib/elixir/lib/supervisor.ex
index aadab8a6f..0775c867a 100644
--- a/lib/elixir/lib/supervisor.ex
+++ b/lib/elixir/lib/supervisor.ex
@@ -611,8 +611,8 @@ defmodule Supervisor do
is allowed within 5 seconds. Check the `Supervisor` module for a detailed
description of the available strategies.
"""
+ @doc since: "1.5.0"
# TODO: Warn if simple_one_for_one strategy is used on Elixir v1.8.
- @since "1.5.0"
@spec init([:supervisor.child_spec() | {module, term} | module], [init_option]) :: {:ok, tuple}
def init(children, options) when is_list(children) and is_list(options) do
unless strategy = options[:strategy] do
diff --git a/lib/elixir/lib/system.ex b/lib/elixir/lib/system.ex
index 02757ef6b..80ac75ac1 100644
--- a/lib/elixir/lib/system.ex
+++ b/lib/elixir/lib/system.ex
@@ -523,7 +523,7 @@ defmodule System do
System.stop(1)
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec stop(non_neg_integer | binary) :: no_return
def stop(status \\ 0)
diff --git a/lib/elixir/lib/task.ex b/lib/elixir/lib/task.ex
index c6eef9ad6..33137f98f 100644
--- a/lib/elixir/lib/task.ex
+++ b/lib/elixir/lib/task.ex
@@ -190,7 +190,7 @@ defmodule Task do
See `Supervisor`.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
def child_spec(arg) do
%{
id: Task,
@@ -207,7 +207,7 @@ defmodule Task do
See `Supervisor`.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
def child_spec(arg) do
default = %{
id: __MODULE__,
@@ -409,7 +409,7 @@ defmodule Task do
Enum.to_list(stream)
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec async_stream(Enumerable.t(), module, atom, [term], keyword) :: Enumerable.t()
def async_stream(enumerable, module, function, args, options \\ [])
when is_atom(module) and is_atom(function) and is_list(args) do
@@ -435,7 +435,7 @@ defmodule Task do
See `async_stream/5` for discussion, options, and more examples.
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec async_stream(Enumerable.t(), (term -> term), keyword) :: Enumerable.t()
def async_stream(enumerable, fun, options \\ []) when is_function(fun, 1) do
build_stream(enumerable, fun, options)
diff --git a/lib/elixir/lib/task/supervisor.ex b/lib/elixir/lib/task/supervisor.ex
index 31fabf8d5..bc4485907 100644
--- a/lib/elixir/lib/task/supervisor.ex
+++ b/lib/elixir/lib/task/supervisor.ex
@@ -226,7 +226,7 @@ defmodule Task.Supervisor do
Enum.to_list(stream)
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec async_stream(Supervisor.supervisor(), Enumerable.t(), module, atom, [term], keyword) ::
Enumerable.t()
def async_stream(supervisor, enumerable, module, function, args, options \\ [])
@@ -244,7 +244,7 @@ defmodule Task.Supervisor do
See `async_stream/6` for discussion, options, and examples.
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec async_stream(Supervisor.supervisor(), Enumerable.t(), (term -> term), keyword) ::
Enumerable.t()
def async_stream(supervisor, enumerable, fun, options \\ []) when is_function(fun, 1) do
@@ -261,7 +261,7 @@ defmodule Task.Supervisor do
See `async_stream/6` for discussion, options, and examples.
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec async_stream_nolink(
Supervisor.supervisor(),
Enumerable.t(),
@@ -285,7 +285,7 @@ defmodule Task.Supervisor do
See `async_stream/6` for discussion and examples.
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec async_stream_nolink(Supervisor.supervisor(), Enumerable.t(), (term -> term), keyword) ::
Enumerable.t()
def async_stream_nolink(supervisor, enumerable, fun, options \\ []) when is_function(fun, 1) do
diff --git a/lib/elixir/src/elixir_erl.erl b/lib/elixir/src/elixir_erl.erl
index ce45fb072..ac45bff87 100644
--- a/lib/elixir/src/elixir_erl.erl
+++ b/lib/elixir/src/elixir_erl.erl
@@ -516,6 +516,7 @@ docs_chunk(Set, Module, Line, Def, Defmacro, Types, Callbacks) ->
case elixir_compiler:get_opt(docs) of
true ->
{ModuleDocLine, ModuleDoc} = get_moduledoc(Line, Set),
+ ModuleDocMeta = get_moduledoc_meta(Set),
FunctionDocs = get_docs(Set, Module, Def, function),
MacroDocs = get_docs(Set, Module, Defmacro, macro),
CallbackDocs = get_callback_docs(Set, Callbacks),
@@ -526,7 +527,7 @@ docs_chunk(Set, Module, Line, Def, Defmacro, Types, Callbacks) ->
elixir,
<<"text/markdown">>,
ModuleDoc,
- #{},
+ ModuleDocMeta,
FunctionDocs ++ MacroDocs ++ CallbackDocs ++ TypeDocs
}, [compressed]),
@@ -553,6 +554,12 @@ get_moduledoc(Line, Set) ->
{DocLine, Doc} -> {DocLine, doc_value(Doc)}
end.
+get_moduledoc_meta(Set) ->
+ case ets:lookup(Set, {moduledoc, meta}) of
+ [] -> #{};
+ [{{moduledoc, meta}, Map, _}] when is_map(Map) -> Map
+ end.
+
get_docs(Set, Module, Definitions, Kind) ->
[{Key,
erl_anno:new(Line),
diff --git a/lib/elixir/test/elixir/kernel/docs_test.exs b/lib/elixir/test/elixir/kernel/docs_test.exs
index dfa7a6713..e9e5f52d1 100644
--- a/lib/elixir/test/elixir/kernel/docs_test.exs
+++ b/lib/elixir/test/elixir/kernel/docs_test.exs
@@ -54,10 +54,10 @@ defmodule Kernel.DocsTest do
assert Code.fetch_docs(InMemoryDocs) == {:error, :module_not_found}
end
- test "raises on invalid @since" do
+ test "raises on invalid @doc since: ..." do
assert_raise ArgumentError, ~r"should be a string representing the version", fn ->
defmodule InvalidSince do
- @since 1.2
+ @doc since: 1.2
def foo, do: :bar
end
end
@@ -70,7 +70,7 @@ defmodule Kernel.DocsTest do
end
end
- assert_raise ArgumentError, ~r/should be a binary, a boolean, or nil/, fn ->
+ assert_raise ArgumentError, ~r/should be a binary, boolean, keyword list, or nil/, fn ->
defmodule AtSyntaxDocAttributesFormat do
@moduledoc :not_a_binary
end
@@ -142,23 +142,26 @@ defmodule Kernel.DocsTest do
write_beam(
defmodule SampleDocs do
@moduledoc "Module doc"
+ @moduledoc authors: "Elixir Contributors", purpose: :test
@doc "My struct"
defstruct [:sample]
@typedoc "Type doc"
- @since "1.2.3"
+ @typedoc since: "1.2.3", color: :red
@type foo(any) :: any
@typedoc "Opaque type doc"
@opaque bar(any) :: any
@doc "Callback doc"
- @since "1.2.3"
+ @doc since: "1.2.3", color: :red
+ @doc color: :blue, stable: true
@deprecated "use baz/2 instead"
@callback foo(any) :: any
@doc false
+ @doc since: "1.2.3"
@callback bar() :: term
@callback baz(any, term) :: any
@@ -166,7 +169,9 @@ defmodule Kernel.DocsTest do
@macrocallback qux(any) :: any
@doc "Function doc"
- @since "1.2.3"
+ @doc since: "1.2.3", color: :red
+ @since "1.2-doc-meta-takes-precedence"
+ @doc color: :blue, stable: true
@deprecated "use baz/2 instead"
def foo(arg \\ 0), do: arg + 1
@@ -177,11 +182,11 @@ defmodule Kernel.DocsTest do
def bar(arg), do: arg + 1
@doc "Wrong doc"
- @since "1.2"
+ @doc since: "1.2"
def baz(_arg)
def baz(arg), do: arg + 1
@doc "Multiple bodiless clause and docs"
- @since "1.2.3"
+ @doc since: "1.2.3"
def baz(_arg)
@doc false
@@ -194,11 +199,13 @@ defmodule Kernel.DocsTest do
end
)
- assert {:docs_v1, _, :elixir, "text/markdown", %{"en" => module_doc}, %{}, docs} =
+ assert {:docs_v1, _, :elixir, "text/markdown", %{"en" => module_doc}, module_doc_meta, docs} =
Code.fetch_docs(SampleDocs)
assert module_doc == "Module doc"
+ assert %{authors: "Elixir Contributors", purpose: :test} = module_doc_meta
+
[
callback_bar,
callback_baz,
@@ -219,7 +226,8 @@ defmodule Kernel.DocsTest do
assert {{:callback, :baz, 2}, _, [], :none, %{}} = callback_baz
assert {{:callback, :foo, 1}, _, [], %{"en" => "Callback doc"},
- %{since: "1.2.3", deprecated: "use baz/2 instead"}} = callback_foo
+ %{since: "1.2.3", deprecated: "use baz/2 instead", color: :blue, stable: true}} =
+ callback_foo
assert {{:function, :__struct__, 0}, _, ["%Kernel.DocsTest.SampleDocs{}"],
%{"en" => "My struct"}, %{}} = function_struct_0
@@ -233,7 +241,13 @@ defmodule Kernel.DocsTest do
%{"en" => "Multiple bodiless clause and docs"}, %{since: "1.2.3"}} = function_baz
assert {{:function, :foo, 1}, _, ["foo(arg \\\\ 0)"], %{"en" => "Function doc"},
- %{since: "1.2.3", deprecated: "use baz/2 instead", defaults: 1}} = function_foo
+ %{
+ since: "1.2.3",
+ deprecated: "use baz/2 instead",
+ color: :blue,
+ stable: true,
+ defaults: 1
+ }} = function_foo
assert {{:function, :nullary, 0}, _, ["nullary()"], %{"en" => "add_doc"}, %{}} =
function_nullary
@@ -244,7 +258,9 @@ defmodule Kernel.DocsTest do
macrocallback_qux
assert {{:type, :bar, 1}, _, [], %{"en" => "Opaque type doc"}, %{opaque: true}} = type_bar
- assert {{:type, :foo, 1}, _, [], %{"en" => "Type doc"}, %{since: "1.2.3"}} = type_foo
+
+ assert {{:type, :foo, 1}, _, [], %{"en" => "Type doc"}, %{since: "1.2.3", color: :red}} =
+ type_foo
end
end
diff --git a/lib/elixir/test/elixir/kernel/warning_test.exs b/lib/elixir/test/elixir/kernel/warning_test.exs
index 0f064d183..65bca1b61 100644
--- a/lib/elixir/test/elixir/kernel/warning_test.exs
+++ b/lib/elixir/test/elixir/kernel/warning_test.exs
@@ -1137,6 +1137,28 @@ defmodule Kernel.WarningTest do
purge(Sample)
end
+ test "reserved doc metadata keys" do
+ output =
+ capture_err(fn ->
+ Code.eval_string("""
+ defmodule Sample do
+ @typedoc opaque: false, deprecated: "do not use"
+ @type t :: binary
+
+ @doc defaults: 3, since: "1.2.3"
+ def foo(a), do: a
+ end
+ """)
+ end)
+
+ assert output =~ "ignoring reserved documentation metadata key: :opaque"
+ assert output =~ "ignoring reserved documentation metadata key: :deprecated"
+ assert output =~ "ignoring reserved documentation metadata key: :defaults"
+ refute output =~ ":since"
+ after
+ purge(Sample)
+ end
+
describe "typespecs" do
test "typedoc on typep" do
assert capture_err(fn ->
diff --git a/lib/ex_unit/lib/ex_unit/callbacks.ex b/lib/ex_unit/lib/ex_unit/callbacks.ex
index 3adb843d2..cd52283b3 100644
--- a/lib/ex_unit/lib/ex_unit/callbacks.ex
+++ b/lib/ex_unit/lib/ex_unit/callbacks.ex
@@ -323,7 +323,7 @@ defmodule ExUnit.Callbacks do
This function returns `{:ok, pid}` in case of success, otherwise it
returns `{:error, reason}`.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec start_supervised(Supervisor.child_spec() | module | {module, term}, keyword) ::
Supervisor.on_start_child()
def start_supervised(child_spec_or_module, opts \\ []) do
@@ -348,7 +348,7 @@ defmodule ExUnit.Callbacks do
Same as `start_supervised/2` but returns the PID on success and raises if
not started properly.
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
@spec start_supervised!(Supervisor.child_spec() | module | {module, term}, keyword) :: pid
def start_supervised!(child_spec_or_module, opts \\ []) do
case start_supervised(child_spec_or_module, opts) do
@@ -380,7 +380,7 @@ defmodule ExUnit.Callbacks do
It returns `:ok` if there is a supervised process with such
`id`, `{:error, :not_found}` otherwise.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
@spec stop_supervised(id :: term()) :: :ok | {:error, :not_found}
def stop_supervised(id) do
case ExUnit.OnExitHandler.get_supervisor(self()) do
diff --git a/lib/iex/lib/iex.ex b/lib/iex/lib/iex.ex
index 9e33acde1..84bcc20f0 100644
--- a/lib/iex/lib/iex.ex
+++ b/lib/iex/lib/iex.ex
@@ -552,7 +552,7 @@ defmodule IEx do
@doc """
Macro-based shortcut for `IEx.break!/4`.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
defmacro break!(ast, stops \\ 1) do
quote do
IEx.__break__!(unquote(Macro.escape(ast)), unquote(Macro.escape(stops)), __ENV__)
@@ -731,7 +731,7 @@ defmodule IEx do
iex -S mix test path/to/file:line --trace
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
def break!(module, function, arity, stops \\ 1) when is_integer(arity) do
IEx.Pry.break!(module, function, arity, quote(do: _), stops)
end
diff --git a/lib/iex/lib/iex/helpers.ex b/lib/iex/lib/iex/helpers.ex
index d786d0ca0..637496411 100644
--- a/lib/iex/lib/iex/helpers.ex
+++ b/lib/iex/lib/iex/helpers.ex
@@ -493,7 +493,7 @@ defmodule IEx.Helpers do
Prints vm/runtime information such as versions, memory usage and statistics.
Additional topics are available via `runtime_info/1`.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
def runtime_info(), do: runtime_info([:system, :memory, :limits])
@doc """
@@ -726,7 +726,7 @@ defmodule IEx.Helpers do
@doc """
Prints a list of all the functions and macros exported by the given module.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
def exports(module \\ Kernel) do
exports = IEx.Autocomplete.exports(module)
@@ -847,7 +847,7 @@ defmodule IEx.Helpers do
control of the shell. If you would rather start a new shell,
use `respawn/0` instead.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
def continue do
if whereis = IEx.Server.whereis() do
send(whereis, {:continue, self()})
@@ -859,7 +859,7 @@ defmodule IEx.Helpers do
@doc """
Macro-based shortcut for `IEx.break!/4`.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
defmacro break!(ast, stops \\ 1) do
quote do
require IEx
@@ -874,13 +874,13 @@ defmodule IEx.Helpers do
See `IEx.break!/4` for a complete description of breakpoints
in IEx.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
defdelegate break!(module, function, arity, stops \\ 1), to: IEx
@doc """
Prints all breakpoints to the terminal.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
def breaks do
breaks(IEx.Pry.breaks())
end
@@ -952,7 +952,7 @@ defmodule IEx.Helpers do
like to effectively remove all breakpoints and instrumentation
code from a module, use `remove_breaks/1` instead.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
defdelegate reset_break(id), to: IEx.Pry
@doc """
@@ -967,19 +967,19 @@ defmodule IEx.Helpers do
like to effectively remove all breakpoints and instrumentation
code from a module, use `remove_breaks/1` instead.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
defdelegate reset_break(module, function, arity), to: IEx.Pry
@doc """
Removes all breakpoints and instrumentation from `module`.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
defdelegate remove_breaks(module), to: IEx.Pry
@doc """
Removes all breakpoints and instrumentation from all modules.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
defdelegate remove_breaks(), to: IEx.Pry
@doc """
@@ -1006,7 +1006,7 @@ defmodule IEx.Helpers do
Keep in mind the `whereami/1` location may not exist when prying
precompiled source code, such as Elixir itself.
"""
- @since "1.5.0"
+ @doc since: "1.5.0"
def whereami(radius \\ 2) do
case Process.get(:iex_whereami) do
{file, line, stacktrace} ->
@@ -1085,7 +1085,7 @@ defmodule IEx.Helpers do
13
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
defmacro import_file(path) when is_binary(path) do
import_file_if_available(path, false)
end
@@ -1134,7 +1134,7 @@ defmodule IEx.Helpers do
use_if_available Phoenix.HTML
"""
- @since "1.7.0"
+ @doc since: "1.7.0"
defmacro use_if_available(quoted_module, opts \\ []) do
module = Macro.expand(quoted_module, __CALLER__)
@@ -1208,12 +1208,12 @@ defmodule IEx.Helpers do
#Reference<0.21.32.43>
"""
- @since "1.6.0"
+ @doc since: "1.6.0"
def ref(string) when is_binary(string) do
:erlang.list_to_ref('#Ref<#{string}>')
end
- @since "1.6.0"
+ @doc since: "1.6.0"
def ref(w, x, y, z)
when is_integer(w) and w >= 0 and is_integer(x) and x >= 0 and is_integer(y) and y >= 0 and
is_integer(z) and z >= 0 do
diff --git a/lib/mix/lib/mix/project.ex b/lib/mix/lib/mix/project.ex
index 0603dc6b4..f6e784fbb 100644
--- a/lib/mix/lib/mix/project.ex
+++ b/lib/mix/lib/mix/project.ex
@@ -217,7 +217,7 @@ defmodule Mix.Project do
a full recompilation whenever such configuration files change.
For this reason, the mtime is cached to avoid file system lookups.
"""
- @since "1.7.0"
+ @doc since: "1.7.0"
@spec config_mtime() :: posix_mtime when posix_mtime: integer()
def config_mtime do
Mix.Dep.Lock.manifest()
@@ -254,7 +254,7 @@ defmodule Mix.Project do
#=> %{my_app1: "apps/my_app1", my_app2: "apps/my_app2"}
"""
- @since "1.4.0"
+ @doc since: "1.4.0"
@spec apps_paths() :: %{optional(atom) => Path.t()} | nil
def apps_paths(config \\ config()) do
if apps_path = config[:apps_path] do
@@ -396,7 +396,7 @@ defmodule Mix.Project do
Useful when dependencies need to be reloaded due to change of global state.
"""
- @since "1.7.0"
+ @doc since: "1.7.0"
@spec clear_deps_cache() :: :ok
def clear_deps_cache() do
Mix.Dep.clear_cached()