Skip to content

Commit

Permalink
Improve specs for strong dialyzer compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
kipcole9 committed Aug 16, 2024
1 parent 4b1aa9a commit 0098717
Show file tree
Hide file tree
Showing 12 changed files with 67 additions and 38 deletions.
16 changes: 9 additions & 7 deletions lib/cldr/calendar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,7 @@ defmodule Cldr.Calendar do
"""
@spec new(module(), calendar_type(), Keyword.t()) ::
{:ok, calendar()} | {:module_already_exists, module()}
{:ok, calendar()} | {:module_already_exists, module()} | {:error, String.t()}

def new(calendar_module, calendar_type, config)
when is_atom(calendar_module) and calendar_type in [:week, :month] do
Expand Down Expand Up @@ -860,7 +860,8 @@ defmodule Cldr.Calendar do
%Date{calendar: Cldr.Calendar.NRF, day: 1, month: 1, year: 2019}
"""
@spec first_day_of_year(year :: year(), calendar :: calendar()) :: Date.t()
@spec first_day_of_year(year :: year(), calendar :: calendar()) ::
Date.t() | {:error, :invalid_date}

def first_day_of_year(year, calendar) do
with {:ok, date} <- Date.new(year, 1, 1, calendar) do
Expand Down Expand Up @@ -888,7 +889,7 @@ defmodule Cldr.Calendar do
~D[2019-01-01]
"""
@spec first_day_of_year(date :: date()) :: Date.t()
@spec first_day_of_year(date :: date()) :: Date.t() | {:error, :invalid_date}

def first_day_of_year(%Date{year: year, calendar: calendar}) do
first_day_of_year(year, calendar)
Expand Down Expand Up @@ -2053,7 +2054,7 @@ defmodule Cldr.Calendar do
"""
@spec weeks_to_days(integer) :: integer
def weeks_to_days(n) do
n * @days_in_a_week
trunc(n * @days_in_a_week)
end

@doc """
Expand Down Expand Up @@ -2746,7 +2747,7 @@ defmodule Cldr.Calendar do
{:ok, backend}
end

@spec plus(integer, integer()) :: integer()
@spec plus(integer, integer()) :: integer() | {:error, :invalid_date}
@doc false

def plus(value, increment) when is_integer(value) and is_integer(increment) do
Expand Down Expand Up @@ -2870,7 +2871,7 @@ defmodule Cldr.Calendar do
"""

@spec plus(Calendar.date() | Date.Range.t(), atom(), integer(), Keyword.t()) ::
Calendar.date()
Calendar.date() | {:error, :invalid_date}

def plus(date, period, increment, options \\ [])

Expand Down Expand Up @@ -3429,7 +3430,8 @@ defmodule Cldr.Calendar do
~D[0000-01-01]
"""
@spec date_from_iso_days(Calendar.iso_days() | iso_day_number, calendar()) :: Date.t()
@spec date_from_iso_days(Calendar.iso_days() | iso_day_number, calendar()) ::
Date.t() | {:error, :incompatible_calendars | :invalid_date}

def date_from_iso_days({days, _}, calendar) do
date_from_iso_days(days, calendar)
Expand Down
10 changes: 8 additions & 2 deletions lib/cldr/calendar/backend/calendar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,14 @@ defmodule Cldr.Calendar.Backend do
"""
@doc since: "1.25.0"

@spec localize(datetime :: Cldr.Calendar.any_date_time(), part :: Cldr.Calendar.part(), options :: Keyword.t()) ::
String.t() | [Cldr.Calendar.day_of_week_to_binary()] | {:error, {module(), String.t()}}
@spec localize(
datetime :: Cldr.Calendar.any_date_time(),
part :: Cldr.Calendar.part(),
options :: Keyword.t()
) ::
String.t()
| [Cldr.Calendar.day_of_week_to_binary()]
| {:error, {module(), String.t()}}

