44% % Note that this is also called by the Erlang backend, so we also support
55% % the line number to be none (as it may happen in some erlang errors).
66-module (elixir_errors ).
7- -export ([compile_error /3 , compile_error /4 , form_error /4 , parse_error /4 ]).
7+ -export ([compile_error /3 , compile_error /4 , form_error /4 , parse_error /5 ]).
88-export ([warning_prefix /0 , erl_warn /3 , print_warning /3 , log_and_print_warning /4 , form_warn /4 ]).
99-include (" elixir.hrl" ).
1010-type location () :: non_neg_integer () | {non_neg_integer (), non_neg_integer ()}.
@@ -82,24 +82,36 @@ compile_error(Meta, File, Format, Args) when is_list(Format) ->
8282 compile_error (Meta , File , io_lib :format (Format , Args )).
8383
8484% % Tokenization parsing/errors.
85+ snippet (InputString , Location , StartLine , StartColumn ) ->
86+ {line , Line } = lists :keyfind (line , 1 , Location ),
87+ case lists :keyfind (column , 1 , Location ) of
88+ {column , Column } ->
89+ Lines = string :split (InputString , " \n " , all ),
90+ Snippet = elixir_utils :characters_to_binary (lists :nth (Line - StartLine + 1 , Lines )),
91+ #{content => Snippet , offset => (Column - StartColumn )};
92+
93+ false ->
94+ nil
95+ end .
8596
8697-spec parse_error (elixir :keyword (), binary () | {binary (), binary ()},
87- binary (), binary ()) -> no_return ().
88- parse_error (Location , File , Error , <<>>) ->
98+ binary (), binary (), { list (), integer (), integer ()} ) -> no_return ().
99+ parse_error (Location , File , Error , <<>>, { InputString , StartLine , StartColumn } ) ->
89100 Message = case Error of
90101 <<" syntax error before: " >> -> <<" syntax error: expression is incomplete" >>;
91- _ -> Error
102+ _ -> << Error / binary >>
92103 end ,
93- raise (Location , File , 'Elixir.TokenMissingError' , Message );
104+ Snippet = snippet (InputString , Location , StartLine , StartColumn ),
105+ raise (Location , File , 'Elixir.TokenMissingError' , Message , Snippet );
94106
95107% % Show a nicer message for end of line
96- parse_error (Location , File , <<" syntax error before: " >>, <<" eol" >>) ->
108+ parse_error (Location , File , <<" syntax error before: " >>, <<" eol" >>, _Input ) ->
97109 raise (Location , File , 'Elixir.SyntaxError' ,
98110 <<" unexpectedly reached end of line. The current expression is invalid or incomplete" >>);
99111
100112
101113% % Show a nicer message for keywords pt1 (Erlang keywords show up wrapped in single quotes)
102- parse_error (Location , File , <<" syntax error before: " >>, Keyword )
114+ parse_error (Location , File , <<" syntax error before: " >>, Keyword , _Input )
103115 when Keyword == <<" 'not'" >>;
104116 Keyword == <<" 'and'" >>;
105117 Keyword == <<" 'or'" >>;
@@ -110,7 +122,7 @@ parse_error(Location, File, <<"syntax error before: ">>, Keyword)
110122 raise_reserved (Location , File , binary_part (Keyword , 1 , byte_size (Keyword ) - 2 ));
111123
112124% % Show a nicer message for keywords pt2 (Elixir keywords show up as is)
113- parse_error (Location , File , <<" syntax error before: " >>, Keyword )
125+ parse_error (Location , File , <<" syntax error before: " >>, Keyword , _Input )
114126 when Keyword == <<" fn" >>;
115127 Keyword == <<" else" >>;
116128 Keyword == <<" rescue" >>;
@@ -121,7 +133,7 @@ parse_error(Location, File, <<"syntax error before: ">>, Keyword)
121133 raise_reserved (Location , File , Keyword );
122134
123135% % Produce a human-readable message for errors before a sigil
124- parse_error (Location , File , <<" syntax error before: " >>, <<" {sigil," , _Rest /binary >> = Full ) ->
136+ parse_error (Location , File , <<" syntax error before: " >>, <<" {sigil," , _Rest /binary >> = Full , _Input ) ->
125137 {sigil , _ , Sigil , [Content | _ ], _ , _ , _ } = parse_erl_term (Full ),
126138 Content2 = case is_binary (Content ) of
127139 true -> Content ;
@@ -131,15 +143,15 @@ parse_error(Location, File, <<"syntax error before: ">>, <<"{sigil,", _Rest/bina
131143 raise (Location , File , 'Elixir.SyntaxError' , Message );
132144
133145% % Binaries (and interpolation) are wrapped in [<<...>>]
134- parse_error (Location , File , Error , <<" [" , _ /binary >> = Full ) when is_binary (Error ) ->
146+ parse_error (Location , File , Error , <<" [" , _ /binary >> = Full , _Input ) when is_binary (Error ) ->
135147 Term = case parse_erl_term (Full ) of
136148 [H | _ ] when is_binary (H ) -> <<$" , H /binary , $" >>;
137149 _ -> <<$" >>
138150 end ,
139151 raise (Location , File , 'Elixir.SyntaxError' , <<Error /binary , Term /binary >>);
140152
141153% % Given a string prefix and suffix to insert the token inside the error message rather than append it
142- parse_error (Location , File , {ErrorPrefix , ErrorSuffix }, Token ) when is_binary (ErrorPrefix ), is_binary (ErrorSuffix ), is_binary (Token ) ->
154+ parse_error (Location , File , {ErrorPrefix , ErrorSuffix }, Token , _Input ) when is_binary (ErrorPrefix ), is_binary (ErrorSuffix ), is_binary (Token ) ->
143155 Message = <<ErrorPrefix /binary , Token /binary , ErrorSuffix /binary >>,
144156 raise (Location , File , 'Elixir.SyntaxError' , Message );
145157
@@ -148,14 +160,15 @@ parse_error(Location, File, {ErrorPrefix, ErrorSuffix}, Token) when is_binary(Er
148160% % because {char, _, _} is a valid Erlang token for an Erlang char literal. We
149161% % want to represent that token as ?a in the error, according to the Elixir
150162% % syntax.
151- parse_error (Location , File , <<" syntax error before: " >>, <<$$ , Char /binary >>) ->
163+ parse_error (Location , File , <<" syntax error before: " >>, <<$$ , Char /binary >>, _Input ) ->
152164 Message = <<" syntax error before: ?" , Char /binary >>,
153165 raise (Location , File , 'Elixir.SyntaxError' , Message );
154166
155167% % Everything else is fine as is
156- parse_error (Location , File , Error , Token ) when is_binary (Error ), is_binary (Token ) ->
157- Message = <<Error /binary , Token /binary >>,
158- raise (Location , File , 'Elixir.SyntaxError' , Message ).
168+ parse_error (Location , File , Error , Token , {InputString , StartLine , StartColumn }) when is_binary (Error ), is_binary (Token ) ->
169+ Message = <<Error /binary , Token /binary >>,
170+ Snippet = snippet (InputString , Location , StartLine , StartColumn ),
171+ raise (Location , File , 'Elixir.SyntaxError' , Message , Snippet ).
159172
160173parse_erl_term (Term ) ->
161174 {ok , Tokens , _ } = erl_scan :string (binary_to_list (Term )),
@@ -196,6 +209,9 @@ meta_location(Meta, File) ->
196209 nil -> [{file , File }, {line , ? line (Meta )}]
197210 end .
198211
212+ raise (Location , File , Kind , Message , Snippet ) when is_binary (File ) ->
213+ raise (Kind , Message , [{file , File }, {snippet , Snippet } | Location ]).
214+
199215raise (Location , File , Kind , Message ) when is_binary (File ) ->
200216 raise (Kind , Message , [{file , File } | Location ]).
201217
0 commit comments