1 use crate::lowerer::Env;
3 use naming_special_names_rust::{classes, expression_trees as et, pseudo_functions};
6 aast_visitor::{visit, AstParams, Node, Visitor},
8 ast::{ClassId, ClassId_, Expr, Expr_, Hint_, Sid, Stmt, Stmt_},
14 /// Rewrite the contents of an expression tree literal into an
15 /// expression on a visitor class.
17 /// Given the following expression tree:
19 /// MyDsl`foo(1) + ${ $x }`;
22 /// First, the splices are extracted and assigned to temporary variables:
24 /// { $0splice0 = $x; }
27 /// Then the expression is virtualized as virtualized_expr
29 /// MyDsl::symbolType(foo<>)(MyDsl::intType())->__plus( ${ $0splice0 } )
31 /// Where virtualized_expr is used in helping type Expression Trees
33 /// Finally, the expression is desugared as runtime_expr
36 /// // At runtime, expression tree visitors know the position of the literal.
37 /// shape('path' => 'whatever.php', 'start_line' => 123, ...),
39 /// // We provide metadata of values used inside the visitor, so users can access
40 /// // spliced or called values without having to re-run the visit method.
42 /// 'splices' => dict['$0splice0' => $0splice0],
43 /// 'functions' => vec[foo<>],
44 /// 'static_methods' => vec[],
49 /// // (ignoring ExprPos arguments for brevity)
51 /// $0v->visitGlobalFunction(foo<>),
52 /// vec[$0v->visitInt(1)],
55 /// $0v->splice('$0splice0', $0splice0),
60 /// Which is the runtime representation of the Expression Tree
61 pub fn desugar<TF>(hint: &aast::Hint, e: Expr, env: &Env<TF>) -> Result<Expr, (Pos, String)> {
62 let visitor_name = hint_name(hint)?;
63 let et_literal_pos = e.1.clone();
65 let mut temps = Temporaries {
67 global_function_pointers: vec![],
68 static_method_pointers: vec![],
70 let (virtual_expr, desugar_expr) = rewrite_expr(&mut temps, e, &visitor_name)?;
72 let splice_count = temps.splices.len();
73 let function_count = temps.global_function_pointers.len();
74 let static_method_count = temps.static_method_pointers.len();
76 let metadata = maketree_metadata(
79 &temps.global_function_pointers,
80 &temps.static_method_pointers,
83 // Make anonymous function of smart constructor calls
84 let visitor_expr = wrap_return(desugar_expr, &et_literal_pos);
85 let visitor_body = ast::FuncBody {
86 fb_ast: vec![visitor_expr],
88 let param = ast::FunParam {
90 type_hint: ast::TypeHint((), Some(hint.clone())),
93 name: visitor_variable(),
95 callconv: ParamKind::Pnormal,
97 user_attributes: vec![],
100 let visitor_fun_ = wrap_fun_(visitor_body, vec![param], et_literal_pos.clone());
101 let visitor_lambda = Expr::new(
103 et_literal_pos.clone(),
104 Expr_::mk_lfun(visitor_fun_, vec![]),
107 // Wrap this in an Efun with appropriate variables for typing.
108 // This enables us to report unbound variables correctly.
109 let virtualized_expr = {
110 let typing_fun_body = ast::FuncBody {
111 fb_ast: vec![wrap_return(virtual_expr, &et_literal_pos)],
113 let typing_fun_ = wrap_fun_(typing_fun_body, vec![], et_literal_pos.clone());
114 let mut spliced_vars: Vec<ast::Lid> = (0..splice_count)
116 .map(|i| ast::Lid(et_literal_pos.clone(), (0, temp_splice_lvar_string(i))))
118 let function_pointer_vars: Vec<ast::Lid> = (0..function_count)
122 et_literal_pos.clone(),
123 (0, temp_function_pointer_lvar_string(i)),
127 let static_method_vars: Vec<ast::Lid> = (0..static_method_count)
131 et_literal_pos.clone(),
132 (0, temp_static_method_lvar_string(i)),
136 spliced_vars.extend(function_pointer_vars);
137 spliced_vars.extend(static_method_vars);
140 et_literal_pos.clone(),
141 Expr_::Call(Box::new((
144 et_literal_pos.clone(),
145 Expr_::mk_efun(typing_fun_, spliced_vars),
154 // Create assignment of the extracted expressions to temporary variables
155 // `$0splice0 = spliced_expr0;`
156 let splice_assignments: Vec<Stmt> = create_temp_statements(temps.splices, temp_splice_lvar);
158 let function_pointer_assignments: Vec<Stmt> =
159 create_temp_statements(temps.global_function_pointers, temp_function_pointer_lvar);
160 // `$0sm0 = Foo::bar<>;`
161 let static_method_assignments: Vec<Stmt> =
162 create_temp_statements(temps.static_method_pointers, temp_static_method_lvar);
164 let mut function_pointers = vec![];
165 function_pointers.extend(function_pointer_assignments);
166 function_pointers.extend(static_method_assignments);
168 let make_tree = static_meth_call(
171 vec![exprpos(&et_literal_pos), metadata, visitor_lambda],
172 &et_literal_pos.clone(),
175 let runtime_expr = if splice_assignments.is_empty() && function_pointers.is_empty() {
178 let body = if env.codegen {
179 let mut b = splice_assignments.clone();
180 b.extend(function_pointers.clone());
181 b.push(wrap_return(make_tree, &et_literal_pos));
184 vec![wrap_return(make_tree, &et_literal_pos)]
186 immediately_invoked_lambda(&et_literal_pos, body)
192 Expr_::mk_expression_tree(ast::ExpressionTree {
194 splices: splice_assignments,
202 /// Convert `foo` to `return foo;`.
203 fn wrap_return(e: Expr, pos: &Pos) -> Stmt {
204 Stmt::new(pos.clone(), Stmt_::Return(Box::new(Some(e))))
207 /// Wrap a FuncBody into an anonymous Fun_
208 fn wrap_fun_(body: ast::FuncBody, params: Vec<ast::FunParam>, pos: Pos) -> ast::Fun_ {
214 ret: ast::TypeHint((), None),
215 name: make_id(pos, ";anonymous"),
217 where_constraints: vec![],
218 variadic: aast::FunVariadicity::FVnonVariadic,
221 fun_kind: ast::FunKind::FSync,
222 ctxs: None, // TODO(T70095684)
223 unsafe_ctxs: None, // TODO(T70095684)
224 user_attributes: vec![],
230 struct VoidReturnCheck {
231 only_void_return: bool,
234 impl<'ast> Visitor<'ast> for VoidReturnCheck {
235 type P = AstParams<(), ()>;
237 fn object(&mut self) -> &mut dyn Visitor<'ast, P = Self::P> {
241 fn visit_expr(&mut self, env: &mut (), e: &aast::Expr<(), ()>) -> Result<(), ()> {
245 // Don't recurse into splices or LFuns
246 ETSplice(_) | Lfun(_) => Ok(()),
247 // TODO: Do we even recurse on expressions?
248 _ => e.recurse(env, self),
252 fn visit_stmt(&mut self, env: &mut (), s: &'ast aast::Stmt<(), ()>) -> Result<(), ()> {
258 self.only_void_return = false;
262 _ => s.recurse(env, self),
267 fn only_void_return(lfun_body: &ast::Block) -> bool {
268 let mut checker = VoidReturnCheck {
269 only_void_return: true,
271 visit(&mut checker, &mut (), lfun_body).unwrap();
272 checker.only_void_return
275 struct NestedSpliceCheck {
276 has_nested_splice: Option<Pos>,
279 impl<'ast> Visitor<'ast> for NestedSpliceCheck {
280 type P = AstParams<(), ()>;
282 fn object(&mut self) -> &mut dyn Visitor<'ast, P = Self::P> {
286 fn visit_expr(&mut self, env: &mut (), e: &aast::Expr<(), ()>) -> Result<(), ()> {
291 self.has_nested_splice = Some(e.1.clone());
293 _ if self.has_nested_splice.is_none() => e.recurse(env, self)?,
300 /// Assumes that the Expr is the expression within a splice.
301 /// If the expression has a splice contained within, then we have
302 /// nested splices and this will raise an error
303 fn check_nested_splice(e: &ast::Expr) -> Result<(), (Pos, String)> {
304 let mut checker = NestedSpliceCheck {
305 has_nested_splice: None,
307 visit(&mut checker, &mut (), e).unwrap();
308 if let Some(p) = checker.has_nested_splice {
309 return Err((p, "Splice syntax `${...}` cannot be nested.".into()));
314 fn null_literal(pos: Pos) -> Expr {
315 Expr::new((), pos, Expr_::Null)
318 fn string_literal(pos: Pos, s: &str) -> Expr {
319 Expr::new((), pos, Expr_::String(BString::from(s)))
322 fn int_literal(pos: Pos, i: usize) -> Expr {
323 Expr::new((), pos, Expr_::Int(i.to_string()))
326 fn vec_literal(items: Vec<Expr>) -> Expr {
327 let positions: Vec<_> = items.iter().map(|x| &x.1).collect();
328 let position = merge_positions(&positions);
329 vec_literal_with_pos(&position, items)
332 fn vec_literal_with_pos(pos: &Pos, items: Vec<Expr>) -> Expr {
333 let fields: Vec<_> = items.into_iter().map(|e| ast::Afield::AFvalue(e)).collect();
337 Expr_::Collection(Box::new((make_id(pos.clone(), "vec"), None, fields))),
341 fn dict_literal(pos: &Pos, key_value_pairs: Vec<(Expr, Expr)>) -> Expr {
342 let fields = key_value_pairs
344 .map(|(k, v)| ast::Afield::AFkvalue(k, v))
349 Expr_::Collection(Box::new((make_id(pos.clone(), "dict"), None, fields))),
353 fn make_id(pos: Pos, name: &str) -> ast::Id {
354 ast::Id(pos, name.into())
357 fn visitor_variable() -> String {
361 /// Build `$v->meth_name(args)`.
362 fn v_meth_call(meth_name: &str, args: Vec<Expr>, pos: &Pos) -> Expr {
363 let receiver = Expr::mk_lvar(pos, &visitor_variable());
364 let meth = Expr::new(
367 Expr_::Id(Box::new(ast::Id(pos.clone(), meth_name.into()))),
370 let c = Expr_::Call(Box::new((
374 Expr_::ObjGet(Box::new((
377 OgNullFlavor::OGNullthrows,
385 Expr::new((), pos.clone(), c)
388 fn meth_call(receiver: Expr, meth_name: &str, args: Vec<Expr>, pos: &Pos) -> Expr {
389 let meth = Expr::new(
392 Expr_::Id(Box::new(ast::Id(pos.clone(), meth_name.into()))),
395 let c = Expr_::Call(Box::new((
399 Expr_::ObjGet(Box::new((
402 OgNullFlavor::OGNullthrows,
410 Expr::new((), pos.clone(), c)
413 fn static_meth_call(classname: &str, meth_name: &str, args: Vec<Expr>, pos: &Pos) -> Expr {
414 let callee = Expr::new(
417 Expr_::ClassConst(Box::new((
418 // TODO: Refactor ClassId creation with new_obj
422 ClassId_::CIexpr(Expr::new(
425 Expr_::Id(Box::new(Id(pos.clone(), classname.to_string()))),
428 (pos.clone(), meth_name.to_string()),
434 Expr_::Call(Box::new((callee, vec![], args, None))),
438 /// Join a slice of positions together into a single, larger position.
439 fn merge_positions(positions: &[&Pos]) -> Pos {
442 .fold(None, |acc, pos| match acc {
443 Some(res) => Some(Pos::merge(&res, pos).expect("Positions should be in the same file")),
444 None => Some((*pos).clone()),
446 .unwrap_or(Pos::make_none())
449 fn create_temp_statements(exprs: Vec<Expr>, mk_lvar: fn(&Pos, usize) -> Expr) -> Vec<Stmt> {
456 Stmt_::Expr(Box::new(Expr::new(
459 Expr_::Binop(Box::new((Bop::Eq(None), mk_lvar(&expr.1, i), expr))),
466 fn temp_lvar_string(name: &str, num: usize) -> String {
467 format!("$0{}{}", name, num)
470 fn temp_splice_lvar_string(num: usize) -> String {
471 temp_lvar_string("splice", num)
474 fn temp_splice_lvar(pos: &Pos, num: usize) -> Expr {
475 Expr::mk_lvar(pos, &temp_splice_lvar_string(num))
478 fn temp_function_pointer_lvar_string(num: usize) -> String {
479 temp_lvar_string("fp", num)
482 fn temp_function_pointer_lvar(pos: &Pos, num: usize) -> Expr {
483 Expr::mk_lvar(pos, &temp_function_pointer_lvar_string(num))
486 fn temp_static_method_lvar_string(num: usize) -> String {
487 temp_lvar_string("sm", num)
490 fn temp_static_method_lvar(pos: &Pos, num: usize) -> Expr {
491 Expr::mk_lvar(pos, &temp_static_method_lvar_string(num))
494 /// Given a Pos, returns a shape literal expression representing it.
498 /// 'path' => __FILE__,
499 /// 'start_line' => 1,
500 /// 'end_line' => 10,
501 /// 'start_column' => 0,
502 /// 'end_column' => 80,
506 /// If this Pos is Pos.none or invalid, return a literal null instead.
507 fn exprpos(pos: &Pos) -> Expr {
508 if pos.is_none() || !pos.is_valid() {
509 null_literal(pos.clone())
511 let ((start_lnum, start_bol, start_cnum), (end_lnum, end_bol, end_cnum)) =
512 pos.to_start_and_end_lnum_bol_cnum();
520 Expr_::Id(Box::new(make_id(pos.clone(), "__FILE__"))),
523 ("start_line", int_literal(pos.clone(), start_lnum)),
524 ("end_line", int_literal(pos.clone(), end_lnum)),
527 int_literal(pos.clone(), start_cnum - start_bol),
529 ("end_column", int_literal(pos.clone(), end_cnum - end_bol)),
532 shape_literal(pos, fields)
536 fn shape_literal(pos: &Pos, fields: Vec<(&str, Expr)>) -> Expr {
537 let shape_fields: Vec<_> = fields
539 .map(|(name, value)| {
540 let bs = BString::from(name);
541 let field_name = ShapeFieldName::SFlitStr((pos.clone(), bs));
545 Expr::new((), pos.clone(), Expr_::Shape(shape_fields))
548 fn boolify(receiver: Expr) -> Expr {
549 let pos = receiver.1.clone();
550 meth_call(receiver, "__bool", vec![], &pos)
555 global_function_pointers: Vec<Expr>,
556 static_method_pointers: Vec<Expr>,
559 /// Performs both the virtualization and the desugaring in tandem
560 /// Also extracts the expressions that need to be assigned to temporaries
561 /// Replaces the extracted splices, function pointers, and static method pointers
562 /// with temporary variables
564 temps: &mut Temporaries,
567 ) -> Result<(Expr, Expr), (Pos, String)> {
570 let Expr(_, pos, expr_) = e;
571 let pos_expr = exprpos(&pos);
572 let (virtual_expr, desugar_expr) = match expr_ {
574 // Virtualized: MyDsl::intType()
575 // Desugared: $0v->visitInt(new ExprPos(...), 1)
577 let virtual_expr = static_meth_call(visitor_name, et::INT_TYPE, vec![], &pos);
578 let desugar_expr = v_meth_call(
580 vec![pos_expr, Expr((), pos.clone(), expr_)],
583 (virtual_expr, desugar_expr)
585 // Source: MyDsl`1.0`
586 // Virtualized: MyDsl::floatType()
587 // Desugared: $0v->visitFloat(new ExprPos(...), 1.0)
589 let virtual_expr = static_meth_call(visitor_name, et::FLOAT_TYPE, vec![], &pos);
590 let desugar_expr = v_meth_call(
592 vec![pos_expr, Expr((), pos.clone(), expr_)],
595 (virtual_expr, desugar_expr)
597 // Source: MyDsl`'foo'`
598 // Virtualized: MyDsl::stringType()
599 // Desugared: $0v->visitString(new ExprPos(...), 'foo')
601 let virtual_expr = static_meth_call(visitor_name, et::STRING_TYPE, vec![], &pos);
602 let desugar_expr = v_meth_call(
604 vec![pos_expr, Expr((), pos.clone(), expr_)],
607 (virtual_expr, desugar_expr)
609 // Source: MyDsl`true`
610 // Virtualized: MyDsl::boolType()
611 // Desugared: $0v->visitBool(new ExprPos(...), true)
613 let virtual_expr = static_meth_call(visitor_name, et::BOOL_TYPE, vec![], &pos);
614 let desugar_expr = v_meth_call(
616 vec![pos_expr, Expr((), pos.clone(), expr_)],
619 (virtual_expr, desugar_expr)
621 // Source: MyDsl`null`
622 // Virtualized: MyDsl::nullType()
623 // Desugared: $0v->visitNull(new ExprPos(...))
625 let virtual_expr = static_meth_call(visitor_name, et::NULL_TYPE, vec![], &pos);
626 let desugar_expr = v_meth_call(et::VISIT_NULL, vec![pos_expr], &pos);
627 (virtual_expr, desugar_expr)
631 // Desugared: $0v->visitLocal(new ExprPos(...), '$x')
633 let desugar_expr = v_meth_call(
635 vec![pos_expr, string_literal(lid.0.clone(), &((lid.1).1))],
638 let virtual_expr = Expr((), pos, Lvar(lid));
639 (virtual_expr, desugar_expr)
642 let (op, lhs, rhs) = *bop;
643 let (virtual_lhs, desugar_lhs) = rewrite_expr(temps, lhs, visitor_name)?;
644 let (virtual_rhs, desugar_rhs) = rewrite_expr(temps, rhs, visitor_name)?;
645 if op == Bop::Eq(None) {
646 // Source: MyDsl`$x = ...`
647 // Virtualized: $x = ...
648 // Desugared: $0v->visitAssign(new ExprPos(...), $0v->visitLocal(...), ...)
649 let desugar_expr = v_meth_call(
651 vec![pos_expr, desugar_lhs, desugar_rhs],
654 let virtual_expr = Expr((), pos, Binop(Box::new((op, virtual_lhs, virtual_rhs))));
655 (virtual_expr, desugar_expr)
657 // Source: MyDsl`... + ...`
658 // Virtualized: ...->__plus(...)
659 // Desugared: $0v->visitBinop(new ExprPos(...), ..., '__plus', ...)
660 let binop_str = match op {
661 Bop::Plus => "__plus",
662 Bop::Minus => "__minus",
663 Bop::Star => "__star",
664 Bop::Slash => "__slash",
665 Bop::Percent => "__percent",
666 // Convert boolean &&, ||
667 Bop::Ampamp => "__ampamp",
668 Bop::Barbar => "__barbar",
669 // Convert comparison operators, <, <=, >, >=, ===, !==
670 Bop::Lt => "__lessThan",
671 Bop::Lte => "__lessThanEqual",
672 Bop::Gt => "__greaterThan",
673 Bop::Gte => "__greaterThanEqual",
674 Bop::Eqeqeq => "__tripleEquals",
675 Bop::Diff2 => "__notTripleEquals",
676 // Convert string concatenation
678 // Convert bitwise operators, &, |, ^, <<, >>
681 Bop::Xor => "__caret",
682 Bop::Ltlt => "__lessThanLessThan",
683 Bop::Gtgt => "__greaterThanGreaterThan",
684 // Explicit list of unsupported operators and error messages
688 "Expression trees do not support the exponent operator `**`.".into(),
691 Bop::Eqeq | Bop::Diff => {
694 "Expression trees only support strict equality operators `===` and `!==`".into(),
700 "Expression trees do not support the spaceship operator `<=>`. Try comparison operators like `<` and `>=`".into(),
703 Bop::QuestionQuestion => {
706 "Expression trees do not support the null coalesce operator `??`."
713 "Expression trees do not support compound assignments. Try the long form style `$foo = $foo + $bar` instead.".into(),
717 let virtual_expr = meth_call(virtual_lhs, &binop_str, vec![virtual_rhs], &pos);
718 let desugar_expr = v_meth_call(
723 string_literal(pos.clone(), &binop_str),
728 (virtual_expr, desugar_expr)
731 // Source: MyDsl`!...`
732 // Virtualized: ...->__exclamationMark(...)
733 // Desugared: $0v->visitUnop(new ExprPos(...), ..., '__exclamationMark')
735 let (op, operand) = *unop;
736 let (virtual_operand, desugar_operand) = rewrite_expr(temps, operand, visitor_name)?;
737 let op_str = match op {
738 // Allow boolean not operator !$x
739 Uop::Unot => "__exclamationMark",
740 // Allow negation -$x (required for supporting negative literals -123)
741 Uop::Uminus => "__negate",
742 // Allow bitwise complement
743 Uop::Utild => "__tilde",
744 // Currently not allowed operators
748 "Expression trees do not support the unary plus operator.".into(),
751 Uop::Uincr | Uop::Upincr => {
754 "Expression trees do not support the increment operator `++`.".into(),
757 Uop::Udecr | Uop::Updecr => {
760 "Expression trees do not support the decrement operator `--`.".into(),
766 "Expression trees do not support the error suppression operator `@`."
771 let virtual_expr = meth_call(virtual_operand, &op_str, vec![], &pos);
772 let desugar_expr = v_meth_call(
777 string_literal(pos.clone(), &op_str),
781 (virtual_expr, desugar_expr)
783 // Source: MyDsl`... ? ... : ...`
784 // Virtualized: ...->__bool() ? ... : ...
785 // Desugared: $0v->visitTernary(new ExprPos(...), ..., ..., ...)
787 let (e1, e2o, e3) = *eif;
788 let (virtual_e1, desugar_e1) = rewrite_expr(temps, e1, visitor_name)?;
789 let (virtual_e2, desugar_e2) = if let Some(e2) = e2o {
790 rewrite_expr(temps, e2, visitor_name)?
794 "Unsupported expression tree syntax: Elvis operator".into(),
797 let (virtual_e3, desugar_e3) = rewrite_expr(temps, e3, visitor_name)?;
799 let desugar_expr = v_meth_call(
801 vec![pos_expr, desugar_e1, desugar_e2, desugar_e3],
804 let virtual_expr = Expr(
813 (virtual_expr, desugar_expr)
815 // Source: MyDsl`...()`
816 // Virtualized: ...()
817 // Desugared: $0v->visitCall(new ExprPos(...), ..., vec[])
819 let (recv, targs, args, variadic) = *call;
820 if variadic.is_some() {
823 "Expression trees do not support variadic calls.".into(),
826 if !targs.is_empty() {
829 "Expression trees do not support function calls with generics.".into(),
833 // Don't transform calls to `hh_show`.
834 Id(sid) if is_typechecker_fun_name(&sid.1) => {
835 let call_e = Expr::new(
838 Call(Box::new((recv, targs, args, variadic))),
840 return Ok((call_e.clone(), call_e));
845 let (virtual_args, desugar_args) = rewrite_exprs(temps, args, visitor_name)?;
848 // Source: MyDsl`foo()`
849 // Virtualized: MyDsl::symbolType($0fpXX)()
850 // Desugared: $0v->visitCall(new ExprPos(...), $0v->visitGlobalFunction(new ExprPos(...), $0fpXX), vec[])
852 let len = temps.global_function_pointers.len();
853 temps.global_function_pointers.push(global_func_ptr(&sid));
854 let temp_variable = temp_function_pointer_lvar(&recv.1, len);
856 let desugar_expr = v_meth_call(
861 et::VISIT_GLOBAL_FUNCTION,
862 vec![pos_expr, temp_variable.clone()],
865 vec_literal(desugar_args),
869 let virtual_expr = Expr(
884 (virtual_expr, desugar_expr)
886 // Source: MyDsl`Foo::bar()`
887 // Virtualized: MyDsl::symbolType($0smXX)()
888 // Desugared: $0v->visitCall(new ExprPos(...), $0v->visitStaticMethod(new ExprPos(...), $0smXX, vec[])
891 if let ClassId_::CIexpr(Expr(_, _, Id(sid))) = &cid.2 {
892 if sid.1 == classes::PARENT
893 || sid.1 == classes::SELF
894 || sid.1 == classes::STATIC
898 "Static method calls in expression trees require explicit class names.".into(),
904 "Expression trees only support function calls and static method calls on named classes.".into()));
907 let len = temps.static_method_pointers.len();
909 .static_method_pointers
910 .push(static_meth_ptr(&recv.1, &cid, &s));
911 let temp_variable = temp_static_method_lvar(&recv.1, len);
913 let desugar_expr = v_meth_call(
918 et::VISIT_STATIC_METHOD,
919 vec![pos_expr, temp_variable.clone()],
922 vec_literal(desugar_args),
926 let virtual_expr = Expr(
941 (virtual_expr, desugar_expr)
943 // Source: MyDsl`$x->bar()`
944 // Virtualized: $x->bar()
945 // Desugared: $0v->visitCall($0v->visitMethodCall(new ExprPos(...), $0v->visitLocal(new ExprPos(...), '$x'), 'bar'), vec[])
946 ObjGet(og) if !og.3 => {
947 let (e1, e2, null_flavor, is_prop_call) = *og;
948 if null_flavor == OgNullFlavor::OGNullsafe {
951 "Expression Trees do not support nullsafe method calls".into(),
954 let (virtual_e1, desugar_e1) = rewrite_expr(temps, e1, visitor_name)?;
955 let id = if let Id(id) = &e2.2 {
956 string_literal(id.0.clone(), &id.1)
960 "Expression trees only support named method calls.".into(),
963 let desugar_expr = v_meth_call(
968 et::VISIT_INSTANCE_METHOD,
969 vec![pos_expr, desugar_e1, id],
972 vec_literal(desugar_args),
976 let virtual_expr = Expr(
983 ObjGet(Box::new((virtual_e1, e2, null_flavor, is_prop_call))),
990 (virtual_expr, desugar_expr)
993 let (virtual_recv, desugar_recv) =
994 rewrite_expr(temps, Expr((), recv.1, recv.2), visitor_name)?;
995 let desugar_expr = v_meth_call(
997 vec![pos_expr, desugar_recv, vec_literal(desugar_args)],
1000 let virtual_expr = Expr(
1003 Call(Box::new((virtual_recv, vec![], virtual_args, None))),
1005 (virtual_expr, desugar_expr)
1009 // Source: MyDsl`($x) ==> { ... }`
1010 // Virtualized: ($x) ==> { ...; return MyDsl::voidType(); }
1011 // if no `return expr;` statements.
1012 // Desugared: $0v->visitLambda(new ExprPos(...), vec['$x'], vec[...]).
1014 let mut fun_ = lf.0;
1016 let mut param_names = Vec::with_capacity(fun_.params.len());
1017 for param in &fun_.params {
1018 if param.expr.is_some() {
1021 "Expression trees do not support parameters with default values.".into(),
1024 param_names.push(string_literal(param.pos.clone(), ¶m.name));
1027 let body = std::mem::take(&mut fun_.body.fb_ast);
1029 let should_append_return = only_void_return(&body);
1031 let (mut virtual_body_stmts, desugar_body) = rewrite_stmts(temps, body, visitor_name)?;
1033 if should_append_return {
1034 virtual_body_stmts.push(Stmt(
1036 aast::Stmt_::Return(Box::new(Some(static_meth_call(
1045 let desugar_expr = v_meth_call(
1049 vec_literal(param_names),
1050 vec_literal(desugar_body),
1054 fun_.body.fb_ast = virtual_body_stmts;
1055 let virtual_expr = Expr((), pos, Lfun(Box::new((fun_, vec![]))));
1056 (virtual_expr, desugar_expr)
1058 // Source: MyDsl`${ ... }`
1059 // Virtualized to `${ ... }`
1060 // Desugared to `$0v->splice(new ExprPos(...), '$var_name', ...)`
1062 check_nested_splice(&e)?;
1063 let len = temps.splices.len();
1064 let expr_pos = e.1.clone();
1065 temps.splices.push(*e);
1066 let temp_variable = temp_splice_lvar(&expr_pos, len);
1067 let temp_variable_string = string_literal(expr_pos, &temp_splice_lvar_string(len));
1068 let desugar_expr = v_meth_call(
1070 vec![pos_expr, temp_variable_string, temp_variable.clone()],
1073 let virtual_expr = Expr((), pos, ETSplice(Box::new(temp_variable)));
1074 (virtual_expr, desugar_expr)
1077 let (e1, e2, null_flavor, is_prop_call) = *og;
1078 if null_flavor == OgNullFlavor::OGNullsafe {
1081 "Expression Trees do not support nullsafe property access".into(),
1084 let (virtual_e1, desugar_e1) = rewrite_expr(temps, e1, visitor_name)?;
1085 let id = if let Id(id) = &e2.2 {
1086 string_literal(id.0.clone(), &id.1)
1090 "Expression trees only support named property access.".into(),
1093 let desugar_expr = v_meth_call(
1094 et::VISIT_PROPERTY_ACCESS,
1095 vec![pos_expr, desugar_e1, id],
1098 let virtual_expr = Expr(
1101 ObjGet(Box::new((virtual_e1, e2, null_flavor, is_prop_call))),
1103 (virtual_expr, desugar_expr)
1105 // Source: MyDsl`<foo my-attr="stuff">text <foo-child/> </foo>`
1106 // Virtualized: <foo my-attr={MyDsl::stringType()}>{MyDsl::stringType()} <foo-child/> </foo>
1111 // dict["my-attr" => $0v->visitString(...)],
1113 // $0v->visitString(..., "text ")],
1114 // $0v->visitXhp(..., :foo-child::class, ...),
1118 let (hint, attrs, children) = *xml;
1120 let mut virtual_attrs = vec![];
1121 let mut desugar_attrs = vec![];
1122 for attr in attrs.clone() {
1124 aast::XhpAttribute::XhpSimple(xs) => {
1125 let (attr_name_pos, attr_name) = xs.name.clone();
1127 Expr::new((), attr_name_pos, Expr_::String(BString::from(attr_name)));
1129 let (virtual_attr_expr, desugar_attr_expr) =
1130 rewrite_expr(temps, xs.expr, visitor_name)?;
1132 desugar_attrs.push((dict_key, desugar_attr_expr));
1133 virtual_attrs.push(aast::XhpAttribute::XhpSimple(aast::XhpSimple {
1134 expr: virtual_attr_expr,
1138 aast::XhpAttribute::XhpSpread(e) => {
1141 "Expression trees do not support attribute spread syntax.".into(),
1147 let (virtual_children, desugar_children) =
1148 rewrite_exprs(temps, children.clone(), visitor_name)?;
1150 // Construct :foo::class.
1151 let hint_pos = hint.0.clone();
1152 let hint_class = Expr_::ClassConst(Box::new((
1156 ClassId_::CIexpr(Expr::new(
1159 Expr_::Id(Box::new(ast_defs::Id(hint_pos.clone(), hint.1.clone()))),
1162 (hint_pos.clone(), "class".to_string()),
1165 let virtual_expr = Expr(
1168 Xml(Box::new((hint, virtual_attrs, virtual_children))),
1170 let desugar_expr = v_meth_call(
1174 Expr((), pos.clone(), hint_class),
1175 dict_literal(&pos, desugar_attrs),
1176 vec_literal(desugar_children),
1180 (virtual_expr, desugar_expr)
1182 ExpressionTree(_) => {
1183 return Err((pos, "Expression trees may not be nested".into()));
1186 return Err((pos, "Unsupported expression tree syntax.".into()));
1189 Ok((virtual_expr, desugar_expr))
1193 temps: &mut Temporaries,
1196 ) -> Result<(Vec<Expr>, Vec<Expr>), (Pos, String)> {
1197 let mut virtual_results = Vec::with_capacity(exprs.len());
1198 let mut desugar_results = Vec::with_capacity(exprs.len());
1200 let (virtual_expr, desugar_expr) = rewrite_expr(temps, expr, visitor_name)?;
1201 virtual_results.push(virtual_expr);
1202 desugar_results.push(desugar_expr);
1204 Ok((virtual_results, desugar_results))
1208 temps: &mut Temporaries,
1211 ) -> Result<(Vec<Stmt>, Vec<Expr>), (Pos, String)> {
1212 let mut virtual_results = Vec::with_capacity(stmts.len());
1213 let mut desugar_results = Vec::with_capacity(stmts.len());
1215 let (virtual_stmt, desugared_expr) = rewrite_stmt(temps, stmt, visitor_name)?;
1216 virtual_results.push(virtual_stmt);
1217 if let Some(desugared_expr) = desugared_expr {
1218 desugar_results.push(desugared_expr);
1221 Ok((virtual_results, desugar_results))
1225 temps: &mut Temporaries,
1228 ) -> Result<(Stmt, Option<Expr>), (Pos, String)> {
1231 let Stmt(pos, stmt_) = s;
1232 let pos_expr = exprpos(&pos);
1234 let virtual_desugar = match stmt_ {
1236 let (virtual_expr, desugar_expr) = rewrite_expr(temps, *e, visitor_name)?;
1237 (Stmt(pos, Expr(Box::new(virtual_expr))), Some(desugar_expr))
1239 Return(e) => match *e {
1240 // Source: MyDsl`return ...;`
1241 // Virtualized: return ...;
1242 // Desugared: $0v->visitReturn(new ExprPos(...), $0v->...)
1244 let (virtual_expr, desugar_expr) = rewrite_expr(temps, e, visitor_name)?;
1246 v_meth_call(et::VISIT_RETURN, vec![pos_expr, desugar_expr], &pos);
1247 let virtual_stmt = Stmt(pos, Return(Box::new(Some(virtual_expr))));
1248 (virtual_stmt, Some(desugar_expr))
1250 // Source: MyDsl`return;`
1251 // Virtualized: return MyDsl::voidType();
1252 // Desugared: $0v->visitReturn(new ExprPos(...), null)
1254 let desugar_expr = v_meth_call(
1256 vec![pos_expr, null_literal(pos.clone())],
1260 let virtual_void_expr = static_meth_call(visitor_name, et::VOID_TYPE, vec![], &pos);
1261 let virtual_stmt = Stmt(pos, Return(Box::new(Some(virtual_void_expr))));
1262 (virtual_stmt, Some(desugar_expr))
1265 // Source: MyDsl`if (...) {...} else {...}`
1266 // Virtualized: if (...->__bool())) {...} else {...}
1267 // Desugared: $0v->visitIf(new ExprPos(...), $0v->..., vec[...], vec[...])
1269 let (cond_expr, then_block, else_block) = *if_stmt;
1270 let (virtual_cond, desugar_cond) = rewrite_expr(temps, cond_expr, visitor_name)?;
1271 let (virtual_then_stmts, desugar_then) =
1272 rewrite_stmts(temps, then_block, visitor_name)?;
1273 let (virtual_else_stmts, desugar_else) =
1274 rewrite_stmts(temps, else_block, visitor_name)?;
1276 let desugar_expr = v_meth_call(
1281 vec_literal(desugar_then),
1282 vec_literal(desugar_else),
1286 let virtual_stmt = Stmt(
1289 boolify(virtual_cond),
1294 (virtual_stmt, Some(desugar_expr))
1296 // Source: MyDsl`while (...) {...}`
1297 // Virtualized: while (...->__bool()) {...}
1298 // Desugared: $0v->visitWhile(new ExprPos(...), $0v->..., vec[...])
1300 let (cond, body) = *w;
1301 let (virtual_cond, desugar_cond) = rewrite_expr(temps, cond, visitor_name)?;
1302 let (virtual_body_stmts, desugar_body) = rewrite_stmts(temps, body, visitor_name)?;
1304 let desugar_expr = v_meth_call(
1306 vec![pos_expr, desugar_cond, vec_literal(desugar_body)],
1309 let virtual_stmt = Stmt(
1311 While(Box::new((boolify(virtual_cond), virtual_body_stmts))),
1313 (virtual_stmt, Some(desugar_expr))
1315 // Source: MyDsl`for (...; ...; ...) {...}`
1316 // Virtualized: for (...; ...->__bool(); ...) {...}
1317 // Desugared: $0v->visitFor(new ExprPos(...), vec[...], ..., vec[...], vec[...])
1319 let (init, cond, incr, body) = *w;
1320 let (virtual_init_exprs, desugar_init_exprs) =
1321 rewrite_exprs(temps, init, visitor_name)?;
1322 let (virtual_cond_option, desugar_cond_expr) = match cond {
1324 let (virtual_cond, desugar_cond) = rewrite_expr(temps, cond, visitor_name)?;
1325 (Some(boolify(virtual_cond)), desugar_cond)
1327 None => (None, null_literal(pos.clone())),
1329 let (virtual_incr_exprs, desugar_incr_exprs) =
1330 rewrite_exprs(temps, incr, visitor_name)?;
1331 let (virtual_body_stmts, desugar_body) = rewrite_stmts(temps, body, visitor_name)?;
1333 let desugar_expr = v_meth_call(
1337 vec_literal(desugar_init_exprs),
1339 vec_literal(desugar_incr_exprs),
1340 vec_literal(desugar_body),
1344 let virtual_stmt = Stmt(
1348 virtual_cond_option,
1353 (virtual_stmt, Some(desugar_expr))
1355 // Source: MyDsl`break;`
1356 // Virtualized: break;
1357 // Desugared: $0v->visitBreak(new ExprPos(...))
1359 let desugar_expr = v_meth_call(et::VISIT_BREAK, vec![pos_expr], &pos);
1360 let virtual_stmt = Stmt(pos, Break);
1361 (virtual_stmt, Some(desugar_expr))
1363 // Source: MyDsl`continue;`
1364 // Virtualized: continue;
1365 // Desugared: $0v->visitContinue(new ExprPos(...))
1367 let desugar_expr = v_meth_call(et::VISIT_CONTINUE, vec![pos_expr], &pos);
1368 let virtual_stmt = Stmt(pos, Continue);
1369 (virtual_stmt, Some(desugar_expr))
1371 Noop => (Stmt(pos, Noop), None),
1375 "Expression trees do not support this statement syntax.".into(),
1382 fn hint_name(hint: &aast::Hint) -> Result<String, (Pos, String)> {
1383 if let Hint_::Happly(id, _) = &*hint.1 {
1388 "Could not determine the visitor type for this Expression Tree".into(),
1393 fn immediately_invoked_lambda(pos: &Pos, stmts: Vec<Stmt>) -> Expr {
1394 let func_body = ast::FuncBody { fb_ast: stmts };
1395 let fun_ = wrap_fun_(func_body, vec![], pos.clone());
1396 let lambda_expr = Expr::new((), pos.clone(), Expr_::mk_lfun(fun_, vec![]));
1401 Expr_::Call(Box::new((lambda_expr, vec![], vec![], None))),
1405 /// Is this is a typechecker pseudo function like `hh_show` that
1406 /// shouldn't be desugared?
1407 fn is_typechecker_fun_name(name: &str) -> bool {
1408 strip_ns(name) == strip_ns(pseudo_functions::HH_SHOW)
1409 || strip_ns(name) == strip_ns(pseudo_functions::HH_SHOW_ENV)
1412 fn strip_ns(name: &str) -> &str {
1413 match name.chars().next() {
1414 Some('\\') => &name[1..],
1419 /// Return a shape literal that describes the values inside this
1420 /// expression tree literal. For example, given the expression tree:
1422 /// $et = Code`${ $x } + foo() + Bar::baz()`;
1424 /// The metadata is:
1427 /// // Simplified: We actually use a temporary variable whose value is $x.
1428 /// 'splices' => dict['$0splice0' => $x],
1430 /// 'functions' => vec[foo<>],
1431 /// 'static_methods' => vec[Bar::baz<>],
1433 fn maketree_metadata(
1437 static_methods: &[Expr],
1439 let key_value_pairs = splices
1443 let key = Expr::new(
1446 Expr_::String(BString::from(temp_splice_lvar_string(i))),
1448 let value = temp_splice_lvar(&expr.1, i);
1452 let splices_dict = dict_literal(pos, key_value_pairs);
1454 let function_vars = functions
1457 .map(|(i, expr)| temp_function_pointer_lvar(&expr.1, i))
1459 let functions_vec = vec_literal_with_pos(&pos, function_vars);
1461 let static_method_vars = static_methods
1464 .map(|(i, expr)| temp_static_method_lvar(&expr.1, i))
1466 let static_method_vec = vec_literal_with_pos(&pos, static_method_vars);
1471 ("splices", splices_dict),
1472 ("functions", functions_vec),
1473 ("static_methods", static_method_vec),
1478 fn global_func_ptr(sid: &Sid) -> Expr {
1479 let pos = sid.0.clone();
1483 Expr_::FunctionPointer(Box::new((ast::FunctionPtrId::FPId(sid.clone()), vec![]))),
1487 fn static_meth_ptr(pos: &Pos, cid: &ClassId, meth: &Pstring) -> Expr {
1491 Expr_::FunctionPointer(Box::new((
1492 aast::FunctionPtrId::FPClassConst(cid.clone(), meth.clone()),