diff options
author | José Valim <jose.valim@plataformatec.com.br> | 2017-05-26 22:09:23 +0200 |
---|---|---|
committer | José Valim <jose.valim@plataformatec.com.br> | 2017-05-26 22:09:23 +0200 |
commit | 2e8eb159ca2d9710f2258985cf4f59716fa93b19 (patch) | |
tree | 452afed84d2e3338879e2585bf2c75bbadbe77da | |
parent | 6164e5024266b8c166ae383790071a3e66826e34 (diff) | |
download | elixir-2e8eb159ca2d9710f2258985cf4f59716fa93b19.tar.gz |
Improve error messages
-rw-r--r-- | lib/elixir/src/elixir_tokenizer.erl | 15 | ||||
-rw-r--r-- | lib/elixir/test/elixir/kernel/errors_test.exs | 24 | ||||
-rw-r--r-- | lib/elixir/unicode/tokenizer.ex | 2 |
3 files changed, 28 insertions, 13 deletions
diff --git a/lib/elixir/src/elixir_tokenizer.erl b/lib/elixir/src/elixir_tokenizer.erl index 18914e498..c5b810625 100644 --- a/lib/elixir/src/elixir_tokenizer.erl +++ b/lib/elixir/src/elixir_tokenizer.erl @@ -471,11 +471,11 @@ tokenize(String, Line, Column, Scope, Tokens) -> Reason = {Line, "keyword argument must be followed by space after: ", AtomName}, {error, Reason, String, Tokens}; _ when HasAt -> - Reason = {Line, invalid_character_error($@), atom_to_list(Atom)}, + Reason = {Line, invalid_character_error(Kind, $@), atom_to_list(Atom)}, {error, Reason, String, Tokens}; _ when Kind == alias -> tokenize_alias(Rest, Line, Column, Atom, Length, Ascii, Special, Scope, Tokens); - _ when Kind == var -> + _ when Kind == identifier -> tokenize_other(Rest, Line, Column, Atom, Length, Scope, Tokens); _ -> unexpected_token(String, Line, Column, Tokens) @@ -837,7 +837,7 @@ tokenize([H | T]) when ?is_upcase(H) -> {alias, lists:reverse(Acc), Rest, Length, true, Special}; tokenize([H | T]) when ?is_downcase(H); H == $_ -> {Acc, Rest, Length, Special} = tokenize_continue(T, [H], 1, []), - {var, lists:reverse(Acc), Rest, Length, true, Special}; + {identifier, lists:reverse(Acc), Rest, Length, true, Special}; tokenize(_List) -> {error, empty}. @@ -869,11 +869,12 @@ tokenize_alias(Rest, Line, Column, Atom, Length, Ascii, Special, Scope, Tokens) if not Ascii -> AtomName = atom_to_list(Atom), - Reason = {Line, "aliases must have only ascii characters", AtomName}, + Invalid = hd([C || C <- AtomName, C > 127]), + Reason = {Line, invalid_character_error("alias (only ascii characters are allowed)", Invalid), AtomName}, {error, Reason, AtomName ++ Rest, Tokens}; Special /= [] -> AtomName = atom_to_list(Atom), - Reason = {Line, invalid_character_error(hd(Special)), AtomName}, + Reason = {Line, invalid_character_error("alias", hd(Special)), AtomName}, {error, Reason, AtomName ++ Rest, Tokens}; true -> tokenize(Rest, Line, Column + Length, Scope, [{aliases, {Line, Column, Column + Length}, [Atom]} | Tokens]) @@ -1062,8 +1063,8 @@ keyword('catch') -> block; keyword(_) -> false. -invalid_character_error(Char) -> - io_lib:format("invalid character \"~ts\" (codepoint U+~4.16.0B) in token: ", [[Char], Char]). +invalid_character_error(What, Char) -> + io_lib:format("invalid character \"~ts\" (codepoint U+~4.16.0B) in ~ts: ", [[Char], Char, What]). invalid_do_error(Prefix) -> Prefix ++ ". In case you wanted to write a \"do\" expression, " diff --git a/lib/elixir/test/elixir/kernel/errors_test.exs b/lib/elixir/test/elixir/kernel/errors_test.exs index bc67d627b..ecfffa49f 100644 --- a/lib/elixir/test/elixir/kernel/errors_test.exs +++ b/lib/elixir/test/elixir/kernel/errors_test.exs @@ -38,12 +38,26 @@ defmodule Kernel.ErrorsTest do end test "invalid identifier" do - msg = fn name -> "nofile:1: invalid character \"@\" (codepoint U+0040) in token: #{name}" end + message = fn name -> "nofile:1: invalid character \"@\" (codepoint U+0040) in identifier: #{name}" end + assert_compile_fail SyntaxError, message.("foo@"), 'foo@' + assert_compile_fail SyntaxError, message.("foo@"), 'foo@ ' + assert_compile_fail SyntaxError, message.("foo@bar"), 'foo@bar' - assert_compile_fail SyntaxError, msg.("foo@"), 'foo@' - assert_compile_fail SyntaxError, msg.("foo@"), 'foo@ ' - assert_compile_fail SyntaxError, msg.("foo@bar"), 'foo@bar' - assert_compile_fail SyntaxError, msg.("Foo@"), 'Foo@' + message = fn name -> "nofile:1: invalid character \"@\" (codepoint U+0040) in alias: #{name}" end + assert_compile_fail SyntaxError, message.("Foo@"), 'Foo@' + assert_compile_fail SyntaxError, message.("Foo@bar"), 'Foo@bar' + + message = "nofile:1: invalid character \"!\" (codepoint U+0021) in alias: Foo!" + assert_compile_fail SyntaxError, message, 'Foo!' + + message = "nofile:1: invalid character \"?\" (codepoint U+003F) in alias: Foo?" + assert_compile_fail SyntaxError, message, 'Foo?' + + # TODO: Remove this check once we depend on OTP 20+ + if :erlang.system_info(:otp_release) >= '20' do + message = "invalid character \"ó\" (codepoint U+00F3) in alias (only ascii characters are allowed): Foó" + assert_compile_fail SyntaxError, message, 'Foó' + end end test "invalid fn" do diff --git a/lib/elixir/unicode/tokenizer.ex b/lib/elixir/unicode/tokenizer.ex index d65a8a4b4..a3cf0bd38 100644 --- a/lib/elixir/unicode/tokenizer.ex +++ b/lib/elixir/unicode/tokenizer.ex @@ -140,7 +140,7 @@ defmodule String.Tokenizer do ascii_upper?(head) -> validate(continue(tail, [head], 1, true, []), :alias) ascii_start?(head) -> - validate(continue(tail, [head], 1, true, []), :var) + validate(continue(tail, [head], 1, true, []), :identifier) unicode_start?(head) or unicode_upper?(head) -> validate(continue(tail, [head], 1, false, []), :atom) true -> |