The variable "user" is unsafe as it has been set inside a case/cond/receive/if/&&/||

The code is from Phoenix Guardian example. I think it’s cleaner than using multiple case logics but the compiler give you this annoying warning. Is the compiler right and is there a better way of writing it?

  defp create_user_from_auth(auth, current_user, repo) do
    user = current_user
    if !user, do: user = repo.get_by(User, email: auth.info.email)
    if !user, do: user = create_user(auth, repo)
    authorization_from_auth(user, auth, repo)
    {:ok, user}
  end

warning: the variable "user" is unsafe as it has been set inside a case/cond/receive/if/&&/||. Please explicitly return the variable value instead. For example:

    case int do
      1 -> atom = :one
      2 -> atom = :two
    end

should be written as

    atom =
      case int do
        1 -> :one
        2 -> :two
      end

You can (hopefully, because I did not test it) do something like this

  defp create_user_from_auth(auth, current_user, repo) do
    user =
      if !current_user, do: repo.get_by(User, email: auth.info.email), else: nil
    user = 
      if !user, do: create_user(auth, repo), else: user
    case user do
      nil ->
        {:error, "reason for an error, ie: `user does not exist`"}
      _ ->
        authorization_from_auth(user, auth, repo)
        {:ok, user}
     end
  end

I might be totally wrong here though :wink:

Anyway the compiler is warning you, because, setting rest of code aside look at this line:
if !x, do: y = z
It’s similar to second line in your function. y is set only if x == false, it won’t be set if x == true and if you will try to use y later in code, when it is not yet set… you got a problem :wink:

So similarly
if !user, do: user = repo.get_by(User, email: auth.info.email)
user is set only when !user which means user == false and what about when user == true ? Then user won’t be set at all, and this may give you a problem :scream: Compiler is not smart enough to understand this code like a human does. So although from my point of view your code seems to be legit an will work, compiler just warns you, to make you aware of potential problems.

Why not to use standard “or” construction?

user = current_user || repo.get_by(User, email: auth.info.email) || create_user(auth, repo)

@grych That fixes it in one go :smiley:
I still don’t think the compiler should be outputting a warning though. There is always a value in user from the first statement.

@omin: check the “Deprecation of imperative assignment” topic in Elixir v1.3 released - The Elixir programming language. This warning appears only in Elixir 1.3. And in my opinion, it is great! We should avoid such constructions in functional languages, even if they allow it.

Everything is an expression; everything will return some value.

As others has said, the compiler will warn you if you construct something like one would write in an imperative language. All of a sudden branching out and defining a variable for later use, outside the block, is hard to read and confusing.

:+1: Thanks guys