def localize(datetime, part, options \\ []) do
options = Keyword.put(options, :backend, unquote(backend))
Expand Down
4 changes: 2 additions & 2 deletions lib/cldr/calendar/backend/month_compiler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ defmodule Cldr.Calendar.Compiler.Month do
"""
@spec days_in_month(year :: Cldr.Calendar.year(), month :: Cldr.Calendar.month()) ::
days :: Calendar.day() | {:error, {module(), String.t()}}
days :: Calendar.day() | {:error, {module(), String.t()}} | {:ambiguous, Range.t()}

@impl true
def days_in_month(year, month) do
Expand All @@ -418,7 +418,7 @@ defmodule Cldr.Calendar.Compiler.Month do
"""
@spec days_in_month(month :: Cldr.Calendar.month()) ::
days :: Calendar.day() | {:error, {module(), String.t()}}
days :: Calendar.day() | {:error, {module(), String.t()}} | {:ambiguous, Range.t()}

@impl true
def days_in_month(month) do
Expand Down
14 changes: 11 additions & 3 deletions lib/cldr/calendar/backend/week_compiler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ defmodule Cldr.Calendar.Compiler.Week do
a week in week-based calendars..
"""
@spec periods_in_year(year :: Cldr.Calendar.year()) :: Calendar.week()
@spec periods_in_year(year :: Cldr.Calendar.year()) :: Calendar.week() | :error
@impl true
def periods_in_year(year) do
{weeks_in_year, _} = weeks_in_year(year)
Expand Down Expand Up @@ -377,7 +377,11 @@ defmodule Cldr.Calendar.Compiler.Week do
"""
@spec days_in_month(year :: Cldr.Calendar.year(), month :: Cldr.Calendar.month()) ::
days :: Calendar.day() | {:error, {module(), String.t()}}
days ::
Calendar.day()
| {:error, {module(), String.t()}}
| {:ambiguous, Range.t()}
| {:ambiguous, [integer(), ...]}

@impl true
def days_in_month(year, month) do
Expand All @@ -392,7 +396,11 @@ defmodule Cldr.Calendar.Compiler.Week do
"""
@spec days_in_month(month :: Cldr.Calendar.month()) ::
days :: Calendar.day() | {:error, {module(), String.t()}}
days ::
Calendar.day()
| {:error, {module(), String.t()}}
| {:ambiguous, Range.t()}
| {:ambiguous, [integer(), ...]}

@impl true
def days_in_month(month) do
Expand Down
10 changes: 5 additions & 5 deletions lib/cldr/calendar/base/week.ex
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ defmodule Cldr.Calendar.Base.Week do
def day_of_year(year, week, day, config) when is_date(year, week, day) do
start_of_year = first_gregorian_day_of_year(year, config)
this_day = first_gregorian_day_of_year(year, config) + week_to_days(week) + day
this_day - start_of_year + 1
trunc(this_day - start_of_year + 1)
end

def day_of_year(year, week, day, _config) do
Expand Down Expand Up @@ -210,13 +210,13 @@ defmodule Cldr.Calendar.Base.Week do
def days_in_month(year, @months_in_year, config) do
%Config{weeks_in_month: [_, _, weeks_in_last_month]} = config
weeks = if long_year?(year, config), do: weeks_in_last_month + 1, else: weeks_in_last_month
weeks * days_in_week()
trunc(weeks * days_in_week())
end

def days_in_month(_year, month, config) do
%Config{weeks_in_month: weeks_in_month} = config
month_in_quarter = Math.amod(rem(month, @months_in_quarter), @months_in_quarter)
Enum.at(weeks_in_month, month_in_quarter - 1) * days_in_week()
(Enum.at(weeks_in_month, month_in_quarter - 1) * days_in_week()) |> trunc()
end

# If the month is the last month of the year then it will be different
Expand All @@ -225,8 +225,8 @@ defmodule Cldr.Calendar.Base.Week do

def days_in_month(month, %Config{weeks_in_month: [_, _, weeks_in_last_month]} = config) do
if month == @months_in_year do
long_year_days = (weeks_in_last_month + 1) * days_in_week()
short_year_days = weeks_in_last_month * days_in_week()
long_year_days = trunc((weeks_in_last_month + 1) * days_in_week())
short_year_days = trunc(weeks_in_last_month * days_in_week())
{:ambiguous, [short_year_days, long_year_days]}
else
days_in_month(@any_year, month, config)
Expand Down
6 changes: 3 additions & 3 deletions lib/cldr/calendar/calendars/julian.ex
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,8 @@ defmodule Cldr.Calendar.Julian do
@spec day_of_year(year, month, day) :: 1..366
@impl Calendar
def day_of_year(year, month, day) do
first_day = date_to_iso_days(year, 1, 1)
this_day = date_to_iso_days(year, month, day)
first_day = date_to_iso_days(year, 1, 1) |> floor()
this_day = date_to_iso_days(year, month, day) |> floor()
this_day - first_day + 1
end

Expand Down Expand Up @@ -383,7 +383,7 @@ defmodule Cldr.Calendar.Julian do
knowning the year an error tuple is returned.
"""
@spec days_in_month(month) :: Calendar.day()
@spec days_in_month(month) :: Calendar.day() | {:error, :unresolved}
@impl true
def days_in_month(month) do
case month do
Expand Down
11 changes: 9 additions & 2 deletions lib/cldr/calendar/duration.ex
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,10 @@ defmodule Cldr.Calendar.Duration do
"""

@spec new(from :: date_or_time_or_datetime(), to :: date_or_time_or_datetime()) ::
{:ok, t()} | {:error, {module(), String.t()}}
{:ok, t()}
| {:error, {module(), String.t()}}
| {:ambiguous, DateTime.t(), DateTime.t()}
| {:gap, DateTime.t(), DateTime.t()}

def new(unquote(Cldr.Calendar.datetime()) = from, unquote(Cldr.Calendar.datetime()) = to) do
with :ok <- confirm_same_time_zone(from, to),
Expand Down Expand Up @@ -326,7 +329,11 @@ defmodule Cldr.Calendar.Duration do
}}
"""
@spec new(interval :: interval()) :: {:ok, t()} | {:error, {module(), String.t()}}
@spec new(interval :: interval()) ::
{:ok, t()}
| {:error, {module(), String.t()}}
| {:ambiguous, DateTime.t(), DateTime.t()}
| {:gap, DateTime.t(), DateTime.t()}

