diff options
author | László Bácsi <lackac@lackac.hu> | 2018-07-11 10:19:38 +0200 |
---|---|---|
committer | José Valim <jose.valim@gmail.com> | 2018-07-11 10:19:38 +0200 |
commit | 0267ae2c7eff6064876c780d7a9083a78d26700d (patch) | |
tree | df4931d2bf2de1947959cbde7560cd49822a38f7 | |
parent | ec0435b84f19eff70cbf8ea29ae0c167212f6e2e (diff) | |
download | elixir-0267ae2c7eff6064876c780d7a9083a78d26700d.tar.gz |
Support adding arbitrary docs metadata with `@doc keyword()` (#7852)
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() |