Skip to content

Commit 60642c3

Browse files
committed
Improve error message on do-block in module attr, closes #10544
1 parent 35e4263 commit 60642c3

File tree

3 files changed

+58
-22
lines changed

3 files changed

+58
-22
lines changed

lib/elixir/lib/kernel.ex

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3123,7 +3123,15 @@ defmodule Kernel do
31233123

31243124
not function? and __CALLER__.context == :match ->
31253125
raise ArgumentError,
3126-
"invalid write attribute syntax, you probably meant to use: @#{name} expression"
3126+
"""
3127+
invalid write attribute syntax. If you want to define an attribute, don't do this:
3128+
3129+
@foo = :value
3130+
3131+
Instead, do this:
3132+
3133+
@foo :value
3134+
"""
31273135

31283136
# Typespecs attributes are currently special cased by the compiler
31293137
is_list(args) and typespec?(name) ->
@@ -3219,7 +3227,21 @@ defmodule Kernel do
32193227
end
32203228
end
32213229

3222-
# All other cases
3230+
# Error cases
3231+
defp do_at([{call, meta, ctx_or_args}, [{:do, _} | _] = kw], _meta, name, _function?, _env) do
3232+
args = if is_atom(ctx_or_args), do: [], else: ctx_or_args
3233+
code = "\n@#{name} (#{Macro.to_string({call, meta, args ++ [kw]})})"
3234+
3235+
raise ArgumentError, """
3236+
expected 0 or 1 argument for @#{name}, got 2.
3237+
3238+
It seems you are trying to use the do-syntax with @module attributes \
3239+
but the do-block is binding to the attribute name. You probably want \
3240+
to wrap the argument value in parentheses, like this:
3241+
#{String.replace(code, "\n", "\n ")}
3242+
"""
3243+
end
3244+
32233245
defp do_at(args, _meta, name, _function?, _env) do
32243246
raise ArgumentError, "expected 0 or 1 argument for @#{name}, got: #{length(args)}"
32253247
end

lib/elixir/test/elixir/kernel/errors_test.exs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -692,16 +692,6 @@ defmodule Kernel.ErrorsTest do
692692
end
693693
end
694694

695-
test "match attribute in module" do
696-
msg = "invalid write attribute syntax, you probably meant to use: @foo expression"
697-
698-
assert_raise ArgumentError, msg, fn ->
699-
defmodule MatchAttributeInModule do
700-
@foo = 42
701-
end
702-
end
703-
end
704-
705695
test "invalid case clauses" do
706696
assert_eval_raise CompileError,
707697
"nofile:1: expected one argument for :do clauses (->) in \"case\"",

lib/elixir/test/elixir/kernel_test.exs

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -592,16 +592,6 @@ defmodule KernelTest do
592592
_ = a
593593
end
594594

595-
test "setting attribute with uppercase" do
596-
message = "module attributes set via @ cannot start with an uppercase letter"
597-
598-
assert_raise ArgumentError, message, fn ->
599-
defmodule UpcaseAttrSample do
600-
@Upper
601-
end
602-
end
603-
end
604-
605595
test "in module body" do
606596
defmodule InSample do
607597
@foo [:a, :b]
@@ -782,6 +772,40 @@ defmodule KernelTest do
782772
end
783773
end
784774

775+
describe "@" do
776+
test "setting attribute with do-block" do
777+
exception =
778+
catch_error(
779+
defmodule UpcaseAttrSample do
780+
@foo quote do
781+
:ok
782+
end
783+
end
784+
)
785+
786+
assert exception.message =~ "expected 0 or 1 argument for @foo, got 2"
787+
assert exception.message =~ "You probably want to wrap the argument value in parentheses"
788+
end
789+
790+
test "setting attribute with uppercase" do
791+
message = "module attributes set via @ cannot start with an uppercase letter"
792+
793+
assert_raise ArgumentError, message, fn ->
794+
defmodule UpcaseAttrSample do
795+
@Upper
796+
end
797+
end
798+
end
799+
800+
test "matching attribute" do
801+
assert_raise ArgumentError, ~r"invalid write attribute syntax", fn ->
802+
defmodule MatchAttributeInModule do
803+
@foo = 42
804+
end
805+
end
806+
end
807+
end
808+
785809
describe "defdelegate" do
786810
defdelegate my_flatten(list), to: List, as: :flatten
787811

0 commit comments

Comments
 (0)