if Code.ensure_loaded?(CalendarInterval) do
def new(%CalendarInterval{first: first, last: last, precision: precision} = _interval)
Expand Down
7 changes: 4 additions & 3 deletions lib/cldr/calendar/interval.ex
Original file line number Diff line number Diff line change
Expand Up @@ -256,13 +256,14 @@ defmodule Cldr.Calendar.Interval do
Date.range(~D[2019-W02-1 Cldr.Calendar.ISOWeek], ~D[2019-W02-1 Cldr.Calendar.ISOWeek])
"""
@spec day(Calendar.year(), Calendar.day(), Cldr.Calendar.calendar()) :: Date.Range.t()
@spec day(Calendar.year(), Calendar.day(), Cldr.Calendar.calendar()) ::
Date.Range.t() | {:error, :invalid_date}
@spec day(Date.t()) :: Date.Range.t()

def day(%{calendar: Calendar.ISO} = date) do
%{date | calendar: Cldr.Calendar.Gregorian}
|> day
|> coerce_iso_calendar
|> day()
|> coerce_iso_calendar()
end

def day(date) do
Expand Down
16 changes: 8 additions & 8 deletions lib/cldr/calendar/kday.ex
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ defmodule Cldr.Calendar.Kday do
"""
@spec kday_on_or_before(Calendar.day() | Date.t(), Cldr.Calendar.day_of_week()) ::
Calendar.day() | Date.t()
Calendar.day() | Date.t() | {:error, :incompatible_calendars | :invalid_date}

def kday_on_or_before(%{year: _, month: _, day: _, calendar: calendar} = date, k)
when k in 1..7 do
Expand Down Expand Up @@ -90,7 +90,7 @@ defmodule Cldr.Calendar.Kday do
"""
@spec kday_on_or_after(Calendar.day() | Date.t(), Cldr.Calendar.day_of_week()) ::
Calendar.day() | Date.t()
Calendar.day() | Date.t() | {:error, :incompatible_calendars | :invalid_date}

def kday_on_or_after(%{year: _, month: _, day: _, calendar: calendar} = date, k)
when k in 1..7 do
Expand Down Expand Up @@ -133,7 +133,7 @@ defmodule Cldr.Calendar.Kday do
"""
@spec kday_nearest(Calendar.day() | Date.t(), Cldr.Calendar.day_of_week()) ::
Calendar.day() | Date.t()
Calendar.day() | Date.t() | {:error, :incompatible_calendars | :invalid_date}

