I’m trying to add typespecs to the functions created by phx.gen.auth
. One of them is Accounts.User.validate_current_password/2
which is reproduced here for completeness, along with my attempt at a typespec:
@spec validate_current_password(Ecto.Changeset.t(), String.t()) :: Ecto.Changeset.t()
def validate_current_password(changeset, password) do
if valid_password?(changeset.data, password) do
changeset
else
add_error(changeset, :current_password, "is not valid")
end
end
When running mix dialyzer
with missing_return enabled, I get the following error:
lib/leaf/accounts/user.ex:171:missing_range
The type specification is missing types returned by function.
Function:
Leaf.Accounts.User.validate_current_password/2
Type specification return types:
%Ecto.Changeset{
:action => atom(),
:changes => %{atom() => _},
:constraints => [
%{
:constraint =>
binary()
| %Regex{
:opts => binary() | [any()],
:re_pattern => _,
:re_version => _,
:source => binary()
},
:error_message => binary(),
:error_type => atom(),
:field => atom(),
:match => :exact | :prefix | :suffix,
:type => :check | :exclusion | :foreign_key | :unique
}
],
:data => nil | map(),
:empty_values => _,
:errors => Keyword.t({binary(), Keyword.t()}),
:filters => %{atom() => _},
:params => nil | %{binary() => _},
:prepare => [(_ -> any())],
:repo => atom(),
:repo_opts => Keyword.t(),
:required => [atom()],
:types =>
nil
| %{
atom() =>
atom()
| {:array | :assoc | :embed | :in | :map | :maybe | :param, _}
| {:parameterized, atom(), _}
},
:valid? => boolean(),
:validations => Keyword.t()
}
Missing from spec:
atom()
My question is, where does that atom()
come from, and how do I get rid of the error?
After fiddling around a bit, the error still happens if both branches of the if
return changeset
, but not if I remove the if
completely and just have the function returning changeset
directly, e.g. this produces the same error:
@spec validate_current_password(Ecto.Changeset.t(), String.t()) :: Ecto.Changeset.t()
def validate_current_password(changeset, password) do
if valid_password?(changeset.data, password) do
changeset
else
changeset
end
end
But this works:
@spec validate_current_password(Ecto.Changeset.t(), String.t()) :: Ecto.Changeset.t()
def validate_current_password(changeset, password) do
changeset
end