Skip to content

Commit 85b1ad0

Browse files
committed
Do not emit warnings during IEx parsing
Closes #15090. Unfortunately, I could not write a test for this as the error only happens when using actual I/O devices. When using the mock device we use in tests, the warning appears as expected.
1 parent 23633f7 commit 85b1ad0

7 files changed

Lines changed: 70 additions & 59 deletions

File tree

lib/elixir/lib/code.ex

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,14 +1325,7 @@ defmodule Code do
13251325
file = Keyword.get(opts, :file, "nofile")
13261326
line = Keyword.get(opts, :line, 1)
13271327
column = Keyword.get(opts, :column, 1)
1328-
1329-
case :elixir.string_to_tokens(to_charlist(string), line, column, file, opts) do
1330-
{:ok, tokens} ->
1331-
:elixir.tokens_to_quoted(tokens, file, opts)
1332-
1333-
{:error, _error_msg} = error ->
1334-
error
1335-
end
1328+
:elixir.string_to_quoted(to_charlist(string), line, column, file, opts)
13361329
end
13371330

13381331
@doc """
@@ -1408,8 +1401,7 @@ defmodule Code do
14081401
Process.put(:code_formatter_comments, [])
14091402
opts = [preserve_comments: &preserve_comments/5] ++ opts
14101403

1411-
with {:ok, tokens} <- :elixir.string_to_tokens(charlist, line, column, file, opts),
1412-
{:ok, forms} <- :elixir.tokens_to_quoted(tokens, file, opts) do
1404+
with {:ok, forms} <- :elixir.string_to_quoted(charlist, line, column, file, opts) do
14131405
comments = Enum.reverse(Process.get(:code_formatter_comments))
14141406
{:ok, forms, comments}
14151407
end

lib/elixir/lib/code/fragment.ex

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,7 +1241,7 @@ defmodule Code.Fragment do
12411241
def container_cursor_to_quoted(fragment, opts \\ []) do
12421242
{trailing_fragment, opts} = Keyword.pop(opts, :trailing_fragment)
12431243
opts = Keyword.take(opts, [:columns, :token_metadata, :literal_encoder])
1244-
opts = [check_terminators: {:cursor, []}, emit_warnings: false] ++ opts
1244+
opts = [check_terminators: {:cursor, []}] ++ opts
12451245

12461246
file = Keyword.get(opts, :file, "nofile")
12471247
line = Keyword.get(opts, :line, 1)
@@ -1261,7 +1261,10 @@ defmodule Code.Fragment do
12611261
end
12621262

12631263
tokens = reverse_tokens(line, column, rev_tokens, rev_terminators)
1264-
:elixir.tokens_to_quoted(tokens, file, opts)
1264+
1265+
with {:ok, forms, _warnings} <- :elixir.tokens_to_quoted(tokens, file, opts) do
1266+
{:ok, forms}
1267+
end
12651268

12661269
{:ok, line, column, _warnings, rev_tokens, rev_terminators} ->
12671270
tokens =
@@ -1288,10 +1291,12 @@ defmodule Code.Fragment do
12881291
_ -> reverse_tokens(line, column, rev_tokens, rev_terminators)
12891292
end
12901293

1291-
:elixir.tokens_to_quoted(tokens, file, opts)
1294+
with {:ok, forms, _warnings} <- :elixir.tokens_to_quoted(tokens, file, opts) do
1295+
{:ok, forms}
1296+
end
12921297

12931298
{:error, info, _rest, _warnings, _so_far} ->
1294-
{:error, :elixir.format_token_error(info)}
1299+
{:error, :elixir_tokenizer.format_error(info)}
12951300
end
12961301
end
12971302

lib/elixir/src/elixir.erl

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
-export([start_cli/0, start/0]).
1010
-export([start/2, stop/1, config_change/3]).
1111
-export([
12-
string_to_tokens/5, tokens_to_quoted/3, 'string_to_quoted!'/5,
12+
string_to_tokens/5, tokens_to_quoted/3, string_to_quoted/5, 'string_to_quoted!'/5,
1313
env_for_eval/1, quoted_to_erl/2, eval_forms/3, eval_quoted/3,
1414
eval_quoted/4, eval_local_handler/2, eval_external_handler/3,
15-
format_token_error/1
15+
emit_warnings/3
1616
]).
1717
-include("elixir.hrl").
1818
-define(system, 'Elixir.System').
@@ -445,38 +445,34 @@ quoted_to_erl(Quoted, ErlS, ExS, Env) ->
445445

446446
string_to_tokens(String, StartLine, StartColumn, File, Opts) when is_integer(StartLine), is_binary(File) ->
447447
case elixir_tokenizer:tokenize(String, StartLine, StartColumn, Opts) of
448-
{ok, _Line, _Column, [], Tokens, []} ->
449-
{ok, lists:reverse(Tokens)};
450448
{ok, _Line, _Column, Warnings, Tokens, Terminators} ->
451-
(lists:keyfind(emit_warnings, 1, Opts) /= {emit_warnings, false}) andalso
452-
[elixir_errors:erl_warn(L, File, M) || {L, M} <- lists:reverse(Warnings)],
453-
{ok, lists:reverse(Tokens, Terminators)};
449+
{ok, lists:reverse(Tokens, Terminators), Warnings};
454450
{error, Info, _Rest, _Warnings, _SoFar} ->
455-
{error, format_token_error(Info)}
451+
{error, elixir_tokenizer:format_error(Info)}
456452
end.
457453

458-
format_token_error({Location, {ErrorPrefix, ErrorSuffix}, Token}) ->
459-
{Location, {to_binary(ErrorPrefix), to_binary(ErrorSuffix)}, to_binary(Token)};
460-
format_token_error({Location, Error, Token}) ->
461-
{Location, to_binary(Error), to_binary(Token)}.
462-
463-
tokens_to_quoted(Tokens, WarningFile, Opts) ->
464-
handle_parsing_opts(WarningFile, Opts),
454+
tokens_to_quoted(Tokens, _WarningFile, Opts) ->
455+
put_parsing_state(Opts),
465456

466457
try elixir_parser:parse(Tokens) of
467458
{ok, Forms} ->
468-
{ok, Forms};
459+
{ok, Forms, get(elixir_parser_warnings)};
469460
{error, {Line, _, [{ErrorPrefix, ErrorSuffix}, Token]}} ->
470461
{error, {parser_location(Line), {to_binary(ErrorPrefix), to_binary(ErrorSuffix)}, to_binary(Token)}};
471462
{error, {Line, _, [Error, Token]}} ->
472463
{error, {parser_location(Line), to_binary(Error), to_binary(Token)}}
473464
after
474-
erase(elixir_parser_warning_file),
465+
erase(elixir_parser_warnings),
475466
erase(elixir_parser_columns),
476467
erase(elixir_token_metadata),
477468
erase(elixir_literal_encoder)
478469
end.
479470

471+
emit_warnings(Warnings, File, Opts) ->
472+
(Warnings /= []) andalso
473+
(lists:keyfind(emit_warnings, 1, Opts) /= {emit_warnings, false}) andalso
474+
[elixir_errors:erl_warn(L, File, M) || {L, M} <- lists:reverse(Warnings)].
475+
480476
parser_location({Line, Column, _}) ->
481477
[{line, Line}, {column, Column}];
482478
parser_location(Meta) ->
@@ -491,17 +487,28 @@ parser_location(Meta) ->
491487
false -> [{line, Line}]
492488
end.
493489

494-
'string_to_quoted!'(String, StartLine, StartColumn, File, Opts) ->
490+
string_to_quoted(String, StartLine, StartColumn, File, Opts) ->
495491
case string_to_tokens(String, StartLine, StartColumn, File, Opts) of
496-
{ok, Tokens} ->
492+
{ok, Tokens, Warnings1} ->
493+
emit_warnings(Warnings1, File, Opts),
494+
497495
case tokens_to_quoted(Tokens, File, Opts) of
498-
{ok, Forms} ->
499-
Forms;
500-
{error, {Meta, Error, Token}} ->
501-
Indentation = proplists:get_value(indentation, Opts, 0),
502-
Input = {String, StartLine, StartColumn, Indentation},
503-
elixir_errors:parse_error(Meta, File, Error, Token, Input)
496+
{ok, Forms, Warnings2} ->
497+
emit_warnings(Warnings2, File, Opts),
498+
{ok, Forms};
499+
500+
{error, Error} ->
501+
{error, Error}
504502
end;
503+
504+
{error, Error} ->
505+
{error, Error}
506+
end.
507+
508+
'string_to_quoted!'(String, StartLine, StartColumn, File, Opts) ->
509+
case string_to_quoted(String, StartLine, StartColumn, File, Opts) of
510+
{ok, Forms} ->
511+
Forms;
505512
{error, {Meta, Error, Token}} ->
506513
Indentation = proplists:get_value(indentation, Opts, 0),
507514
Input = {String, StartLine, StartColumn, Indentation},
@@ -511,20 +518,15 @@ parser_location(Meta) ->
511518
to_binary(List) when is_list(List) -> elixir_utils:characters_to_binary(List);
512519
to_binary(Atom) when is_atom(Atom) -> atom_to_binary(Atom).
513520

514-
handle_parsing_opts(File, Opts) ->
515-
WarningFile =
516-
case lists:keyfind(emit_warnings, 1, Opts) of
517-
{emit_warnings, false} -> nil;
518-
_ -> File
519-
end,
521+
put_parsing_state(Opts) ->
520522
LiteralEncoder =
521523
case lists:keyfind(literal_encoder, 1, Opts) of
522524
{literal_encoder, Fun} -> Fun;
523525
false -> false
524526
end,
525527
TokenMetadata = lists:keyfind(token_metadata, 1, Opts) == {token_metadata, true},
526528
Columns = lists:keyfind(columns, 1, Opts) == {columns, true},
527-
put(elixir_parser_warning_file, WarningFile),
529+
put(elixir_parser_warnings, []),
528530
put(elixir_parser_columns, Columns),
529531
put(elixir_token_metadata, TokenMetadata),
530532
put(elixir_literal_encoder, LiteralEncoder).

lib/elixir/src/elixir_parser.yrl

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,7 +1346,4 @@ warn_empty_stab_clause({stab_op, {Line, Column, _}, '->'}) ->
13461346
).
13471347

13481348
warn(LineColumn, Message) ->
1349-
case get(elixir_parser_warning_file) of
1350-
nil -> ok;
1351-
File -> elixir_errors:erl_warn(LineColumn, File, Message)
1352-
end.
1349+
put(elixir_parser_warnings, [{LineColumn, Message} | get(elixir_parser_warnings)]).

lib/elixir/src/elixir_tokenizer.erl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
-module(elixir_tokenizer).
66
-include("elixir.hrl").
77
-include("elixir_tokenizer.hrl").
8-
-export([tokenize/1, tokenize/3, tokenize/4, invalid_do_error/1, terminator/1]).
8+
-export([tokenize/1, tokenize/3, tokenize/4, invalid_do_error/1,
9+
format_error/1, terminator/1]).
910

1011
-define(at_op(T),
1112
T =:= $@).
@@ -1895,6 +1896,11 @@ maybe_unicode_lint_warnings(_Ascii=true, _Tokens, Warnings) ->
18951896
error(Reason, Rest, #elixir_tokenizer{warnings=Warnings}, Tokens) ->
18961897
{error, Reason, Rest, Warnings, Tokens}.
18971898

1899+
format_error({Location, {ErrorPrefix, ErrorSuffix}, Token}) ->
1900+
{Location, {elixir_utils:characters_to_binary(ErrorPrefix), elixir_utils:characters_to_binary(ErrorSuffix)}, elixir_utils:characters_to_binary(Token)};
1901+
format_error({Location, Error, Token}) ->
1902+
{Location, elixir_utils:characters_to_binary(Error), elixir_utils:characters_to_binary(Token)}.
1903+
18981904
%% Cursor handling
18991905

19001906
add_cursor(_Line, Column, noprune, Terminators, Tokens) ->

lib/iex/lib/iex/evaluator.ex

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,10 @@ defmodule IEx.Evaluator do
8282
:elixir_errors.parse_error([line: line], file, "incomplete expression", "", triplet)
8383
else
8484
result =
85-
with {:ok, tokens} <- :elixir.string_to_tokens(input, line, column, file, opts),
85+
with {:ok, tokens, warnings1} <- :elixir.string_to_tokens(input, line, column, file, opts),
8686
{:ok, adjusted_tokens, adjusted_op} <-
8787
adjust_operator(tokens, line, column, file, opts, last_op),
88-
{:ok, forms} <- :elixir.tokens_to_quoted(adjusted_tokens, file, opts) do
88+
{:ok, forms, warnings2} <- :elixir.tokens_to_quoted(adjusted_tokens, file, opts) do
8989
last_op =
9090
case forms do
9191
{:=, _, [_, _]} -> :match
@@ -102,7 +102,12 @@ defmodule IEx.Evaluator do
102102
forms
103103
end
104104

105-
{:ok, forms, last_op}
105+
callback = fn ->
106+
:elixir.emit_warnings(warnings1 ++ warnings2, file, opts)
107+
forms
108+
end
109+
110+
{:ok, callback, last_op}
106111
end
107112

108113
case result do
@@ -134,7 +139,7 @@ defmodule IEx.Evaluator do
134139

135140
defp adjust_operator([{op_type, _, _} | _] = tokens, line, column, file, opts, _last_op)
136141
when op_type in @op_tokens do
137-
{:ok, prefix} = :elixir.string_to_tokens(~c"v(-1)", line, column, file, opts)
142+
{:ok, prefix, _warnings} = :elixir.string_to_tokens(~c"v(-1)", line, column, file, opts)
138143
{:ok, prefix ++ tokens, op_type}
139144
end
140145

lib/iex/lib/iex/server.ex

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,9 @@ defmodule IEx.Server do
138138

139139
defp wait_input(state, evaluator, evaluator_ref, input) do
140140
receive do
141-
{:io_reply, ^input, {:ok, code, parser_state}} ->
141+
{:io_reply, ^input, {:ok, code_fun, parser_state}} ->
142142
:io.setopts(expand_fun: fn _ -> {:yes, [], []} end)
143-
send(evaluator, {:eval, self(), code, state.counter})
143+
send(evaluator, {:eval, self(), code_fun.(), state.counter})
144144
wait_eval(%{state | parser_state: parser_state}, evaluator, evaluator_ref)
145145

146146
{:io_reply, ^input, :eof} ->
@@ -400,8 +400,12 @@ defmodule IEx.Server do
400400
args = [chars, [line: counter, file: "iex"], parser_state | args]
401401

402402
case apply(parser_module, parser_fun, args) do
403-
{:ok, forms, parser_state} -> {:done, {:ok, forms, parser_state}, []}
404-
{:incomplete, parser_state} -> {:more, {counter, parser_state, mfa}}
403+
{:ok, forms, parser_state} ->
404+
forms = if is_function(forms, 0), do: forms, else: fn -> forms end
405+
{:done, {:ok, forms, parser_state}, []}
406+
407+
{:incomplete, parser_state} ->
408+
{:more, {counter, parser_state, mfa}}
405409
end
406410
catch
407411
kind, error ->

0 commit comments

Comments
 (0)