Function which calculates time from now to end date - strange warning from VSCode

Hello,

I created a function which takes timedate as parameter in NaiveDateTime format. This date is an end date and the function has to calculate how much time is left from now to this end date.

def time_remaining(ends_at) when not is_nil(ends_at) do
    days_remaining = NaiveDateTime.diff(ends_at, NaiveDateTime.utc_now(), :day)

    if days_remaining < 1 do
      difference = NaiveDateTime.diff(ends_at, NaiveDateTime.utc_now(), :second)
      hours = div(difference, 3600)
      minutes = div(difference, 60) - (hours * 60)
      seconds = rem(difference, 60)

      "#{hours}h : #{minutes}min : #{seconds}s Remaining"
    else
      "#{days_remaining} Days Remaining"
    end
  end

The functions works as expected but VSCode is giving me this warning:

Guard test 
          _@46 ::
              {'safe',
               binary() |
               maybe_improper_list(binary() |
                                   maybe_improper_list(any(),
                                                       binary() | []) |
                                   byte(),
                                   binary() | [])} =:= 
          'nil' can never succeed

Can someone please help me locate the problem?

Do you have another function which pattern matches for nil ?

def time_remaining(nil), do: # do something 

if you don’t have it may be you can rewrite it this way to handle nil:

def time_remaining(nil),  do: # do something return an error or empty string 
def time_remaining(ends_at) do
    days_remaining = NaiveDateTime.diff(ends_at, NaiveDateTime.utc_now(), :day)

    if days_remaining < 1 do
      difference = NaiveDateTime.diff(ends_at, NaiveDateTime.utc_now(), :second)
      hours = div(difference, 3600)
      minutes = div(difference, 60) - (hours * 60)
      seconds = rem(difference, 60)

      "#{hours}h : #{minutes}min : #{seconds}s Remaining"
    else
      "#{days_remaining} Days Remaining"
    end
  end

Thanks for checking it out, unfortunately, nil handling doesn’t help, the warning remains.

What happens when you invoke function time_remaining with nil ?

What version of Elixir are you using? I get no warnings when I compile in 1.14.1.

Regardless, since ends_at must be a NaiveDateTime, it’s better to be explicit about this in the function head as opposed to just checking that it isn’t nil:

def time_remaining(%NaiveDateTime{} = ends_at) do
  ...
end

or if you prefer when you can do the more verbose:

def time_remaining(ends_at) when is_struct(ends_at, NaiveDateTime) do
  ...
end
2 Likes

Sure that is coming from this function?
Would expect sth like ends_at@... here.

I haven’t seen this type of Dialyzer warning on a guard call before; but I have seen similar warnings for function parameters when Dialyzer thinks there’s no way for a given pattern match to possibly match a condition… which I sometimes do for a catch all function.

For example if I write something like the below, with no other calls to func_a/1:

def entry_func() do
  %{test: "Hi!"}
  |> func_a()
end

def func_a(%{} = param) do
  # do something
end

def func_a(_) do
  raise "Bad Param"
end

I will, with some regularity, see the ElixirLS Dialyzer warning that the second function head for func_a will never run… which looks like it could be similar to what you’re seeing. In this case Dialyzer isn’t exactly wrong… there’s no way for the second func_a/1 function head to match. But it’s not exactly helpful either because I may know now that in the future I might call it from somewhere else where the second function head is helpful.

Anyway, I can’t say for sure this is what you’re seeing, but it looks similar.

1 Like

I don’t think the error is in the time_remaining function shown; the success typing in the error message doesn’t match anything that would appear inside that function.

It’s instead a synonym for Phoenix.HTML.safe. Maybe there’s a when not is_nil check someplace in the template near where this is used?

1 Like

@Sebb
Sure that is coming from this function?
Would expect sth like ends_at@... here.

@al2o3cr
I don’t think the error is in the time_remaining function shown; the success typing in the error message doesn’t match anything that would appear inside that function.

Yes, it looks like something else is happening here… if I remove the logic from this function and make it to return just a simple string, VSCode still shows the same error but starts highlighting the code in another function so the error is probably somewhere else,=. I will isolate each function from that view and and try to find the problem that way. Thanks for your help.

yes, @al2o3cr is right, didn’t look that closely.

You can try to delete the .elixir-ls folder and restart/reload vscode.
Sometimes there is a hickup.
And maybe install dilyxir and run mix dialyzer to get a better picture.

1 Like

Deleting .elixir_ls folder solved it, thanks for taking the time to help me out here.

2 Likes