def kday_nearest(%{year: _, month: _, day: _, calendar: calendar} = date, k)
when k in 1..7 do
Expand Down Expand Up @@ -180,7 +180,7 @@ defmodule Cldr.Calendar.Kday do
"""
@spec kday_before(Calendar.day() | Date.t(), Cldr.Calendar.day_of_week()) ::
Calendar.day() | Date.t()
Calendar.day() | Date.t() | {:error, :incompatible_calendars | :invalid_date}

def kday_before(%{year: _, month: _, day: _, calendar: calendar} = date, k)
when k in 1..7 do
Expand Down Expand Up @@ -226,7 +226,7 @@ defmodule Cldr.Calendar.Kday do
"""
@spec kday_after(Calendar.day() | Date.t(), Cldr.Calendar.day_of_week()) ::
Calendar.day() | Date.t()
Calendar.day() | Date.t() | {:error, :incompatible_calendars | :invalid_date}

def kday_after(%{year: _, month: _, day: _, calendar: calendar} = date, k)
when k in 1..7 do
Expand Down Expand Up @@ -275,7 +275,7 @@ defmodule Cldr.Calendar.Kday do
"""
@spec nth_kday(Calendar.day() | Date.t(), integer(), Cldr.Calendar.day_of_week()) ::
Calendar.day() | Date.t()
Calendar.day() | Date.t() | {:error, :incompatible_calendars | :invalid_date}

def nth_kday(%{year: _, month: _, day: _, calendar: calendar} = date, n, k)
when k in 1..7 and is_integer(n) do
Expand Down Expand Up @@ -321,7 +321,7 @@ defmodule Cldr.Calendar.Kday do
"""
@spec first_kday(Calendar.day() | Date.t(), Cldr.Calendar.day_of_week()) ::
Calendar.day() | Date.t()
Calendar.day() | Date.t() | {:error, :incompatible_calendars | :invalid_date}

def first_kday(%{year: _, month: _, day: _, calendar: calendar} = date, k)
when k in 1..7 do
Expand Down Expand Up @@ -359,7 +359,7 @@ defmodule Cldr.Calendar.Kday do
"""
@spec last_kday(Calendar.day() | Date.t(), Cldr.Calendar.day_of_week()) ::
Calendar.day() | Date.t()
Calendar.day() | Date.t() | {:error, :incompatible_calendars | :invalid_date}

def last_kday(%{year: _, month: _, day: _, calendar: calendar} = date, k)
when k in 1..7 do
Expand Down
8 changes: 7 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ defmodule Cldr.Calendar.MixProject do
ignore_warnings: ".dialyzer_ignore_warnings",
plt_add_apps: ~w(inets jason mix ex_cldr_currencies ex_cldr_units ex_cldr_lists
ex_cldr_numbers calendar_interval)a,
flags: [:underspecs]
flags: [
:error_handling,
:unknown,
:underspecs,
:extra_return,
:missing_return
]
],
compilers: Mix.compilers()
]
Expand Down
2 changes: 1 addition & 1 deletion mix/basic_behaviour.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ defmodule Cldr.Calendar.Behaviour.Basic do
"""
def date_to_iso_days(year, month, day) do
epoch() + 365 * (year - 1) + floor(year / 4) + 30 * (month - 1) + day - 1
floor(epoch() + 365 * (year - 1) + floor(year / 4) + 30 * (month - 1) + day - 1)
end

@doc """
Expand Down
1 change: 0 additions & 1 deletion mix/for_dialyzer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,4 @@ defmodule Cldr.Calendar.ForDialyzer do

MyApp.Cldr.Calendar.localize(%{month: 3}, :month, format: :wide, locale: :da)
end

end

0 comments on commit 0098717

Please sign in to comment.