Dialayer warning of Ecto.Multi.insert_all

When I use a tuple as the schema_or_source of Ecto.Multi.insert_all, Sometimes the warning will appear, and sometimes not.

This is my code

    |> Ecto.Multi.insert_all(:contents_golden, {"contents_golden", Content}, Map.get(grouped_contents, "golden", []))

the dialyzers warning always show:

The call 'Elixir.Ecto.Multi':insert_all
         (_multi@1 ::
              #{'__struct__' := 'Elixir.Ecto.Multi',
                'names' := 'Elixir.MapSet':t(_),
                'operations' := [{_, _}]},
          any()) will never return since it differs in the 3rd argument from the success typing arguments: 
         (#{'__struct__' := 'Elixir.Ecto.Multi',
            'names' := 'Elixir.MapSet':t(_),
            'operations' := [{_, {_, _} | {_, _, _} | {_, _, _, _}}]},
          atom() | binary() | {'nil' | binary(), binary()},
          [[{atom(), _}] | map()])

but in another project,

this code

        |> Ecto.Multi.insert_all(:insert_all, {"branched_cards", Card}, branched_cards_list)
        |> Repo.transaction()

The dialyzer show nothing.

my deps is

* ecto 3.3.1 (Hex package) (mix)
  locked at 3.3.1 (ecto) 82ab7429
* ecto_sql 3.3.2 (Hex package) (mix)
  locked at 3.3.2 (ecto_sql) 92804e0d

I’ve checked the source code of Ecto, the spec of source_or_shema is @typep schema_or_source :: binary | {binary | nil, binary} | atom, {binary|nil, binary} is so wierd.

So what’s the type of branched_cards_list?

A list of Card, but the 3rd args not means the branched_cards_list, it means {"branched_cards", Card} and {"contents_golden", Content}

Thats not the problem.

The dialyzer error complains about the 3rd argument, not the 4th.

So you can reduce the message to `{"contents_golden", Content}` does not match the required type `atom | binary | {nil | binary, binary}`

Which is indeed true. I’m not sure though, why dialyzer does not complain in the other project.

Anyway, the type of the 3rd argument is @typep, so in my opinion it shouldn’t even occur in the public interface of a function. If its considered an implementation detail it should have been made @opaque.

Anyway, the documentation does use litaral aliases in that position all the time, so the type should be fixed and become properly documented and a @type.

Yes, I’m really confusing that the spec of shema_or_source of Repo.insert_all is binary | {binary, module} | module,, and Ecto.Multi.insert_all is atom | binary | {nil | binary, binary}


So what’s the right way to set dynamic table when I use Multi functions?

Whatever works in code, disabling dialyzer for the function in question either through @dialyzer module attributes or through the configuration of the dialyzer-task you are using.

Also file a bug please.

1 Like

Thanks, I’ll use @dialyzer {:noreturn_function, insert_all_contents: 2} now and wish the bug will be fixed in the near feature.

1 Like