2

I have an umbrella app. I see the value of Dialyzer and I'm trying to get started with it. I've gotten pretty far but I have an issue around Ecto I cannot solve.

This is for a small App in the umbrella that handles Authentication. I can trim it all down the simplest example.

Using Elixir 1.4.2 and Dialyxir 0.4.0.

Code in Question

defmodule Auth.Account do
  use Ecto.Schema
  import Ecto.Changeset

  schema "auth_accounts" do
    field :email, :string
    field :password_hash, :string
    field :password, :string, virtual: true

    timestamps()
  end

  def build(params \\ %{}) do
    changeset(%__MODULE__{}, params)
  end

  def changeset(account, params \\ %{}) do
    account
    |> cast(params, ~w(email password))
  end
end

Relevant Error Output

lib/auth/account.ex:13: Function build/0 has no local return
lib/auth/account.ex:13: Function build/1 has no local return
lib/auth/account.ex:14: The call 'Elixir.Auth.Account':changeset(#{'__meta__':=#{'__struct__':='Elixir.Ecto.Schema.Metadata', 'context':='nil', 'source':={'nil',<<_:104>>}, 'state':='built'}, '__struct__':='Elixir.Auth.Account', 'email':='nil', 'id':='nil', 'inserted_at':='nil', 'password':='nil', 'password_hash':='nil', 'updated_at':='nil'},params@1::any()) 
  will never return since it differs in the 1st argument from the success typing arguments: ({map(),map()} | #{'__struct__':=atom(), 'action'=>'delete' | 'insert' | 'nil' | 'replace' | 'update', 'changes'=>#{atom()=>_}, 'constraints'=>[#{'constraint':=binary(), 'field':=atom(), 'match':='exact' | 'suffix', 'message':={_,_}, 'type':='unique'}], 'data'=>'nil' | #{'__struct__':=atom()}, 'empty_values'=>_, 'errors'=>[{atom(),{_,_}}], 'filters'=>#{atom()=>_}, 'params'=>'nil' | #{binary()=>_}, 'prepare'=>[fun((map()) -> map())], 'repo'=>atom(), 'required'=>[atom()], 'types'=>'nil' | #{atom()=>atom() | {'array',_} | {'embed',map()} | {'in',_} | {'map',_}}, 'valid?'=>boolean(), 'validations'=>[{atom(),_}]},'invalid' | #{'__struct__'=>none(), atom() | binary()=>_})

It appears the problem is around the build function's use of %__MODULE__{}. See this related Stack Overflow Topic.

However, I just can't figure out a valid alternate syntax.

Community
  • 1
  • 1
Mark Eric
  • 753
  • 1
  • 7
  • 14
  • Does this fix the warning? `def build(params \\ %{}) do`? – Dogbert Feb 28 '17 at 20:02
  • Unfortunately not. I updated the code above so that doesn't confuse others. But I just re-tested to verify. The problem remains. Rebuilt the PLT to be sure. – Mark Eric Feb 28 '17 at 21:58
  • Scratch that, I'm unable to reproduce this locally if I copy paste your exact code in a new mix package. What version of Ecto are you using? Are you able to reproduce this if you copy that module to a brand new mix package? (FWIW I don't think this error is related to the other question you linked to.) – Dogbert Mar 01 '17 at 05:16
  • Purely for some possibly-helpful background context, many of the core or commonly used Elixir libraries don't themselves use (i.e. regularly run) Dialyzer and there have been a few typespec errors that can cause Dialyzer to emit warnings for code that uses those libraries. Fortunately, those same libraries are almost all well written and thus fairly easy to read yourself. – Kenny Evitt Dec 18 '21 at 06:07

1 Answers1

2

Dogbert prompted me to dig deeper by not being able to reproduce it.

I was on ecto ~> 2.0. The mix.lock file had me at 2.0.5. After mix deps.unlock --all and a mix deps.clean --all and mix deps.get, I was moved up to ecto 2.1.3.

After the library upgrades, dialyzer no longer complained about this. So my fix was to upgrade to a newer ecto version.

Mark Eric
  • 753
  • 1
  • 7
  • 14