Skip to content

Commit db2504e

Browse files
committed
Do not emit invalid code when formatting assocs in maps, closes #9771
1 parent 98c3f5c commit db2504e

File tree

5 files changed

+41
-34
lines changed

5 files changed

+41
-34
lines changed

lib/elixir/lib/code/formatter.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2239,10 +2239,10 @@ defmodule Code.Formatter do
22392239
defp keyword?(rest), do: rest == []
22402240

22412241
defp keyword_key?({:__block__, meta, [atom]}) when is_atom(atom),
2242-
do: meta[:delimiter] != ":"
2242+
do: meta[:format] == :keyword
22432243

22442244
defp keyword_key?({{:., _, [:erlang, :binary_to_atom]}, meta, [{:<<>>, _, _}, :utf8]}),
2245-
do: meta[:delimiter] != ":"
2245+
do: meta[:format] == :keyword
22462246

22472247
defp keyword_key?(_),
22482248
do: false

lib/elixir/lib/macro.ex

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,9 @@ defmodule Macro do
163163
with parens. The `:closing` does not delimit the end of expression if
164164
there are `:do` and `:end` metadata (when `:token_metadata` is true)
165165
* `:column` - the column number of the AST node (when `:columns` is true)
166-
* `:delimiter` - contains the opening delimiter for sigils, strings, atoms,
167-
and charlists as a string (such as `"{"`, `"/"`, `":"`, and the like)
166+
* `:delimiter` - contains the opening delimiter for sigils, strings,
167+
and charlists as a string (such as `"{"`, `"/"`, `"'"`, and the like)
168+
* `:format` - set to `:keyword` when an atom is defined as a keyword
168169
* `:do` - contains metadata about the `do` location in a function call with
169170
`do/end` blocks (when `:token_metadata` is true)
170171
* `:end` - contains metadata about the `end` location in a function call with

lib/elixir/src/elixir_parser.yrl

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -259,9 +259,9 @@ access_expr -> bin_heredoc : build_bin_heredoc('$1').
259259
access_expr -> list_heredoc : build_list_heredoc('$1').
260260
access_expr -> bit_string : '$1'.
261261
access_expr -> sigil : build_sigil('$1').
262-
access_expr -> atom : handle_literal(?exprs('$1'), '$1', delimiter(<<$:>>)).
263-
access_expr -> atom_safe : build_quoted_atom('$1', true, delimiter(<<$:>>)).
264-
access_expr -> atom_unsafe : build_quoted_atom('$1', false, delimiter(<<$:>>)).
262+
access_expr -> atom : handle_literal(?exprs('$1'), '$1').
263+
access_expr -> atom_safe : build_quoted_atom('$1', true, []).
264+
access_expr -> atom_unsafe : build_quoted_atom('$1', false, []).
265265
access_expr -> dot_alias : '$1'.
266266
access_expr -> parens_call : '$1'.
267267

@@ -526,12 +526,12 @@ call_args_parens -> open_paren call_args_parens_base ',' kw_base ',' close_paren
526526

527527
% KV
528528

529-
kw_eol -> kw_identifier : handle_literal(?exprs('$1'), '$1').
530-
kw_eol -> kw_identifier eol : handle_literal(?exprs('$1'), '$1').
531-
kw_eol -> kw_identifier_safe : build_quoted_atom('$1', true, []).
532-
kw_eol -> kw_identifier_safe eol : build_quoted_atom('$1', true, []).
533-
kw_eol -> kw_identifier_unsafe : build_quoted_atom('$1', false, []).
534-
kw_eol -> kw_identifier_unsafe eol : build_quoted_atom('$1', false, []).
529+
kw_eol -> kw_identifier : handle_literal(?exprs('$1'), '$1', [{format, keyword}]).
530+
kw_eol -> kw_identifier eol : handle_literal(?exprs('$1'), '$1', [{format, keyword}]).
531+
kw_eol -> kw_identifier_safe : build_quoted_atom('$1', true, [{format, keyword}]).
532+
kw_eol -> kw_identifier_safe eol : build_quoted_atom('$1', true, [{format, keyword}]).
533+
kw_eol -> kw_identifier_unsafe : build_quoted_atom('$1', false, [{format, keyword}]).
534+
kw_eol -> kw_identifier_unsafe eol : build_quoted_atom('$1', false, [{format, keyword}]).
535535

