jeremyjh / dialyxir

Mix tasks to simplify use of Dialyzer in Elixir projects.
Apache License 2.0
1.69k stars 141 forks source link

Failed to parse warning (SQL query as charlist instead of binary) #437

Open smaximov opened 3 years ago

smaximov commented 3 years ago

Environment

Current behavior

Consider this module (requires ecto/ecto_sql/postgrex to compile):

defmodule Repo do
  use Ecto.Repo,
    otp_app: :myapp,
    adapter: Ecto.Adapters.Postgres

  def fails_to_parse_dialyzer_warning do
    'SELECT do_something($1);' |> Repo.query!([1])
  end

  def parses_dialyzer_warning do
    'SELECT $1;' |> Repo.query!([1])
  end
end

The issue with this code is that SQL queries are passed as charlists, but Repo.query/2 expects a binary.

Running mix dialyzer yields:

lib/repo.ex:6:no_return
Function fails_to_parse_dialyzer_warning/0 has no local return.
________________________________________________________________________________
Please file a bug in https://github.com/jeremyjh/dialyxir/issues with this message.
Failed to parse warning:
[{:"(", 1}, {:atom_part, 1, '"'}, {:atom_part, 1, 'S'}, {:atom_part, 1, 'E'}, {:atom_part, 1, 'L'}, {:atom_part, 1, 'E'}, {:atom_part, 1, 'C'}, {:atom_part, 1, 'T'}, {:atom_part, 1, 'd'}, {:atom_part, 1, 'o'}, {:_, 1}, {:atom_part, 1, 's'}, {:atom_part, 1, 'o'}, {:atom_part, 1, 'm'}, {:atom_part,
1, 'e'}, {:atom_part, 1, 't'}, {:atom_part, 1, 'h'}, {:atom_part, 1, 'i'}, {:atom_part, 1, 'n'}, {:atom_part, 1, 'g'}, {:"(", 1}, {:atom_part, 1, '$'}, {:int, 1, 1}, {:")", 1}, {:atom_part, 1, ';'}, {:atom_part, 1, '"'}, {:",", 1}, {:"[", 1}, {:int, 1, 1}, {:"]", 1}, {:")", 1}]

Legacy warning:
lib/repo.ex:7: The call 'Elixir.Repo':'query!'("SELECT do_something($1);",[1]) will never return since it differs in the 1st argument from the success typing arguments: (binary(),[any()])
________________________________________________________________________________
lib/repo.ex:10:no_return
Function parses_dialyzer_warning/0 has no local return.
________________________________________________________________________________
lib/repo.ex:11:call
The function call will not succeed.

Repo.query!(:"\"SELECT$1;\"", [1])

will never return since the 1st arguments differ
from the success typing arguments:

(binary(), [any()])

There're two (maybe unrelated) problems with the result:

  1. In the warning emitted for parses_dialyzer_warning/0, the SQL query (a charlist) is formatted as an atom :"\"SELECT$1;\" (also note that the space between SELECT and $1 is missing).
  2. Dialyxir fails to parse the warning emitted for fails_to_parse_dialyzer_warning/0, but the only difference between the two functions are SQL queries they are using: one uses SELECT $1, the other — SELECT do_something_with($1).

Expected behavior

It gives two warnings similar to the warning emitted for parses_dialyzer_warning/0 (but with SQL queries formatted correctly as charlists).

Alternative outputs

--format dialyzer:

lib/repo.ex:6: Function fails_to_parse_dialyzer_warning/0 has no local return
lib/repo.ex:7: The call 'Elixir.Repo':'query!'("SELECT do_something($1);",[1]) will never return since it differs in the 1st argument from the success typing arguments: (binary(),[any()])
lib/repo.ex:10: Function parses_dialyzer_warning/0 has no local return
lib/repo.ex:11: The call 'Elixir.Repo':'query!'("SELECT $1;",[1]) will never return since it differs in the 1st argument from the success typing arguments: (binary(),[any()])

Note that SQL queries are displayed correctly as charlists (using Erlang syntax).

--format raw:

{:warn_return_no_exit, {'lib/repo.ex', 6}, {:no_return, [:only_normal, :fails_to_parse_dialyzer_warning, 0]}}
{:warn_failing_call, {'lib/repo.ex', 7}, {:call, [Repo, :query!, '("SELECT do_something($1);",[1])', [1], :only_sig, '(binary(),[any()])', '\#{\'num_rows\':=non_neg_integer(), \'rows\':=\'nil\' | [binary() | [any()]], atom()=>_}', {false, :none}]}}
{:warn_return_no_exit, {'lib/repo.ex', 10}, {:no_return, [:only_normal, :parses_dialyzer_warning, 0]}}
{:warn_failing_call, {'lib/repo.ex', 11}, {:call, [Repo, :query!, '("SELECT $1;",[1])', [1], :only_sig, '(binary(),[any()])', '\#{\'num_rows\':=non_neg_integer(), \'rows\':=\'nil\' | [binary() | [any()]], atom()=>_}', {false, :none}]}}