Skip to content

Commit 06a2969

Browse files
Make nil comparison error message more specific (#4561)
1 parent 3a397bf commit 06a2969

File tree

2 files changed

+29
-17
lines changed

2 files changed

+29
-17
lines changed

lib/ecto/query/builder.ex

+20-14
Original file line numberDiff line numberDiff line change
@@ -360,19 +360,25 @@ defmodule Ecto.Query.Builder do
360360

361361
if is_nil(left) or is_nil(right) do
362362
error!(
363-
"comparison with nil is forbidden as it is unsafe. " <>
363+
"comparison with nil in `#{Macro.to_string(expr)}` is forbidden as it is unsafe. " <>
364364
"If you want to check if a value is nil, use is_nil/1 instead"
365365
)
366366
end
367367

368368
ltype = quoted_type(right, vars)
369369
rtype = quoted_type(left, vars)
370370

371-
{left, params_acc} = escape(left, ltype, params_acc, vars, env)
372-
{right, params_acc} = escape(right, rtype, params_acc, vars, env)
371+
{escaped_left, params_acc} = escape(left, ltype, params_acc, vars, env)
372+
{escaped_right, params_acc} = escape(right, rtype, params_acc, vars, env)
373373

374374
{params, acc} = params_acc
375-
{{:{}, [], [comp_op, [], [left, right]]}, {params |> wrap_nil(left) |> wrap_nil(right), acc}}
375+
376+
params =
377+
params
378+
|> wrap_nil(escaped_left, Macro.to_string(right))
379+
|> wrap_nil(escaped_right, Macro.to_string(left))
380+
381+
{{:{}, [], [comp_op, [], [escaped_left, escaped_right]]}, {params, acc}}
376382
end
377383

378384
# mathematical operators
@@ -585,18 +591,18 @@ defmodule Ecto.Query.Builder do
585591
defp validate_json_field!(unsupported_field),
586592
do: error!("`#{Macro.to_string(unsupported_field)}` is not a valid json field")
587593

588-
defp wrap_nil(params, {:{}, _, [:^, _, [ix]]}),
589-
do: wrap_nil(params, length(params) - ix - 1, [])
594+
defp wrap_nil(params, {:{}, _, [:^, _, [ix]]}, compare_str),
595+
do: wrap_nil(params, length(params) - ix - 1, compare_str, [])
590596

591-
defp wrap_nil(params, _other), do: params
597+
defp wrap_nil(params, _other, _compare_str), do: params
592598

593-
defp wrap_nil([{val, type} | params], 0, acc) do
594-
val = quote do: Ecto.Query.Builder.not_nil!(unquote(val))
599+
defp wrap_nil([{val, type} | params], 0, compare_str, acc) do
600+
val = quote do: Ecto.Query.Builder.not_nil!(unquote(val), unquote(compare_str))
595601
Enum.reverse(acc, [{val, type} | params])
596602
end
597603

598-
defp wrap_nil([pair | params], i, acc) do
599-
wrap_nil(params, i - 1, [pair | acc])
604+
defp wrap_nil([pair | params], i, compare_str, acc) do
605+
wrap_nil(params, i - 1, compare_str, [pair | acc])
600606
end
601607

602608
defp expand_and_split_fragment(query, env) do
@@ -1184,13 +1190,13 @@ defmodule Ecto.Query.Builder do
11841190
@doc """
11851191
Called by escaper at runtime to verify that a value is not nil.
11861192
"""
1187-
def not_nil!(nil) do
1193+
def not_nil!(nil, compare_str) do
11881194
raise ArgumentError,
1189-
"comparison with nil is forbidden as it is unsafe. " <>
1195+
"comparing `#{compare_str}` with `nil` is forbidden as it is unsafe. " <>
11901196
"If you want to check if a value is nil, use is_nil/1 instead"
11911197
end
11921198

1193-
def not_nil!(not_nil) do
1199+
def not_nil!(not_nil, _compare_str) do
11941200
not_nil
11951201
end
11961202

test/ecto/query_test.exs

+9-3
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,23 @@ defmodule Ecto.QueryTest do
5656

5757
test "does not allow nils in comparison at compile time" do
5858
assert_raise Ecto.Query.CompileError,
59-
~r"comparison with nil is forbidden as it is unsafe", fn ->
59+
~r"comparison with nil in `p.id == nil` is forbidden as it is unsafe", fn ->
6060
quote_and_eval from p in "posts", where: p.id == nil
6161
end
6262
end
6363

6464
test "does not allow interpolated nils at runtime" do
65+
id = nil
66+
6567
assert_raise ArgumentError,
66-
~r"comparison with nil is forbidden as it is unsafe", fn ->
67-
id = nil
68+
~r"nil given for `id`. comparison with nil is forbidden as it is unsafe", fn ->
6869
from p in "posts", where: [id: ^id]
6970
end
71+
72+
assert_raise ArgumentError,
73+
~r"comparing `p.id` with `nil` is forbidden as it is unsafe", fn ->
74+
from p in "posts", where: p.id == ^id
75+
end
7076
end
7177

7278
test "allows arbitrary parentheses in where" do

0 commit comments

Comments
 (0)