536536
kw_base -> kw_eol container_expr : [{'$1', '$2'}].
537537
kw_base -> kw_base ',' kw_eol container_expr : [{'$3', '$4'} | '$1'].

lib/elixir/test/elixir/code_formatter/containers_test.exs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,11 @@ defmodule Code.Formatter.ContainersTest do
412412
assert_same map, @medium_length
413413
end
414414

415+
test "preserves user choice in regards to =>" do
416+
assert_same "%{:hello => 1, :world => 2}"
417+
assert_format "%{:true => 1, :false => 2}", "%{true => 1, false => 2}"
418+
end
419+
415420
test "preserves user choice even when it fits" do
416421
assert_same """
417422
%{

lib/elixir/test/elixir/code_test.exs

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -338,37 +338,38 @@ defmodule CodeTest do
338338
assert string_to_quoted.("0xEF") == {:__block__, [token: "0xEF", line: 1], [239]}
339339
assert string_to_quoted.("12.3") == {:__block__, [token: "12.3", line: 1], [12.3]}
340340
assert string_to_quoted.("nil") == {:__block__, [line: 1], [nil]}
341-
assert string_to_quoted.(":one") == {:__block__, [delimiter: ":", line: 1], [:one]}
342-
343-
args = [[{:__block__, [token: "1", line: 1], [1]}]]
341+
assert string_to_quoted.(":one") == {:__block__, [line: 1], [:one]}
342+
343+
assert string_to_quoted.("[one: :two]") == {
344+
:__block__,
345+
[{:closing, [line: 1]}, {:line, 1}],
346+
[
347+
[
348+
{{:__block__, [format: :keyword, line: 1], [:one]},
349+
{:__block__, [line: 1], [:two]}}
350+
]
351+
]
352+
}
344353

345354
assert string_to_quoted.("[1]") ==
346-
{:__block__, [closing: [line: 1], line: 1], args}
347-
348-
args = [
349-
{{:__block__, [delimiter: ":", line: 1], [:ok]},
350-
{:__block__, [delimiter: ":", line: 1], [:test]}}
351-
]
352-
353-
assert string_to_quoted.("{:ok, :test}") ==
354-
{:__block__, [closing: [line: 1], line: 1], args}
355+
{:__block__, [closing: [line: 1], line: 1],
356+
[[{:__block__, [token: "1", line: 1], [1]}]]}
355357

356358
assert string_to_quoted.(~s("""\nhello\n""")) ==
357359
{:__block__, [delimiter: ~s["""], line: 1], ["hello\n"]}
358360

359361
assert string_to_quoted.("'''\nhello\n'''") ==
360362
{:__block__, [delimiter: ~s['''], line: 1], ['hello\n']}
361363

362-
args = [
363-
{:->, [line: 1],
364-
[
365-
[{:__block__, [token: "1", line: 1, closing: [line: 1], line: 1], [1]}],
366-
{:__block__, [delimiter: "\"", line: 1], ["hello"]}
367-
]}
368-
]
369-
370364
assert string_to_quoted.(~s[fn (1) -> "hello" end]) ==
371-
{:fn, [closing: [line: 1], line: 1], args}
365+
{:fn, [closing: [line: 1], line: 1],
366+
[
367+
{:->, [line: 1],
368+
[
369+
[{:__block__, [token: "1", line: 1, closing: [line: 1], line: 1], [1]}],
370+
{:__block__, [delimiter: "\"", line: 1], ["hello"]}
371+
]}
372+
]}
372373
end
373374

374375
test "raises on bad literal" do

0 commit comments

Comments
 (0)