1818 line = false ,
1919 file = nil ,
2020 context = nil ,
21- op = escape , % escape | escape_and_prune | quote
21+ op = escape , % escape | escape_and_prune | {struct, Module} | quote
2222 aliases_hygiene = nil ,
2323 imports_hygiene = nil ,
2424 unquote = true ,
@@ -140,15 +140,24 @@ do_tuple_linify(Fun, Meta, Left, Right, Var) ->
140140% % Escapes the given expression. It is similar to quote, but
141141% % lines are kept and hygiene mechanisms are disabled.
142142escape (Expr , Op , Unquote ) ->
143- Q = # elixir_quote {
144- line = true ,
145- file = nil ,
146- op = Op ,
147- unquote = Unquote
148- },
149- case Unquote of
150- true -> do_quote (Expr , Q );
151- false -> do_escape (Expr , Q )
143+ try
144+ Q = # elixir_quote {
145+ line = true ,
146+ file = nil ,
147+ op = Op ,
148+ unquote = Unquote
149+ },
150+ case Unquote of
151+ true -> do_quote (Expr , Q );
152+ false -> do_escape (Expr , Q )
153+ end
154+ catch
155+ Kind :Reason :Stacktrace ->
156+ Pruned = lists :dropwhile (fun
157+ ({? MODULE , _ , _ , _ }) -> true ;
158+ (_ ) -> false
159+ end , Stacktrace ),
160+ erlang :raise (Kind , Reason , Pruned )
152161 end .
153162
154163do_escape ({Left , Meta , Right }, # elixir_quote {op = escape_and_prune } = Q ) when is_list (Meta ) ->
@@ -176,13 +185,24 @@ do_escape(Map, Q) when is_map(Map) ->
176185 maybe
177186 #{'__struct__' := Module } ?= Map ,
178187 true ?= is_atom (Module ),
188+ % We never escape ourselves (it can only happen during Elixir bootstrapping)
189+ true ?= (Q # elixir_quote .op /= {struct , Module }),
179190 {module , Module } ?= code :ensure_loaded (Module ),
180191 true ?= erlang :function_exported (Module , '__escape__' , 1 ),
181- Expr = Module :'__escape__' (Map ),
182- case shallow_valid_ast (Expr ) of
183- true -> Expr ;
184- false -> argument_error (
185- <<('Elixir.Kernel' :inspect (Module ))/binary , " .__escape__/1 returned invalid AST: " , ('Elixir.Kernel' :inspect (Expr ))/binary >>)
192+ case Q # elixir_quote .op of
193+ {struct , _Module } ->
194+ argument_error (<<('Elixir.Kernel' :inspect (Module ))/binary ,
195+ " defines custom escaping rules which are not supported in struct defaults" ,
196+ (bad_escape_hint ())/binary >>);
197+
198+ _ ->
199+ Expr = Module :'__escape__' (Map ),
200+ case shallow_valid_ast (Expr ) of
201+ true -> Expr ;
202+ false -> argument_error (
203+ <<('Elixir.Kernel' :inspect (Module ))/binary , " .__escape__/1 returned invalid AST: " , ('Elixir.Kernel' :inspect (Expr ))/binary >>
204+ )
205+ end
186206 end
187207 else
188208 _ ->
@@ -234,8 +254,8 @@ escape_map_key_value(K, V, Map, Q) ->
234254 if
235255 is_reference (MaybeRef ) ->
236256 argument_error (<<('Elixir.Kernel' :inspect (Map , []))/binary , " contains a reference (" ,
237- ('Elixir.Kernel' :inspect (MaybeRef , []))/binary , " ) and therefore it cannot be escaped " ,
238- " (it must be defined within a function instead). " , (bad_escape_hint ())/binary >>);
257+ ('Elixir.Kernel' :inspect (MaybeRef , []))/binary , " ) and therefore it cannot be escaped" ,
258+ (bad_escape_hint ())/binary >>);
239259 true ->
240260 {do_escape (K , Q ), do_escape (V , Q )}
241261 end .
@@ -248,11 +268,11 @@ find_tuple_ref(Tuple, Index) ->
248268 end .
249269
250270bad_escape (Arg ) ->
251- argument_error (<<" cannot escape " , ('Elixir.Kernel' :inspect (Arg , []))/binary , " . " ,
271+ argument_error (<<" cannot escape " , ('Elixir.Kernel' :inspect (Arg , []))/binary ,
252272 (bad_escape_hint ())/binary >>).
253273
254274bad_escape_hint () ->
255- <<" The supported values are: lists, tuples, maps, atoms, numbers, bitstrings, " ,
275+ <<" . The supported values are: lists, tuples, maps, atoms, numbers, bitstrings, " ,
256276 " PIDs and remote functions in the format &Mod.fun/arity" >>.
257277
258278% % Quote entry points
0 commit comments