|
1 | 1 | -module(elixir_quote). |
2 | 2 | -export([escape/3, linify/3, linify_with_context_counter/3, build/7, quote/2, has_unquotes/1, fun_to_quoted/1]). |
3 | | --export([dot/5, tail_list/3, list/2, validate_runtime/2]). %% Quote callbacks |
| 3 | +-export([dot/5, tail_list/3, list/2, validate_runtime/2, shallow_validate_ast/1]). %% Quote callbacks |
4 | 4 |
|
5 | 5 | -include("elixir.hrl"). |
6 | 6 | -define(defs(Kind), Kind == def; Kind == defp; Kind == defmacro; Kind == defmacrop; Kind == '@'). |
|
15 | 15 | aliases_hygiene=nil, |
16 | 16 | imports_hygiene=nil, |
17 | 17 | unquote=true, |
18 | | - generated=false |
| 18 | + generated=false, |
| 19 | + shallow_validate=false |
19 | 20 | }). |
20 | 21 |
|
21 | 22 | %% fun_to_quoted |
@@ -215,7 +216,8 @@ build(Meta, Line, File, Context, Unquote, Generated, E) -> |
215 | 216 | file=VFile, |
216 | 217 | unquote=Unquote, |
217 | 218 | context=VContext, |
218 | | - generated=Generated |
| 219 | + generated=Generated, |
| 220 | + shallow_validate=true |
219 | 221 | }, |
220 | 222 |
|
221 | 223 | {Q, VContext, Acc3}. |
@@ -254,6 +256,27 @@ is_valid(context, Context) -> is_atom(Context) andalso (Context /= nil); |
254 | 256 | is_valid(generated, Generated) -> is_boolean(Generated); |
255 | 257 | is_valid(unquote, Unquote) -> is_boolean(Unquote). |
256 | 258 |
|
| 259 | +shallow_validate_ast(Expr) -> |
| 260 | + case shallow_valid_ast(Expr) of |
| 261 | + true -> Expr; |
| 262 | + false -> argument_error( |
| 263 | + <<"tried to unquote invalid AST: ", ('Elixir.Kernel':inspect(Expr))/binary, |
| 264 | + "\nDid you forget to escape term using Macro.escape/1?">>) |
| 265 | + end. |
| 266 | + |
| 267 | +shallow_valid_ast(Expr) when is_list(Expr) -> valid_ast_list(Expr); |
| 268 | +shallow_valid_ast(Expr) -> valid_ast_elem(Expr). |
| 269 | + |
| 270 | +valid_ast_list([]) -> true; |
| 271 | +valid_ast_list([Head | Tail]) -> valid_ast_elem(Head) andalso valid_ast_list(Tail); |
| 272 | +valid_ast_list(_Improper) -> false. |
| 273 | + |
| 274 | +valid_ast_elem(Expr) when is_list(Expr); is_atom(Expr); is_binary(Expr); is_number(Expr); is_pid(Expr) -> true; |
| 275 | +valid_ast_elem({Left, Right}) -> valid_ast_elem(Left) andalso valid_ast_elem(Right); |
| 276 | +valid_ast_elem({Atom, Meta, Args}) when is_atom(Atom), is_list(Meta), is_atom(Args) orelse is_list(Args) -> true; |
| 277 | +valid_ast_elem({Call, Meta, Args}) when is_list(Meta), is_list(Args) -> shallow_valid_ast(Call); |
| 278 | +valid_ast_elem(_Term) -> false. |
| 279 | + |
257 | 280 | quote({unquote_splicing, _, [_]}, #elixir_quote{unquote=true}) -> |
258 | 281 | argument_error(<<"unquote_splicing only works inside arguments and block contexts, " |
259 | 282 | "wrap it in parens if you want it to work with one-liners">>); |
@@ -283,8 +306,12 @@ do_quote({quote, Meta, [Opts, Arg]}, Q) when is_list(Meta) -> |
283 | 306 |
|
284 | 307 | {'{}', [], [quote, meta(NewMeta, Q), [TOpts, TArg]]}; |
285 | 308 |
|
286 | | -do_quote({unquote, Meta, [Expr]}, #elixir_quote{unquote=true}) when is_list(Meta) -> |
287 | | - Expr; |
| 309 | +% |
| 310 | +do_quote({unquote, Meta, [Expr]}, #elixir_quote{unquote=true, shallow_validate=Validate}) when is_list(Meta) -> |
| 311 | + case Validate of |
| 312 | + true -> {{'.', Meta, [?MODULE, shallow_validate_ast]}, Meta, [Expr]}; |
| 313 | + false -> Expr |
| 314 | + end; |
288 | 315 |
|
289 | 316 | %% Aliases |
290 | 317 |
|
|
0 commit comments