Hey all — I’m running into an interesting issue with dialyzer. Below is a trimmed down/generalized example of what I’m running into.
The problem arises as I’m composing some Ecto.Query
s.
base_query
makes a call to preload([_, u], upload: u)
and dialyzer seems to ignore that fact.
defmodule SomeItemContext do
import Ecto.Query
@spec list_item_data(String.t(), pos_integer, non_neg_integer | nil) :: [MyFirstStruct.t()]
def list_item_data(item_id, limit, offset) do
do_list_item_data(item_id, limit, offset, MyFirstStruct)
end
@typep struct_modules_with_upload_assoc :: MySecondStruct | MyFirstStruct
@spec do_list_item_data(
String.t(),
pos_integer(),
non_neg_integer() | nil,
struct_modules_with_upload_assoc()
) :: [MySecondStruct.t()] | [MyFirstStruct.t()]
defp do_list_item_data(item_id, limit, offset, item_type) do
item_id
|> base_query(item_type)
|> limit(^limit)
|> offset(^offset)
|> order_by([datum, u], u.updated_at)
|> Repo.all()
end
@spec base_query(String.t(), struct_modules_with_upload_assoc()) :: Ecto.Query.t()
defp base_query(item_id, item_type) do
item_type
|> join(:inner, [item], u in Upload, on: item.upload_id == u.id)
|> where([item, _], item.item_id == ^item_id)
|> where([_, u], u.status == ^Upload.status_upload_complete())
|> preload([_, u], upload: u) # !!! Note the call to preload here !!!
end
end
Edit:
@spec download_attachments([%{upload: Upload.t()}]) :: {:ok, [String.t(), String.t()]}
def download_attachements(items) do
# irrelevant
end
In another module I make a the below call.
Dialyzer warns on: call The function call download_attachments will not succeed.
items = Uploads.list_item_data(item.id, 10, 0)
attachments = download_attachments(items)
However, if I preload the :upload
assoc in the calling module, the dialyzer warning is suppressed.
items =
item.id
|> Uploads.list_item_data(10, 0)
|> Repo.preload(:upload)
attachments = download_attachments(items)