diff --git a/lib/elixir/test/elixir/kernel/errors_test.exs b/lib/elixir/test/elixir/kernel/errors_test.exs index f5449006298..0a582a72b56 100644 --- a/lib/elixir/test/elixir/kernel/errors_test.exs +++ b/lib/elixir/test/elixir/kernel/errors_test.exs @@ -1,4 +1,4 @@ -Code.require_file "../test_helper.exs", __DIR__ +Code.require_file("../test_helper.exs", __DIR__) defmodule Kernel.ErrorsTest do use ExUnit.Case, async: true @@ -10,59 +10,61 @@ defmodule Kernel.ErrorsTest do end test "invalid token" do - assert_eval_raise SyntaxError, + assert_eval_raise( + SyntaxError, "nofile:1: unexpected token: \"\u200B\" (column 7, codepoint U+200B)", '[foo: \u200B]\noops' + ) end test "invalid __CALLER__" do - assert_eval_raise CompileError, + assert_eval_raise( + CompileError, "nofile:1: variable '__CALLER__' is unbound", 'defmodule Sample do def hello do __CALLER__ end end' + ) end test "invalid quoted token" do - assert_eval_raise SyntaxError, - "nofile:1: syntax error before: \"world\"", - '"hello" "world"' + assert_eval_raise(SyntaxError, "nofile:1: syntax error before: \"world\"", '"hello" "world"') - assert_eval_raise SyntaxError, - "nofile:1: syntax error before: 'Foobar'", - '1 Foobar' + assert_eval_raise(SyntaxError, "nofile:1: syntax error before: 'Foobar'", '1 Foobar') - assert_eval_raise SyntaxError, - "nofile:1: syntax error before: foo", - 'Foo.:foo' + assert_eval_raise(SyntaxError, "nofile:1: syntax error before: foo", 'Foo.:foo') - assert_eval_raise SyntaxError, - "nofile:1: syntax error before: \"foo\"", - 'Foo.:"foo\#{:bar}"' + assert_eval_raise(SyntaxError, "nofile:1: syntax error before: \"foo\"", 'Foo.:"foo\#{:bar}"') - assert_eval_raise SyntaxError, - "nofile:1: syntax error before: \"", - 'Foo.:"\#{:bar}"' + assert_eval_raise(SyntaxError, "nofile:1: syntax error before: \"", 'Foo.:"\#{:bar}"') end test "invalid identifier" do - message = fn name -> "nofile:1: invalid character \"@\" (codepoint U+0040) in identifier: #{name}" end - assert_eval_raise SyntaxError, message.("foo@"), 'foo@' - assert_eval_raise SyntaxError, message.("foo@"), 'foo@ ' - assert_eval_raise SyntaxError, message.("foo@bar"), 'foo@bar' + message = fn name -> + "nofile:1: invalid character \"@\" (codepoint U+0040) in identifier: #{name}" + end + + assert_eval_raise(SyntaxError, message.("foo@"), 'foo@') + assert_eval_raise(SyntaxError, message.("foo@"), 'foo@ ') + assert_eval_raise(SyntaxError, message.("foo@bar"), 'foo@bar') + + message = fn name -> + "nofile:1: invalid character \"@\" (codepoint U+0040) in alias: #{name}" + end - message = fn name -> "nofile:1: invalid character \"@\" (codepoint U+0040) in alias: #{name}" end - assert_eval_raise SyntaxError, message.("Foo@"), 'Foo@' - assert_eval_raise SyntaxError, message.("Foo@bar"), 'Foo@bar' + assert_eval_raise(SyntaxError, message.("Foo@"), 'Foo@') + assert_eval_raise(SyntaxError, message.("Foo@bar"), 'Foo@bar') message = "nofile:1: invalid character \"!\" (codepoint U+0021) in alias: Foo!" - assert_eval_raise SyntaxError, message, 'Foo!' + assert_eval_raise(SyntaxError, message, 'Foo!') message = "nofile:1: invalid character \"?\" (codepoint U+003F) in alias: Foo?" - assert_eval_raise SyntaxError, message, 'Foo?' + assert_eval_raise(SyntaxError, message, 'Foo?') # TODO: Remove this check once we depend on OTP 20+ if :erlang.system_info(:otp_release) >= '20' do - message = "nofile:1: invalid character \"ó\" (codepoint U+00F3) in alias (only ascii characters are allowed): Foó" - assert_eval_raise SyntaxError, message, 'Foó' + message = + "nofile:1: invalid character \"ó\" (codepoint U+00F3) in alias (only ascii characters are allowed): Foó" + + assert_eval_raise(SyntaxError, message, 'Foó') message = ~r""" Elixir expects unquoted Unicode atoms and variables to be in NFC form. @@ -76,148 +78,164 @@ defmodule Kernel.ErrorsTest do "foó" \(codepoints 0066 006F 00F3\) """ - assert_eval_raise SyntaxError, message, :unicode.characters_to_nfd_list("foó") + + assert_eval_raise(SyntaxError, message, :unicode.characters_to_nfd_list("foó")) end end test "invalid fn" do - assert_eval_raise SyntaxError, + assert_eval_raise( + SyntaxError, "nofile:1: expected clauses to be defined with -> inside: 'fn'", 'fn 1 end' + ) end test "kw missing space" do msg = "nofile:1: keyword argument must be followed by space after: foo:" - assert_eval_raise SyntaxError, msg, "foo:bar" - assert_eval_raise SyntaxError, msg, "foo:+" - assert_eval_raise SyntaxError, msg, "foo:+1" + assert_eval_raise(SyntaxError, msg, "foo:bar") + assert_eval_raise(SyntaxError, msg, "foo:+") + assert_eval_raise(SyntaxError, msg, "foo:+1") end test "sigil terminator" do - assert_eval_raise TokenMissingError, + assert_eval_raise( + TokenMissingError, "nofile:3: missing terminator: \" (for sigil ~r\" starting at line 1)", '~r"foo\n\n' + ) - assert_eval_raise TokenMissingError, + assert_eval_raise( + TokenMissingError, "nofile:3: missing terminator: } (for sigil ~r{ starting at line 1)", '~r{foo\n\n' + ) end test "dot terminator" do - assert_eval_raise TokenMissingError, + assert_eval_raise( + TokenMissingError, "nofile:1: missing terminator: \" (for function name starting at line 1)", 'foo."bar' + ) end test "string terminator" do - assert_eval_raise TokenMissingError, + assert_eval_raise( + TokenMissingError, "nofile:1: missing terminator: \" (for string starting at line 1)", '"bar' + ) end test "heredoc start" do - assert_eval_raise SyntaxError, + assert_eval_raise( + SyntaxError, "nofile:1: heredoc start must be followed by a new line after \"\"\"", '"""bar\n"""' + ) end test "heredoc terminator" do - assert_eval_raise TokenMissingError, + assert_eval_raise( + TokenMissingError, "nofile:2: missing terminator: \"\"\" (for heredoc starting at line 1)", '"""\nbar' - assert_eval_raise SyntaxError, + ) + + assert_eval_raise( + SyntaxError, "nofile:2: invalid location for heredoc terminator, please escape token or move it to its own line: \"\"\"", '"""\nbar"""' + ) end test "unexpected end" do - assert_eval_raise SyntaxError, - "nofile:1: unexpected token: end", - '1 end' + assert_eval_raise(SyntaxError, "nofile:1: unexpected token: end", '1 end') end test "syntax error" do - assert_eval_raise SyntaxError, - "nofile:1: syntax error before: '.'", - '+.foo' + assert_eval_raise(SyntaxError, "nofile:1: syntax error before: '.'", '+.foo') end test "syntax error before sigil" do msg = fn x -> "nofile:1: syntax error before: sigil ~s starting with content '#{x}'" end - assert_eval_raise SyntaxError, msg.("bar baz"), '~s(foo) ~s(bar baz)' - assert_eval_raise SyntaxError, msg.(""), '~s(foo) ~s()' - assert_eval_raise SyntaxError, msg.("bar "), '~s(foo) ~s(bar \#{:baz})' - assert_eval_raise SyntaxError, msg.(""), '~s(foo) ~s(\#{:bar} baz)' + assert_eval_raise(SyntaxError, msg.("bar baz"), '~s(foo) ~s(bar baz)') + assert_eval_raise(SyntaxError, msg.(""), '~s(foo) ~s()') + assert_eval_raise(SyntaxError, msg.("bar "), '~s(foo) ~s(bar \#{:baz})') + assert_eval_raise(SyntaxError, msg.(""), '~s(foo) ~s(\#{:bar} baz)') end test "op ambiguity" do max = 1 assert max == 1 - assert (max 1, 2) == 2 + assert max(1, 2) == 2 end test "syntax error with do" do - assert_eval_raise SyntaxError, - ~r/nofile:1: unexpected token "do"./, - 'if true, do\n' + assert_eval_raise(SyntaxError, ~r/nofile:1: unexpected token "do"./, 'if true, do\n') - assert_eval_raise SyntaxError, - ~r/nofile:1: unexpected keyword "do:"./, - 'if true do:\n' + assert_eval_raise(SyntaxError, ~r/nofile:1: unexpected keyword "do:"./, 'if true do:\n') end test "syntax error on parens call" do - msg = "nofile:1: unexpected parentheses. If you are making a function call, do not " <> - "insert spaces between the function name and the opening parentheses. " <> - "Syntax error before: '('" + msg = + "nofile:1: unexpected parentheses. If you are making a function call, do not " <> + "insert spaces between the function name and the opening parentheses. " <> + "Syntax error before: '('" - assert_eval_raise SyntaxError, msg, 'foo (hello, world)' + assert_eval_raise(SyntaxError, msg, 'foo (hello, world)') end test "syntax error on nested no parens call" do msg = ~r"nofile:1: unexpected comma. Parentheses are required to solve ambiguity" - assert_eval_raise SyntaxError, msg, '[foo 1, 2]' - assert_eval_raise SyntaxError, msg, '[foo bar 1, 2]' - assert_eval_raise SyntaxError, msg, '[do: foo 1, 2]' - assert_eval_raise SyntaxError, msg, 'foo(do: bar 1, 2)' - assert_eval_raise SyntaxError, msg, '{foo 1, 2}' - assert_eval_raise SyntaxError, msg, '{foo bar 1, 2}' - assert_eval_raise SyntaxError, msg, 'foo 1, foo 2, 3' - assert_eval_raise SyntaxError, msg, 'foo 1, @bar 3, 4' - assert_eval_raise SyntaxError, msg, 'foo 1, 2 + bar 3, 4' - assert_eval_raise SyntaxError, msg, 'foo(1, foo 2, 3)' - - assert is_list List.flatten [1] - assert is_list Enum.reverse [3, 2, 1], [4, 5, 6] - assert is_list(Enum.reverse [3, 2, 1], [4, 5, 6]) - assert false || is_list Enum.reverse [3, 2, 1], [4, 5, 6] - assert [List.flatten List.flatten [1]] == [[1]] - - interpret = fn x -> Macro.to_string Code.string_to_quoted! x end + assert_eval_raise(SyntaxError, msg, '[foo 1, 2]') + assert_eval_raise(SyntaxError, msg, '[foo bar 1, 2]') + assert_eval_raise(SyntaxError, msg, '[do: foo 1, 2]') + assert_eval_raise(SyntaxError, msg, 'foo(do: bar 1, 2)') + assert_eval_raise(SyntaxError, msg, '{foo 1, 2}') + assert_eval_raise(SyntaxError, msg, '{foo bar 1, 2}') + assert_eval_raise(SyntaxError, msg, 'foo 1, foo 2, 3') + assert_eval_raise(SyntaxError, msg, 'foo 1, @bar 3, 4') + assert_eval_raise(SyntaxError, msg, 'foo 1, 2 + bar 3, 4') + assert_eval_raise(SyntaxError, msg, 'foo(1, foo 2, 3)') + + assert is_list(List.flatten([1])) + assert is_list(Enum.reverse([3, 2, 1], [4, 5, 6])) + assert is_list(Enum.reverse([3, 2, 1], [4, 5, 6])) + assert false || is_list(Enum.reverse([3, 2, 1], [4, 5, 6])) + assert [List.flatten(List.flatten([1]))] == [[1]] + + interpret = fn x -> Macro.to_string(Code.string_to_quoted!(x)) end assert interpret.("f 1 + g h 2, 3") == "f(1 + g(h(2, 3)))" + assert interpret.("assert [] = TestRepo.all from p in Post, where: p.title in ^[]") == - "assert([] = TestRepo.all(from(p in Post, where: p.title() in ^[])))" + "assert([] = TestRepo.all(from(p in Post, where: p.title() in ^[])))" end test "syntax error on atom dot alias" do - msg = "nofile:1: atom cannot be followed by an alias. If the '.' was meant to be " <> - "part of the atom's name, the atom name must be quoted. Syntax error before: '.'" + msg = + "nofile:1: atom cannot be followed by an alias. If the '.' was meant to be " <> + "part of the atom's name, the atom name must be quoted. Syntax error before: '.'" - assert_eval_raise SyntaxError, msg, ':foo.Bar' - assert_eval_raise SyntaxError, msg, ':"foo".Bar' + assert_eval_raise(SyntaxError, msg, ':foo.Bar') + assert_eval_raise(SyntaxError, msg, ':"foo".Bar') end test "syntax error with no token" do - assert_eval_raise TokenMissingError, + assert_eval_raise( + TokenMissingError, "nofile:1: missing terminator: ) (for \"(\" starting at line 1)", 'case 1 (' + ) end test "clause with defaults" do - assert_eval_raise CompileError, + assert_eval_raise( + CompileError, ~r"nofile:3: definitions with multiple clauses and default values require a header", ~C''' defmodule Kernel.ErrorsTest.ClauseWithDefaults1 do @@ -225,28 +243,26 @@ defmodule Kernel.ErrorsTest do def hello(arg \\ 1), do: nil end ''' + ) - assert_eval_raise CompileError, - ~r"nofile:2: undefined function foo/0", - ~C''' - defmodule Kernel.ErrorsTest.ClauseWithDefaults3 do - def hello(foo, bar \\ foo()) - def hello(foo, bar), do: foo + bar - end - ''' + assert_eval_raise(CompileError, ~r"nofile:2: undefined function foo/0", ~C''' + defmodule Kernel.ErrorsTest.ClauseWithDefaults3 do + def hello(foo, bar \\ foo()) + def hello(foo, bar), do: foo + bar + end + ''') end test "different defs with defaults" do - assert_eval_raise CompileError, - "nofile:3: def hello/3 defaults conflicts with hello/2", - ~C''' - defmodule Kernel.ErrorsTest.DifferentDefsWithDefaults1 do - def hello(a, b \\ nil), do: a + b - def hello(a, b \\ nil, c \\ nil), do: a + b + c - end - ''' + assert_eval_raise(CompileError, "nofile:3: def hello/3 defaults conflicts with hello/2", ~C''' + defmodule Kernel.ErrorsTest.DifferentDefsWithDefaults1 do + def hello(a, b \\ nil), do: a + b + def hello(a, b \\ nil, c \\ nil), do: a + b + c + end + ''') - assert_eval_raise CompileError, + assert_eval_raise( + CompileError, "nofile:3: def hello/2 conflicts with defaults from hello/3", ~C''' defmodule Kernel.ErrorsTest.DifferentDefsWithDefaults2 do @@ -254,63 +270,66 @@ defmodule Kernel.ErrorsTest do def hello(a, b \\ nil), do: a + b end ''' + ) end test "bad form" do - assert_eval_raise CompileError, - "nofile:2: undefined function bar/0", - ''' - defmodule Kernel.ErrorsTest.BadForm do - def foo, do: bar() - end - ''' + assert_eval_raise(CompileError, "nofile:2: undefined function bar/0", ''' + defmodule Kernel.ErrorsTest.BadForm do + def foo, do: bar() + end + ''') end test "literal on map and struct" do - assert_eval_raise SyntaxError, - "nofile:1: syntax error before: '}'", - '%{{:a, :b}}' + assert_eval_raise(SyntaxError, "nofile:1: syntax error before: '}'", '%{{:a, :b}}') - assert_eval_raise SyntaxError, - "nofile:1: syntax error before: '{'", - '%{:a, :b}{a: :b}' + assert_eval_raise(SyntaxError, "nofile:1: syntax error before: '{'", '%{:a, :b}{a: :b}') - assert_eval_raise CompileError, + assert_eval_raise( + CompileError, "nofile:1: expected key-value pairs in a map, got: put_in(foo.bar().baz(), nil)", 'foo = 1; %{put_in(foo.bar.baz, nil), :bar}' + ) end test "struct fields on defstruct" do - assert_eval_raise ArgumentError, - "struct field names must be atoms, got: 1", - ''' - defmodule Kernel.ErrorsTest.StructFieldsOnDefstruct do - defstruct [1, 2, 3] - end - ''' + assert_eval_raise(ArgumentError, "struct field names must be atoms, got: 1", ''' + defmodule Kernel.ErrorsTest.StructFieldsOnDefstruct do + defstruct [1, 2, 3] + end + ''') end test "struct access on body" do - assert_eval_raise CompileError, - "nofile:3: cannot access struct Kernel.ErrorsTest.StructAccessOnBody, " <> - "the struct was not yet defined or the struct " <> - "is being accessed in the same context that defines it", + code = ''' defmodule Kernel.ErrorsTest.StructAccessOnBody do defstruct %{name: "Brasilia"} %Kernel.ErrorsTest.StructAccessOnBody{} end ''' + + msg = + "nofile:3: cannot access struct Kernel.ErrorsTest.StructAccessOnBody, " <> + "the struct was not yet defined or the struct " <> + "is being accessed in the same context that defines it" + + assert_eval_raise(CompileError, msg, code) end test "struct errors" do - assert_eval_raise CompileError, + assert_eval_raise( + CompileError, "nofile:1: BadStruct.__struct__/1 is undefined, cannot expand struct BadStruct", '%BadStruct{}' + ) - assert_eval_raise CompileError, + assert_eval_raise( + CompileError, "nofile:1: BadStruct.__struct__/0 is undefined, cannot expand struct BadStruct", '%BadStruct{} = %{}' + ) defmodule BadStruct do def __struct__ do @@ -318,39 +337,39 @@ defmodule Kernel.ErrorsTest do end end - assert_eval_raise CompileError, + assert_eval_raise( + CompileError, "nofile:1: expected Kernel.ErrorsTest.BadStruct.__struct__/0 to return a map, got: []", '%#{BadStruct}{} = %{}' + ) defmodule GoodStruct do defstruct name: "john" end - assert_eval_raise KeyError, + assert_eval_raise( + KeyError, "key :age not found in: %Kernel.ErrorsTest.GoodStruct{name: \"john\"}", '%#{GoodStruct}{age: 27}' + ) - assert_eval_raise CompileError, + assert_eval_raise( + CompileError, "nofile:1: unknown key :age for struct Kernel.ErrorsTest.GoodStruct", '%#{GoodStruct}{age: 27} = %{}' + ) end test "name for defmodule" do - assert_eval_raise CompileError, - "nofile:1: invalid module name: 3", - 'defmodule 1 + 2, do: 3' + assert_eval_raise(CompileError, "nofile:1: invalid module name: 3", 'defmodule 1 + 2, do: 3') end test "invalid unquote" do - assert_eval_raise CompileError, - "nofile:1: unquote called outside quote", - 'unquote 1' + assert_eval_raise(CompileError, "nofile:1: unquote called outside quote", 'unquote 1') end test "invalid unquote splicing in oneliners" do - assert_eval_raise ArgumentError, - "unquote_splicing only works inside arguments and block contexts, " <> - "wrap it in parens if you want it to work with one-liners", + code = ''' defmodule Kernel.ErrorsTest.InvalidUnquoteSplicingInOneliners do defmacro oneliner2 do @@ -362,16 +381,21 @@ defmodule Kernel.ErrorsTest do end end ''' + + msg = + "unquote_splicing only works inside arguments and block contexts, " <> + "wrap it in parens if you want it to work with one-liners" + + assert_eval_raise(ArgumentError, msg, code) end test "undefined non-local function" do - assert_eval_raise CompileError, - "nofile:1: undefined function call/2", - 'call foo, do: :foo' + assert_eval_raise(CompileError, "nofile:1: undefined function call/2", 'call foo, do: :foo') end test "invalid attribute" do msg = ~r"cannot inject attribute @foo into function/macro because cannot escape " + assert_raise ArgumentError, msg, fn -> defmodule InvalidAttribute do @foo fn -> nil end @@ -382,6 +406,7 @@ defmodule Kernel.ErrorsTest do test "invalid struct field value" do msg = ~r"invalid value for struct field baz, cannot escape " + assert_raise ArgumentError, msg, fn -> defmodule InvaliadStructFieldValue do defstruct baz: fn -> nil end @@ -391,6 +416,7 @@ defmodule Kernel.ErrorsTest do test "match attribute in module" do msg = "invalid write attribute syntax, you probably meant to use: @foo expression" + assert_raise ArgumentError, msg, fn -> defmodule MatchAttributeInModule do @foo = 42 @@ -399,32 +425,32 @@ defmodule Kernel.ErrorsTest do end test "invalid fn args" do - assert_eval_raise TokenMissingError, + assert_eval_raise( + TokenMissingError, "nofile:1: missing terminator: end (for \"fn\" starting at line 1)", 'fn 1' + ) end test "invalid escape" do - assert_eval_raise TokenMissingError, - "nofile:1: invalid escape \\ at end of file", - '1 \\' + assert_eval_raise(TokenMissingError, "nofile:1: invalid escape \\ at end of file", '1 \\') end test "function local conflict" do - assert_eval_raise CompileError, - "nofile:3: imported Kernel.&&/2 conflicts with local function", + code = ''' defmodule Kernel.ErrorsTest.FunctionLocalConflict do def other, do: 1 && 2 def _ && _, do: :error end ''' + + msg = "nofile:3: imported Kernel.&&/2 conflicts with local function" + assert_eval_raise(CompileError, msg, code) end test "macro local conflict" do - assert_eval_raise CompileError, - "nofile:6: call to local macro &&/2 conflicts with imported Kernel.&&/2, " <> - "please rename the local macro or remove the conflicting import", + code = ''' defmodule Kernel.ErrorsTest.MacroLocalConflict do def hello, do: 1 || 2 @@ -434,23 +460,31 @@ defmodule Kernel.ErrorsTest do def world, do: 1 && 2 end ''' + + msg = + "nofile:6: call to local macro &&/2 conflicts with imported Kernel.&&/2, " <> + "please rename the local macro or remove the conflicting import" + + assert_eval_raise(CompileError, msg, code) end test "macro with undefined local" do - assert_eval_raise UndefinedFunctionError, + code = ''' + defmodule Kernel.ErrorsTest.MacroWithUndefinedLocal do + defmacrop bar, do: unknown(1) + def baz, do: bar() + end + ''' + + msg = "function Kernel.ErrorsTest.MacroWithUndefinedLocal.unknown/1" <> - " is undefined (function unknown/1 is not available)", - ''' - defmodule Kernel.ErrorsTest.MacroWithUndefinedLocal do - defmacrop bar, do: unknown(1) - def baz, do: bar() - end - ''' + " is undefined (function unknown/1 is not available)" + + assert_eval_raise(UndefinedFunctionError, msg, code) end test "private macro" do - assert_eval_raise UndefinedFunctionError, - "function Kernel.ErrorsTest.PrivateMacro.foo/0 is undefined (function foo/0 is not available)", + code = ''' defmodule Kernel.ErrorsTest.PrivateMacro do defmacrop foo, do: 1 @@ -458,11 +492,15 @@ defmodule Kernel.ErrorsTest do defmacro baz, do: bar() end ''' + + msg = + "function Kernel.ErrorsTest.PrivateMacro.foo/0 is undefined (function foo/0 is not available)" + + assert_eval_raise(UndefinedFunctionError, msg, code) end test "function definition with alias" do - assert_eval_raise CompileError, - "nofile:2: function names should start with lowercase characters or underscore, invalid name Bar", + code = ''' defmodule Kernel.ErrorsTest.FunctionDefinitionWithAlias do def Bar do @@ -470,44 +508,53 @@ defmodule Kernel.ErrorsTest do end end ''' + + msg = + "nofile:2: function names should start with lowercase characters or underscore, invalid name Bar" + + assert_eval_raise(CompileError, msg, code) end test "function import conflict" do - assert_eval_raise CompileError, - "nofile:3: function exit/1 imported from both :erlang and Kernel, call is ambiguous", + code = ''' defmodule Kernel.ErrorsTest.FunctionImportConflict do import :erlang, warn: false def foo, do: exit(:test) end ''' + + msg = "nofile:3: function exit/1 imported from both :erlang and Kernel, call is ambiguous" + assert_eval_raise(CompileError, msg, code) end test "unrequired macro" do - assert_eval_raise CompileError, + code = ''' + defmodule Kernel.ErrorsTest.UnrequiredMacro do + Kernel.ErrorsTest.hello() + end + ''' + + msg = "nofile:2: you must require Kernel.ErrorsTest before invoking " <> - "the macro Kernel.ErrorsTest.hello/0", - ''' - defmodule Kernel.ErrorsTest.UnrequiredMacro do - Kernel.ErrorsTest.hello() - end - ''' + "the macro Kernel.ErrorsTest.hello/0" + + assert_eval_raise(CompileError, msg, code) end test "def defmacro clause change" do - assert_eval_raise CompileError, - "nofile:3: defmacro foo/1 already defined as def", - ''' - defmodule Kernel.ErrorsTest.DefDefmacroClauseChange do - def foo(1), do: 1 - defmacro foo(x), do: x - end - ''' + code = ''' + defmodule Kernel.ErrorsTest.DefDefmacroClauseChange do + def foo(1), do: 1 + defmacro foo(x), do: x + end + ''' + + assert_eval_raise(CompileError, "nofile:3: defmacro foo/1 already defined as def", code) end test "def defp clause change from another file" do - assert_eval_raise CompileError, - ~r"nofile:4: def hello/0 already defined as defp", + code = ''' defmodule Kernel.ErrorsTest.DefDefmacroClauseChange do require Kernel.ErrorsTest @@ -515,31 +562,35 @@ defmodule Kernel.ErrorsTest do Kernel.ErrorsTest.hello() end ''' + + assert_eval_raise(CompileError, ~r"nofile:4: def hello/0 already defined as defp", code) end test "internal function overridden" do - assert_eval_raise CompileError, - "nofile:2: cannot define def __info__/1 as it is automatically defined by Elixir", + code = ''' defmodule Kernel.ErrorsTest.InternalFunctionOverridden do def __info__(_), do: [] end ''' + + msg = "nofile:2: cannot define def __info__/1 as it is automatically defined by Elixir" + assert_eval_raise(CompileError, msg, code) end test "no macros" do - assert_eval_raise CompileError, - "nofile:2: could not load macros from module :lists", + code = ''' defmodule Kernel.ErrorsTest.NoMacros do import :lists, only: :macros end ''' + + assert_eval_raise(CompileError, "nofile:2: could not load macros from module :lists", code) end test "invalid macro" do - assert_eval_raise CompileError, - "nofile: invalid quoted expression: {:foo, :bar, :baz, :bat}", + code = ''' defmodule Kernel.ErrorsTest.InvalidMacro do defmacrop oops do @@ -549,29 +600,36 @@ defmodule Kernel.ErrorsTest do def test, do: oops() end ''' + + msg = "nofile: invalid quoted expression: {:foo, :bar, :baz, :bat}" + assert_eval_raise(CompileError, msg, code) end test "unloaded module" do - assert_eval_raise CompileError, + assert_eval_raise( + CompileError, "nofile:1: module Certainly.Doesnt.Exist is not loaded and could not be found", 'import Certainly.Doesnt.Exist' + ) end test "module imported from the context it was defined in" do - assert_eval_raise CompileError, - ~r"nofile:4: module Kernel.ErrorsTest.ScheduledModule.Hygiene is not loaded but was defined.", - ''' - defmodule Kernel.ErrorsTest.ScheduledModule do - defmodule Hygiene do - end - import Kernel.ErrorsTest.ScheduledModule.Hygiene + code = ''' + defmodule Kernel.ErrorsTest.ScheduledModule do + defmodule Hygiene do end - ''' + import Kernel.ErrorsTest.ScheduledModule.Hygiene + end + ''' + + msg = + ~r"nofile:4: module Kernel.ErrorsTest.ScheduledModule.Hygiene is not loaded but was defined." + + assert_eval_raise(CompileError, msg, code) end test "module imported from the same module" do - assert_eval_raise CompileError, - ~r"nofile:3: you are trying to use the module Kernel.ErrorsTest.ScheduledModule.Hygiene which is currently being defined", + code = ''' defmodule Kernel.ErrorsTest.ScheduledModule do defmodule Hygiene do @@ -579,26 +637,37 @@ defmodule Kernel.ErrorsTest do end end ''' + + msg = + ~r"nofile:3: you are trying to use the module Kernel.ErrorsTest.ScheduledModule.Hygiene which is currently being defined" + + assert_eval_raise(CompileError, msg, code) end test "already compiled module" do - assert_eval_raise ArgumentError, + code = 'Module.eval_quoted Record, quote(do: 1), [], file: __ENV__.file' + + msg = "could not call eval_quoted with argument Record " <> - "because the module is already compiled", - 'Module.eval_quoted Record, quote(do: 1), [], file: __ENV__.file' + "because the module is already compiled" + + assert_eval_raise(ArgumentError, msg, code) end test "doc attributes format" do message = "expected the moduledoc attribute to be {line, doc} (where \"doc\" is " <> - "a binary, a boolean, or nil), got: \"Other\"" + "a binary, a boolean, or nil), got: \"Other\"" + assert_raise ArgumentError, message, fn -> defmodule DocAttributesFormat do Module.put_attribute(__MODULE__, :moduledoc, "Other") end end - message = "expected the moduledoc attribute to contain a binary, a boolean, or nil, got: :not_a_binary" + message = + "expected the moduledoc attribute to contain a binary, a boolean, or nil, got: :not_a_binary" + assert_raise ArgumentError, message, fn -> defmodule AtSyntaxDocAttributesFormat do @moduledoc :not_a_binary @@ -607,7 +676,9 @@ defmodule Kernel.ErrorsTest do end test "@on_load attribute format" do - message = "expected the @on_load attribute to be an atom or a {atom, 0} tuple, got: \"not an atom\"" + message = + "expected the @on_load attribute to be an atom or a {atom, 0} tuple, got: \"not an atom\"" + assert_raise ArgumentError, message, fn -> defmodule BadOnLoadAttribute do Module.put_attribute(__MODULE__, :on_load, "not an atom") @@ -616,43 +687,55 @@ defmodule Kernel.ErrorsTest do end test "interpolation error" do - assert_eval_raise SyntaxError, + assert_eval_raise( + SyntaxError, "nofile:1: \"do\" is missing terminator \"end\". unexpected token: \")\" at line 1", '"foo\#{case 1 do )}bar"' + ) end test "in definition module" do - assert_eval_raise CompileError, - "nofile:2: cannot define module Kernel.ErrorsTest.InDefinitionModule " <> - "because it is currently being defined in nofile:1", + code = ''' defmodule Kernel.ErrorsTest.InDefinitionModule do defmodule Elixir.Kernel.ErrorsTest.InDefinitionModule, do: true end ''' + + msg = + "nofile:2: cannot define module Kernel.ErrorsTest.InDefinitionModule " <> + "because it is currently being defined in nofile:1" + + assert_eval_raise(CompileError, msg, code) end test "invalid definition" do - assert_eval_raise CompileError, + assert_eval_raise( + CompileError, "nofile:1: invalid syntax in def 1.(hello)", 'defmodule Kernel.ErrorsTest.InvalidDefinition, do: (def 1.(hello), do: true)' + ) end test "invalid pin in definition" do - assert_eval_raise CompileError, + assert_eval_raise( + CompileError, "nofile:1: cannot use ^hello on function/macro definition as there are no previous variables", 'defmodule Kernel.ErrorsTest.InvalidDefinition, do: (def foo(^hello), do: :ok)' + ) end test "invalid size in bitstrings" do - assert_eval_raise CompileError, + assert_eval_raise( + CompileError, "nofile:1: cannot use ^x outside of match clauses", 'x = 8; <> = <>' + ) end test "end of expression" do # All valid examples - Code.eval_quoted ''' + Code.eval_quoted(''' 1; 2; 3 @@ -676,135 +759,154 @@ defmodule Kernel.ErrorsTest do after ; end - ''' + ''') # All invalid examples - assert_eval_raise SyntaxError, - "nofile:1: syntax error before: ';'", - '1+;\n2' + assert_eval_raise(SyntaxError, "nofile:1: syntax error before: ';'", '1+;\n2') - assert_eval_raise SyntaxError, - "nofile:1: syntax error before: ';'", - 'max(1, ;2)' + assert_eval_raise(SyntaxError, "nofile:1: syntax error before: ';'", 'max(1, ;2)') end test "new line error" do - assert_eval_raise SyntaxError, + assert_eval_raise( + SyntaxError, "nofile:3: unexpectedly reached end of line. The current expression is invalid or incomplete", 'if true do\n foo = [],\n baz\nend' + ) end test "characters literal are printed correctly in syntax errors" do - assert_eval_raise SyntaxError, - "nofile:1: syntax error before: \"?a\"", - ':ok ?a' - assert_eval_raise SyntaxError, - "nofile:1: syntax error before: \"?\\\\s\"", - ':ok ?\\s' - assert_eval_raise SyntaxError, - "nofile:1: syntax error before: \"?す\"" - ':ok ?す' + assert_eval_raise(SyntaxError, "nofile:1: syntax error before: \"?a\"", ':ok ?a') + assert_eval_raise(SyntaxError, "nofile:1: syntax error before: \"?\\\\s\"", ':ok ?\\s') + assert_eval_raise(SyntaxError, "nofile:1: syntax error before: \"?す\"") + ':ok ?す' end test "invalid \"fn do expr end\"" do - assert_eval_raise SyntaxError, + assert_eval_raise( + SyntaxError, "nofile:1: unexpected token \"do\". Anonymous functions are written as:\n\n" <> - " fn pattern -> expression end\n\n" <> - "Syntax error before: do", + " fn pattern -> expression end\n\n" <> "Syntax error before: do", 'fn do :ok end' + ) end test "bodyless function with guard" do - assert_eval_raise CompileError, - "nofile:2: missing :do option in \"def\"", + code = ''' defmodule Kernel.ErrorsTest.BodyessFunctionWithGuard do def foo(n) when is_number(n) end ''' + + assert_eval_raise(CompileError, "nofile:2: missing :do option in \"def\"", code) end test "invalid args for bodyless clause" do - assert_eval_raise CompileError, - ~r"nofile:2: only variables and \\\\ are allowed as arguments in definition header.", + code = ''' defmodule Kernel.ErrorsTest.InvalidArgsForBodylessClause do def foo(nil) def foo(_), do: :ok end ''' + + msg = ~r"nofile:2: only variables and \\\\ are allowed as arguments in definition header." + assert_eval_raise(CompileError, msg, code) end test "typespec errors" do - assert_eval_raise CompileError, - "nofile:2: type foo() undefined", - ''' - defmodule Kernel.ErrorsTest.TypespecErrors1 do - @type omg :: foo - end - ''' + assert_eval_raise(CompileError, "nofile:2: type foo() undefined", ''' + defmodule Kernel.ErrorsTest.TypespecErrors1 do + @type omg :: foo + end + ''') - message = "nofile:2: spec for undefined function omg/0" - assert_eval_raise CompileError, message, - ''' - defmodule Kernel.ErrorsTest.TypespecErrors2 do - @spec omg :: atom - end - ''' + assert_eval_raise(CompileError, "nofile:2: spec for undefined function omg/0", ''' + defmodule Kernel.ErrorsTest.TypespecErrors2 do + @spec omg :: atom + end + ''') end test "bad multi-call" do - assert_eval_raise CompileError, + assert_eval_raise( + CompileError, "nofile:1: invalid argument for alias, expected a compile time atom or alias, got: 42", 'alias IO.{ANSI, 42}' + ) - assert_eval_raise CompileError, + assert_eval_raise( + CompileError, "nofile:1: :as option is not supported by multi-alias call", 'alias Elixir.{Map}, as: Dict' + ) - assert_eval_raise UndefinedFunctionError, + assert_eval_raise( + UndefinedFunctionError, "function List.\"{}\"/1 is undefined or private", '[List.{Chars}, "one"]' + ) end test "macros error stacktrace" do - assert [{:erlang, :+, [1, :foo], _}, - {Kernel.ErrorsTest.MacrosErrorStacktrace, :sample, 1, _} | _] = - rescue_stacktrace(""" - defmodule Kernel.ErrorsTest.MacrosErrorStacktrace do - defmacro sample(num), do: num + :foo - def other, do: sample(1) - end - """) + code = """ + defmodule Kernel.ErrorsTest.MacrosErrorStacktrace do + defmacro sample(num), do: num + :foo + def other, do: sample(1) + end + """ + + expected_stacktrace = + [ + {:erlang, :+, [1, :foo], _}, + {Kernel.ErrorsTest.MacrosErrorStacktrace, :sample, 1, _} | _ + ] + + assert expected_stacktrace = rescue_stacktrace(code) end test "macros function clause stacktrace" do - assert [{__MODULE__, :sample, 1, _} | _] = - rescue_stacktrace(""" - defmodule Kernel.ErrorsTest.MacrosFunctionClauseStacktrace do - import Kernel.ErrorsTest - sample(1) - end - """) + code = """ + defmodule Kernel.ErrorsTest.MacrosFunctionClauseStacktrace do + import Kernel.ErrorsTest + sample(1) + end + """ + + assert [{__MODULE__, :sample, 1, _} | _] = rescue_stacktrace(code) end test "macros interpreted function clause stacktrace" do + code = """ + defmodule Kernel.ErrorsTest.MacrosInterpretedFunctionClauseStacktrace do + defmacro sample(0), do: 0 + def other, do: sample(1) + end + """ + assert [{Kernel.ErrorsTest.MacrosInterpretedFunctionClauseStacktrace, :sample, 1, _} | _] = - rescue_stacktrace(""" - defmodule Kernel.ErrorsTest.MacrosInterpretedFunctionClauseStacktrace do - defmacro sample(0), do: 0 - def other, do: sample(1) - end - """) + rescue_stacktrace(code) end test "macros compiled callback" do - assert [{Kernel.ErrorsTest, :__before_compile__, [%Macro.Env{module: Kernel.ErrorsTest.MacrosCompiledCallback}], _} | _] = - rescue_stacktrace(""" - defmodule Kernel.ErrorsTest.MacrosCompiledCallback do - Module.put_attribute(__MODULE__, :before_compile, Kernel.ErrorsTest) - end - """) + code = """ + defmodule Kernel.ErrorsTest.MacrosCompiledCallback do + Module.put_attribute(__MODULE__, :before_compile, Kernel.ErrorsTest) + end + """ + + expected_stacktrace = [ + { + Kernel.ErrorsTest, + :__before_compile__, + [%Macro.Env{module: Kernel.ErrorsTest.MacrosCompiledCallback}], + _ + } + | _ + ] + + assert expected_stacktrace = rescue_stacktrace(code) end test "failed remote call stacktrace includes file/line info" do @@ -812,24 +914,27 @@ defmodule Kernel.ErrorsTest do bad_remote_call(1) rescue ArgumentError -> - stack = System.stacktrace - assert [{:erlang, :apply, [1, :foo, []], []}, - {__MODULE__, :bad_remote_call, 1, [file: _, line: _]} | _] = stack + stack = System.stacktrace() + + assert [ + {:erlang, :apply, [1, :foo, []], []}, + {__MODULE__, :bad_remote_call, 1, [file: _, line: _]} | _ + ] = stack end end test "def fails when rescue, else or catch don't have clauses" do - assert_eval_raise CompileError, - ~r"expected -> clauses for :else in \"def\"", - """ - defmodule Example do - def foo do - bar() - else - baz() - end + code = """ + defmodule Example do + def foo do + bar() + else + baz() end - """ + end + """ + + assert_eval_raise(CompileError, ~r"expected -> clauses for :else in \"def\"", code) end defp bad_remote_call(x), do: x.foo @@ -860,7 +965,7 @@ defmodule Kernel.ErrorsTest do Code.eval_string(string) nil rescue - _ -> System.stacktrace + _ -> System.stacktrace() end stacktrace || flunk("Expected expression to fail")