Make `param_kind` non-optional
[hiphop-php.git] / hphp / hack / src / parser / lowerer / desugar_expression_tree.rs
blob2d3f5c60ded9d57256cdc29fd57e71f45a8cd04a
1 use crate::lowerer::Env;
2 use bstr::BString;
3 use naming_special_names_rust::{classes, expression_trees as et, pseudo_functions};
4 use oxidized::{
5     aast,
6     aast_visitor::{visit, AstParams, Node, Visitor},
7     ast,
8     ast::{ClassId, ClassId_, Expr, Expr_, Hint_, Sid, Stmt, Stmt_},
9     ast_defs,
10     ast_defs::*,
11     pos::Pos,
14 /// Rewrite the contents of an expression tree literal into an
15 /// expression on a visitor class.
16 ///
17 /// Given the following expression tree:
18 /// ```
19 /// MyDsl`foo(1) + ${ $x }`;
20 /// ```
21 ///
22 /// First, the splices are extracted and assigned to temporary variables:
23 /// ```
24 /// { $0splice0 = $x; }
25 /// ```
26 ///
27 /// Then the expression is virtualized as virtualized_expr
28 /// ```
29 /// MyDsl::symbolType(foo<>)(MyDsl::intType())->__plus( ${ $0splice0 } )
30 /// ```
31 /// Where virtualized_expr is used in helping type Expression Trees
32 ///
33 /// Finally, the expression is desugared as runtime_expr
34 /// ```
35 /// MyDsl::makeTree(
36 ///   // At runtime, expression tree visitors know the position of the literal.
37 ///   shape('path' => 'whatever.php', 'start_line' => 123, ...),
38 ///
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.
41 ///   shape(
42 ///     'splices' => dict['$0splice0' => $0splice0],
43 ///     'functions' => vec[foo<>],
44 ///     'static_methods' => vec[],
45 ///   )
46 ///
47 ///   (MyDsl $0v) ==> {
48 ///     $0v->visitBinop(
49 ///       // (ignoring ExprPos arguments for brevity)
50 ///       $0v->visitCall(
51 ///         $0v->visitGlobalFunction(foo<>),
52 ///         vec[$0v->visitInt(1)],
53 ///       ),
54 ///       '__plus',
55 ///       $0v->splice('$0splice0', $0splice0),
56 ///     )
57 ///   },
58 /// )
59 /// ```
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 {
66         splices: vec![],
67         global_function_pointers: vec![],
68         static_method_pointers: vec![],
69     };
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(
77         &et_literal_pos,
78         &temps.splices,
79         &temps.global_function_pointers,
80         &temps.static_method_pointers,
81     );
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],
87     };
88     let param = ast::FunParam {
89         annotation: (),
90         type_hint: ast::TypeHint((), Some(hint.clone())),
91         is_variadic: false,
92         pos: hint.0.clone(),
93         name: visitor_variable(),
94         expr: None,
95         callconv: ParamKind::Pnormal,
96         readonly: None,
97         user_attributes: vec![],
98         visibility: None,
99     };
100     let visitor_fun_ = wrap_fun_(visitor_body, vec![param], et_literal_pos.clone());
101     let visitor_lambda = Expr::new(
102         (),
103         et_literal_pos.clone(),
104         Expr_::mk_lfun(visitor_fun_, vec![]),
105     );
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)],
112         };
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)
115             .into_iter()
116             .map(|i| ast::Lid(et_literal_pos.clone(), (0, temp_splice_lvar_string(i))))
117             .collect();
118         let function_pointer_vars: Vec<ast::Lid> = (0..function_count)
119             .into_iter()
120             .map(|i| {
121                 ast::Lid(
122                     et_literal_pos.clone(),
123                     (0, temp_function_pointer_lvar_string(i)),
124                 )
125             })
126             .collect();
127         let static_method_vars: Vec<ast::Lid> = (0..static_method_count)
128             .into_iter()
129             .map(|i| {
130                 ast::Lid(
131                     et_literal_pos.clone(),
132                     (0, temp_static_method_lvar_string(i)),
133                 )
134             })
135             .collect();
136         spliced_vars.extend(function_pointer_vars);
137         spliced_vars.extend(static_method_vars);
138         Expr::new(
139             (),
140             et_literal_pos.clone(),
141             Expr_::Call(Box::new((
142                 Expr::new(
143                     (),
144                     et_literal_pos.clone(),
145                     Expr_::mk_efun(typing_fun_, spliced_vars),
146                 ),
147                 vec![],
148                 vec![],
149                 None,
150             ))),
151         )
152     };
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);
157     // `$0fp0 = foo<>;`
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(
169         &visitor_name,
170         et::MAKE_TREE,
171         vec![exprpos(&et_literal_pos), metadata, visitor_lambda],
172         &et_literal_pos.clone(),
173     );
175     let runtime_expr = if splice_assignments.is_empty() && function_pointers.is_empty() {
176         make_tree
177     } else {
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));
182             b
183         } else {
184             vec![wrap_return(make_tree, &et_literal_pos)]
185         };
186         immediately_invoked_lambda(&et_literal_pos, body)
187     };
189     Ok(Expr::new(
190         (),
191         et_literal_pos,
192         Expr_::mk_expression_tree(ast::ExpressionTree {
193             hint: hint.clone(),
194             splices: splice_assignments,
195             function_pointers,
196             virtualized_expr,
197             runtime_expr,
198         }),
199     ))
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_ {
209     ast::Fun_ {
210         span: pos.clone(),
211         readonly_this: None,
212         annotation: (),
213         readonly_ret: None,
214         ret: ast::TypeHint((), None),
215         name: make_id(pos, ";anonymous"),
216         tparams: vec![],
217         where_constraints: vec![],
218         variadic: aast::FunVariadicity::FVnonVariadic,
219         params,
220         body,
221         fun_kind: ast::FunKind::FSync,
222         ctxs: None,        // TODO(T70095684)
223         unsafe_ctxs: None, // TODO(T70095684)
224         user_attributes: vec![],
225         external: false,
226         doc_comment: None,
227     }
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> {
238         self
239     }
241     fn visit_expr(&mut self, env: &mut (), e: &aast::Expr<(), ()>) -> Result<(), ()> {
242         use aast::Expr_::*;
244         match &e.2 {
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),
249         }
250     }
252     fn visit_stmt(&mut self, env: &mut (), s: &'ast aast::Stmt<(), ()>) -> Result<(), ()> {
253         use aast::Stmt_::*;
255         match &s.1 {
256             Return(e) => {
257                 if (*e).is_some() {
258                     self.only_void_return = false;
259                 }
260                 Ok(())
261             }
262             _ => s.recurse(env, self),
263         }
264     }
267 fn only_void_return(lfun_body: &ast::Block) -> bool {
268     let mut checker = VoidReturnCheck {
269         only_void_return: true,
270     };
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> {
283         self
284     }
286     fn visit_expr(&mut self, env: &mut (), e: &aast::Expr<(), ()>) -> Result<(), ()> {
287         use aast::Expr_::*;
289         match &e.2 {
290             ETSplice(_) => {
291                 self.has_nested_splice = Some(e.1.clone());
292             }
293             _ if self.has_nested_splice.is_none() => e.recurse(env, self)?,
294             _ => {}
295         }
296         Ok(())
297     }
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,
306     };
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()));
310     }
311     Ok(())
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();
334     Expr::new(
335         (),
336         pos.clone(),
337         Expr_::Collection(Box::new((make_id(pos.clone(), "vec"), None, fields))),
338     )
341 fn dict_literal(pos: &Pos, key_value_pairs: Vec<(Expr, Expr)>) -> Expr {
342     let fields = key_value_pairs
343         .into_iter()
344         .map(|(k, v)| ast::Afield::AFkvalue(k, v))
345         .collect();
346     Expr::new(
347         (),
348         pos.clone(),
349         Expr_::Collection(Box::new((make_id(pos.clone(), "dict"), None, fields))),
350     )
353 fn make_id(pos: Pos, name: &str) -> ast::Id {
354     ast::Id(pos, name.into())
357 fn visitor_variable() -> String {
358     "$0v".to_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(
365         (),
366         pos.clone(),
367         Expr_::Id(Box::new(ast::Id(pos.clone(), meth_name.into()))),
368     );
370     let c = Expr_::Call(Box::new((
371         Expr::new(
372             (),
373             pos.clone(),
374             Expr_::ObjGet(Box::new((
375                 receiver,
376                 meth,
377                 OgNullFlavor::OGNullthrows,
378                 false,
379             ))),
380         ),
381         vec![],
382         args,
383         None,
384     )));
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(
390         (),
391         pos.clone(),
392         Expr_::Id(Box::new(ast::Id(pos.clone(), meth_name.into()))),
393     );
395     let c = Expr_::Call(Box::new((
396         Expr::new(
397             (),
398             pos.clone(),
399             Expr_::ObjGet(Box::new((
400                 receiver,
401                 meth,
402                 OgNullFlavor::OGNullthrows,
403                 false,
404             ))),
405         ),
406         vec![],
407         args,
408         None,
409     )));
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(
415         (),
416         pos.clone(),
417         Expr_::ClassConst(Box::new((
418             // TODO: Refactor ClassId creation with new_obj
419             ClassId(
420                 (),
421                 pos.clone(),
422                 ClassId_::CIexpr(Expr::new(
423                     (),
424                     pos.clone(),
425                     Expr_::Id(Box::new(Id(pos.clone(), classname.to_string()))),
426                 )),
427             ),
428             (pos.clone(), meth_name.to_string()),
429         ))),
430     );
431     Expr::new(
432         (),
433         pos.clone(),
434         Expr_::Call(Box::new((callee, vec![], args, None))),
435     )
438 /// Join a slice of positions together into a single, larger position.
439 fn merge_positions(positions: &[&Pos]) -> Pos {
440     positions
441         .iter()
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()),
445         })
446         .unwrap_or(Pos::make_none())
449 fn create_temp_statements(exprs: Vec<Expr>, mk_lvar: fn(&Pos, usize) -> Expr) -> Vec<Stmt> {
450     exprs
451         .into_iter()
452         .enumerate()
453         .map(|(i, expr)| {
454             Stmt::new(
455                 expr.1.clone(),
456                 Stmt_::Expr(Box::new(Expr::new(
457                     (),
458                     expr.1.clone(),
459                     Expr_::Binop(Box::new((Bop::Eq(None), mk_lvar(&expr.1, i), expr))),
460                 ))),
461             )
462         })
463         .collect()
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.
496 /// ```
497 /// shape(
498 ///   'path' => __FILE__,
499 ///   'start_line' => 1,
500 ///   'end_line' => 10,
501 ///   'start_column' => 0,
502 ///   'end_column' => 80,
503 /// )
504 /// ```
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())
510     } else {
511         let ((start_lnum, start_bol, start_cnum), (end_lnum, end_bol, end_cnum)) =
512             pos.to_start_and_end_lnum_bol_cnum();
514         let fields = vec![
515             (
516                 "path",
517                 Expr::new(
518                     (),
519                     pos.clone(),
520                     Expr_::Id(Box::new(make_id(pos.clone(), "__FILE__"))),
521                 ),
522             ),
523             ("start_line", int_literal(pos.clone(), start_lnum)),
524             ("end_line", int_literal(pos.clone(), end_lnum)),
525             (
526                 "start_column",
527                 int_literal(pos.clone(), start_cnum - start_bol),
528             ),
529             ("end_column", int_literal(pos.clone(), end_cnum - end_bol)),
530         ];
532         shape_literal(pos, fields)
533     }
536 fn shape_literal(pos: &Pos, fields: Vec<(&str, Expr)>) -> Expr {
537     let shape_fields: Vec<_> = fields
538         .into_iter()
539         .map(|(name, value)| {
540             let bs = BString::from(name);
541             let field_name = ShapeFieldName::SFlitStr((pos.clone(), bs));
542             (field_name, value)
543         })
544         .collect();
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)
553 struct Temporaries {
554     splices: Vec<Expr>,
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
563 fn rewrite_expr(
564     temps: &mut Temporaries,
565     e: Expr,
566     visitor_name: &str,
567 ) -> Result<(Expr, Expr), (Pos, String)> {
568     use aast::Expr_::*;
570     let Expr(_, pos, expr_) = e;
571     let pos_expr = exprpos(&pos);
572     let (virtual_expr, desugar_expr) = match expr_ {
573         // Source: MyDsl`1`
574         // Virtualized: MyDsl::intType()
575         // Desugared: $0v->visitInt(new ExprPos(...), 1)
576         Int(_) => {
577             let virtual_expr = static_meth_call(visitor_name, et::INT_TYPE, vec![], &pos);
578             let desugar_expr = v_meth_call(
579                 et::VISIT_INT,
580                 vec![pos_expr, Expr((), pos.clone(), expr_)],
581                 &pos,
582             );
583             (virtual_expr, desugar_expr)
584         }
585         // Source: MyDsl`1.0`
586         // Virtualized: MyDsl::floatType()
587         // Desugared: $0v->visitFloat(new ExprPos(...), 1.0)
588         Float(_) => {
589             let virtual_expr = static_meth_call(visitor_name, et::FLOAT_TYPE, vec![], &pos);
590             let desugar_expr = v_meth_call(
591                 et::VISIT_FLOAT,
592                 vec![pos_expr, Expr((), pos.clone(), expr_)],
593                 &pos,
594             );
595             (virtual_expr, desugar_expr)
596         }
597         // Source: MyDsl`'foo'`
598         // Virtualized: MyDsl::stringType()
599         // Desugared: $0v->visitString(new ExprPos(...), 'foo')
600         String(_) => {
601             let virtual_expr = static_meth_call(visitor_name, et::STRING_TYPE, vec![], &pos);
602             let desugar_expr = v_meth_call(
603                 et::VISIT_STRING,
604                 vec![pos_expr, Expr((), pos.clone(), expr_)],
605                 &pos,
606             );
607             (virtual_expr, desugar_expr)
608         }
609         // Source: MyDsl`true`
610         // Virtualized: MyDsl::boolType()
611         // Desugared: $0v->visitBool(new ExprPos(...), true)
612         True | False => {
613             let virtual_expr = static_meth_call(visitor_name, et::BOOL_TYPE, vec![], &pos);
614             let desugar_expr = v_meth_call(
615                 et::VISIT_BOOL,
616                 vec![pos_expr, Expr((), pos.clone(), expr_)],
617                 &pos,
618             );
619             (virtual_expr, desugar_expr)
620         }
621         // Source: MyDsl`null`
622         // Virtualized: MyDsl::nullType()
623         // Desugared: $0v->visitNull(new ExprPos(...))
624         Null => {
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)
628         }
629         // Source: MyDsl`$x`
630         // Virtualized: $x
631         // Desugared: $0v->visitLocal(new ExprPos(...), '$x')
632         Lvar(lid) => {
633             let desugar_expr = v_meth_call(
634                 et::VISIT_LOCAL,
635                 vec![pos_expr, string_literal(lid.0.clone(), &((lid.1).1))],
636                 &pos,
637             );
638             let virtual_expr = Expr((), pos, Lvar(lid));
639             (virtual_expr, desugar_expr)
640         }
641         Binop(bop) => {
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(
650                     et::VISIT_ASSIGN,
651                     vec![pos_expr, desugar_lhs, desugar_rhs],
652                     &pos,
653                 );
654                 let virtual_expr = Expr((), pos, Binop(Box::new((op, virtual_lhs, virtual_rhs))));
655                 (virtual_expr, desugar_expr)
656             } else {
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
677                     Bop::Dot => "__dot",
678                     // Convert bitwise operators, &, |, ^, <<, >>
679                     Bop::Amp => "__amp",
680                     Bop::Bar => "__bar",
681                     Bop::Xor => "__caret",
682                     Bop::Ltlt => "__lessThanLessThan",
683                     Bop::Gtgt => "__greaterThanGreaterThan",
684                     // Explicit list of unsupported operators and error messages
685                     Bop::Starstar => {
686                         return Err((
687                             pos,
688                             "Expression trees do not support the exponent operator `**`.".into(),
689                         ));
690                     }
691                     Bop::Eqeq | Bop::Diff => {
692                         return Err((
693                             pos,
694                             "Expression trees only support strict equality operators `===` and `!==`".into(),
695                         ));
696                     }
697                     Bop::Cmp => {
698                         return Err((
699                             pos,
700                             "Expression trees do not support the spaceship operator `<=>`. Try comparison operators like `<` and `>=`".into(),
701                         ));
702                     }
703                     Bop::QuestionQuestion => {
704                         return Err((
705                             pos,
706                             "Expression trees do not support the null coalesce operator `??`."
707                                 .into(),
708                         ));
709                     }
710                     Bop::Eq(_) => {
711                         return Err((
712                             pos,
713                             "Expression trees do not support compound assignments. Try the long form style `$foo = $foo + $bar` instead.".into(),
714                         ));
715                     }
716                 };
717                 let virtual_expr = meth_call(virtual_lhs, &binop_str, vec![virtual_rhs], &pos);
718                 let desugar_expr = v_meth_call(
719                     et::VISIT_BINOP,
720                     vec![
721                         pos_expr,
722                         desugar_lhs,
723                         string_literal(pos.clone(), &binop_str),
724                         desugar_rhs,
725                     ],
726                     &pos,
727                 );
728                 (virtual_expr, desugar_expr)
729             }
730         }
731         // Source: MyDsl`!...`
732         // Virtualized: ...->__exclamationMark(...)
733         // Desugared: $0v->visitUnop(new ExprPos(...), ..., '__exclamationMark')
734         Unop(unop) => {
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
745                 Uop::Uplus => {
746                     return Err((
747                         pos,
748                         "Expression trees do not support the unary plus operator.".into(),
749                     ));
750                 }
751                 Uop::Uincr | Uop::Upincr => {
752                     return Err((
753                         pos,
754                         "Expression trees do not support the increment operator `++`.".into(),
755                     ));
756                 }
757                 Uop::Udecr | Uop::Updecr => {
758                     return Err((
759                         pos,
760                         "Expression trees do not support the decrement operator `--`.".into(),
761                     ));
762                 }
763                 Uop::Usilence => {
764                     return Err((
765                         pos,
766                         "Expression trees do not support the error suppression operator `@`."
767                             .into(),
768                     ));
769                 }
770             };
771             let virtual_expr = meth_call(virtual_operand, &op_str, vec![], &pos);
772             let desugar_expr = v_meth_call(
773                 et::VISIT_UNOP,
774                 vec![
775                     pos_expr,
776                     desugar_operand,
777                     string_literal(pos.clone(), &op_str),
778                 ],
779                 &pos,
780             );
781             (virtual_expr, desugar_expr)
782         }
783         // Source: MyDsl`... ? ... : ...`
784         // Virtualized: ...->__bool() ? ... : ...
785         // Desugared: $0v->visitTernary(new ExprPos(...), ..., ..., ...)
786         Eif(eif) => {
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)?
791             } else {
792                 return Err((
793                     pos,
794                     "Unsupported expression tree syntax: Elvis operator".into(),
795                 ));
796             };
797             let (virtual_e3, desugar_e3) = rewrite_expr(temps, e3, visitor_name)?;
799             let desugar_expr = v_meth_call(
800                 et::VISIT_TERNARY,
801                 vec![pos_expr, desugar_e1, desugar_e2, desugar_e3],
802                 &pos,
803             );
804             let virtual_expr = Expr(
805                 (),
806                 pos,
807                 Eif(Box::new((
808                     boolify(virtual_e1),
809                     Some(virtual_e2),
810                     virtual_e3,
811                 ))),
812             );
813             (virtual_expr, desugar_expr)
814         }
815         // Source: MyDsl`...()`
816         // Virtualized: ...()
817         // Desugared: $0v->visitCall(new ExprPos(...), ..., vec[])
818         Call(call) => {
819             let (recv, targs, args, variadic) = *call;
820             if variadic.is_some() {
821                 return Err((
822                     pos,
823                     "Expression trees do not support variadic calls.".into(),
824                 ));
825             }
826             if !targs.is_empty() {
827                 return Err((
828                     pos,
829                     "Expression trees do not support function calls with generics.".into(),
830                 ));
831             }
832             match &recv.2 {
833                 // Don't transform calls to `hh_show`.
834                 Id(sid) if is_typechecker_fun_name(&sid.1) => {
835                     let call_e = Expr::new(
836                         (),
837                         pos.clone(),
838                         Call(Box::new((recv, targs, args, variadic))),
839                     );
840                     return Ok((call_e.clone(), call_e));
841                 }
842                 _ => {}
843             }
845             let (virtual_args, desugar_args) = rewrite_exprs(temps, args, visitor_name)?;
847             match recv.2 {
848                 // Source: MyDsl`foo()`
849                 // Virtualized: MyDsl::symbolType($0fpXX)()
850                 // Desugared: $0v->visitCall(new ExprPos(...), $0v->visitGlobalFunction(new ExprPos(...), $0fpXX), vec[])
851                 Id(sid) => {
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(
857                         et::VISIT_CALL,
858                         vec![
859                             pos_expr.clone(),
860                             v_meth_call(
861                                 et::VISIT_GLOBAL_FUNCTION,
862                                 vec![pos_expr, temp_variable.clone()],
863                                 &pos,
864                             ),
865                             vec_literal(desugar_args),
866                         ],
867                         &pos,
868                     );
869                     let virtual_expr = Expr(
870                         (),
871                         pos.clone(),
872                         Call(Box::new((
873                             static_meth_call(
874                                 visitor_name,
875                                 et::SYMBOL_TYPE,
876                                 vec![temp_variable],
877                                 &pos,
878                             ),
879                             vec![],
880                             virtual_args,
881                             None,
882                         ))),
883                     );
884                     (virtual_expr, desugar_expr)
885                 }
886                 // Source: MyDsl`Foo::bar()`
887                 // Virtualized: MyDsl::symbolType($0smXX)()
888                 // Desugared: $0v->visitCall(new ExprPos(...), $0v->visitStaticMethod(new ExprPos(...), $0smXX, vec[])
889                 ClassConst(cc) => {
890                     let (cid, s) = *cc;
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
895                         {
896                             return Err((
897                                 pos,
898                                 "Static method calls in expression trees require explicit class names.".into(),
899                             ));
900                         }
901                     } else {
902                         return Err((
903                             pos,
904                             "Expression trees only support function calls and static method calls on named classes.".into()));
905                     };
907                     let len = temps.static_method_pointers.len();
908                     temps
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(
914                         et::VISIT_CALL,
915                         vec![
916                             pos_expr.clone(),
917                             v_meth_call(
918                                 et::VISIT_STATIC_METHOD,
919                                 vec![pos_expr, temp_variable.clone()],
920                                 &pos,
921                             ),
922                             vec_literal(desugar_args),
923                         ],
924                         &pos,
925                     );
926                     let virtual_expr = Expr(
927                         (),
928                         pos.clone(),
929                         Call(Box::new((
930                             static_meth_call(
931                                 visitor_name,
932                                 et::SYMBOL_TYPE,
933                                 vec![temp_variable],
934                                 &pos,
935                             ),
936                             vec![],
937                             virtual_args,
938                             None,
939                         ))),
940                     );
941                     (virtual_expr, desugar_expr)
942                 }
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 {
949                         return Err((
950                             pos,
951                             "Expression Trees do not support nullsafe method calls".into(),
952                         ));
953                     }
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)
957                     } else {
958                         return Err((
959                             pos,
960                             "Expression trees only support named method calls.".into(),
961                         ));
962                     };
963                     let desugar_expr = v_meth_call(
964                         et::VISIT_CALL,
965                         vec![
966                             pos_expr.clone(),
967                             v_meth_call(
968                                 et::VISIT_INSTANCE_METHOD,
969                                 vec![pos_expr, desugar_e1, id],
970                                 &pos,
971                             ),
972                             vec_literal(desugar_args),
973                         ],
974                         &pos,
975                     );
976                     let virtual_expr = Expr(
977                         (),
978                         pos.clone(),
979                         Call(Box::new((
980                             Expr(
981                                 (),
982                                 pos,
983                                 ObjGet(Box::new((virtual_e1, e2, null_flavor, is_prop_call))),
984                             ),
985                             vec![],
986                             virtual_args,
987                             None,
988                         ))),
989                     );
990                     (virtual_expr, desugar_expr)
991                 }
992                 _ => {
993                     let (virtual_recv, desugar_recv) =
994                         rewrite_expr(temps, Expr((), recv.1, recv.2), visitor_name)?;
995                     let desugar_expr = v_meth_call(
996                         et::VISIT_CALL,
997                         vec![pos_expr, desugar_recv, vec_literal(desugar_args)],
998                         &pos,
999                     );
1000                     let virtual_expr = Expr(
1001                         (),
1002                         pos,
1003                         Call(Box::new((virtual_recv, vec![], virtual_args, None))),
1004                     );
1005                     (virtual_expr, desugar_expr)
1006                 }
1007             }
1008         }
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[...]).
1013         Lfun(lf) => {
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() {
1019                     return Err((
1020                         param.pos.clone(),
1021                         "Expression trees do not support parameters with default values.".into(),
1022                     ));
1023                 }
1024                 param_names.push(string_literal(param.pos.clone(), &param.name));
1025             }
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(
1035                     pos.clone(),
1036                     aast::Stmt_::Return(Box::new(Some(static_meth_call(
1037                         visitor_name,
1038                         et::VOID_TYPE,
1039                         vec![],
1040                         &pos,
1041                     )))),
1042                 ));
1043             }
1045             let desugar_expr = v_meth_call(
1046                 et::VISIT_LAMBDA,
1047                 vec![
1048                     pos_expr,
1049                     vec_literal(param_names),
1050                     vec_literal(desugar_body),
1051                 ],
1052                 &pos,
1053             );
1054             fun_.body.fb_ast = virtual_body_stmts;
1055             let virtual_expr = Expr((), pos, Lfun(Box::new((fun_, vec![]))));
1056             (virtual_expr, desugar_expr)
1057         }
1058         // Source: MyDsl`${ ... }`
1059         // Virtualized to `${ ... }`
1060         // Desugared to `$0v->splice(new ExprPos(...), '$var_name', ...)`
1061         ETSplice(e) => {
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(
1069                 et::SPLICE,
1070                 vec![pos_expr, temp_variable_string, temp_variable.clone()],
1071                 &pos,
1072             );
1073             let virtual_expr = Expr((), pos, ETSplice(Box::new(temp_variable)));
1074             (virtual_expr, desugar_expr)
1075         }
1076         ObjGet(og) => {
1077             let (e1, e2, null_flavor, is_prop_call) = *og;
1078             if null_flavor == OgNullFlavor::OGNullsafe {
1079                 return Err((
1080                     pos,
1081                     "Expression Trees do not support nullsafe property access".into(),
1082                 ));
1083             }
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)
1087             } else {
1088                 return Err((
1089                     pos,
1090                     "Expression trees only support named property access.".into(),
1091                 ));
1092             };
1093             let desugar_expr = v_meth_call(
1094                 et::VISIT_PROPERTY_ACCESS,
1095                 vec![pos_expr, desugar_e1, id],
1096                 &pos,
1097             );
1098             let virtual_expr = Expr(
1099                 (),
1100                 pos,
1101                 ObjGet(Box::new((virtual_e1, e2, null_flavor, is_prop_call))),
1102             );
1103             (virtual_expr, desugar_expr)
1104         }
1105         // Source: MyDsl`<foo my-attr="stuff">text <foo-child/> </foo>`
1106         // Virtualized: <foo my-attr={MyDsl::stringType()}>{MyDsl::stringType()} <foo-child/> </foo>
1107         // Desugared:
1108         //   $0v->visitXhp(
1109         //     newExprPos(...),
1110         //     :foo::class,
1111         //     dict["my-attr" => $0v->visitString(...)],
1112         //     vec[
1113         //       $0v->visitString(..., "text ")],
1114         //       $0v->visitXhp(..., :foo-child::class, ...),
1115         //     ],
1116         //   )
1117         Xml(xml) => {
1118             let (hint, attrs, children) = *xml;
1120             let mut virtual_attrs = vec![];
1121             let mut desugar_attrs = vec![];
1122             for attr in attrs.clone() {
1123                 match attr {
1124                     aast::XhpAttribute::XhpSimple(xs) => {
1125                         let (attr_name_pos, attr_name) = xs.name.clone();
1126                         let dict_key =
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,
1135                             ..xs
1136                         }))
1137                     }
1138                     aast::XhpAttribute::XhpSpread(e) => {
1139                         return Err((
1140                             e.1,
1141                             "Expression trees do not support attribute spread syntax.".into(),
1142                         ));
1143                     }
1144                 }
1145             }
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((
1153                 ClassId(
1154                     (),
1155                     hint_pos.clone(),
1156                     ClassId_::CIexpr(Expr::new(
1157                         (),
1158                         hint_pos.clone(),
1159                         Expr_::Id(Box::new(ast_defs::Id(hint_pos.clone(), hint.1.clone()))),
1160                     )),
1161                 ),
1162                 (hint_pos.clone(), "class".to_string()),
1163             )));
1165             let virtual_expr = Expr(
1166                 (),
1167                 pos.clone(),
1168                 Xml(Box::new((hint, virtual_attrs, virtual_children))),
1169             );
1170             let desugar_expr = v_meth_call(
1171                 et::VISIT_XHP,
1172                 vec![
1173                     pos_expr,
1174                     Expr((), pos.clone(), hint_class),
1175                     dict_literal(&pos, desugar_attrs),
1176                     vec_literal(desugar_children),
1177                 ],
1178                 &pos,
1179             );
1180             (virtual_expr, desugar_expr)
1181         }
1182         ExpressionTree(_) => {
1183             return Err((pos, "Expression trees may not be nested".into()));
1184         }
1185         _ => {
1186             return Err((pos, "Unsupported expression tree syntax.".into()));
1187         }
1188     };
1189     Ok((virtual_expr, desugar_expr))
1192 fn rewrite_exprs(
1193     temps: &mut Temporaries,
1194     exprs: Vec<Expr>,
1195     visitor_name: &str,
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());
1199     for expr in exprs {
1200         let (virtual_expr, desugar_expr) = rewrite_expr(temps, expr, visitor_name)?;
1201         virtual_results.push(virtual_expr);
1202         desugar_results.push(desugar_expr);
1203     }
1204     Ok((virtual_results, desugar_results))
1207 fn rewrite_stmts(
1208     temps: &mut Temporaries,
1209     stmts: Vec<Stmt>,
1210     visitor_name: &str,
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());
1214     for stmt in stmts {
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);
1219         }
1220     }
1221     Ok((virtual_results, desugar_results))
1224 fn rewrite_stmt(
1225     temps: &mut Temporaries,
1226     s: Stmt,
1227     visitor_name: &str,
1228 ) -> Result<(Stmt, Option<Expr>), (Pos, String)> {
1229     use aast::Stmt_::*;
1231     let Stmt(pos, stmt_) = s;
1232     let pos_expr = exprpos(&pos);
1234     let virtual_desugar = match stmt_ {
1235         Expr(e) => {
1236             let (virtual_expr, desugar_expr) = rewrite_expr(temps, *e, visitor_name)?;
1237             (Stmt(pos, Expr(Box::new(virtual_expr))), Some(desugar_expr))
1238         }
1239         Return(e) => match *e {
1240             // Source: MyDsl`return ...;`
1241             // Virtualized: return ...;
1242             // Desugared: $0v->visitReturn(new ExprPos(...), $0v->...)
1243             Some(e) => {
1244                 let (virtual_expr, desugar_expr) = rewrite_expr(temps, e, visitor_name)?;
1245                 let desugar_expr =
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))
1249             }
1250             // Source: MyDsl`return;`
1251             // Virtualized: return MyDsl::voidType();
1252             // Desugared: $0v->visitReturn(new ExprPos(...), null)
1253             None => {
1254                 let desugar_expr = v_meth_call(
1255                     et::VISIT_RETURN,
1256                     vec![pos_expr, null_literal(pos.clone())],
1257                     &pos,
1258                 );
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))
1263             }
1264         },
1265         // Source: MyDsl`if (...) {...} else {...}`
1266         // Virtualized: if (...->__bool())) {...} else {...}
1267         // Desugared: $0v->visitIf(new ExprPos(...), $0v->..., vec[...], vec[...])
1268         If(if_stmt) => {
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(
1277                 et::VISIT_IF,
1278                 vec![
1279                     pos_expr,
1280                     desugar_cond,
1281                     vec_literal(desugar_then),
1282                     vec_literal(desugar_else),
1283                 ],
1284                 &pos,
1285             );
1286             let virtual_stmt = Stmt(
1287                 pos,
1288                 If(Box::new((
1289                     boolify(virtual_cond),
1290                     virtual_then_stmts,
1291                     virtual_else_stmts,
1292                 ))),
1293             );
1294             (virtual_stmt, Some(desugar_expr))
1295         }
1296         // Source: MyDsl`while (...) {...}`
1297         // Virtualized: while (...->__bool()) {...}
1298         // Desugared: $0v->visitWhile(new ExprPos(...), $0v->..., vec[...])
1299         While(w) => {
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(
1305                 et::VISIT_WHILE,
1306                 vec![pos_expr, desugar_cond, vec_literal(desugar_body)],
1307                 &pos,
1308             );
1309             let virtual_stmt = Stmt(
1310                 pos,
1311                 While(Box::new((boolify(virtual_cond), virtual_body_stmts))),
1312             );
1313             (virtual_stmt, Some(desugar_expr))
1314         }
1315         // Source: MyDsl`for (...; ...; ...) {...}`
1316         // Virtualized: for (...; ...->__bool(); ...) {...}
1317         // Desugared: $0v->visitFor(new ExprPos(...), vec[...], ..., vec[...], vec[...])
1318         For(w) => {
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 {
1323                 Some(cond) => {
1324                     let (virtual_cond, desugar_cond) = rewrite_expr(temps, cond, visitor_name)?;
1325                     (Some(boolify(virtual_cond)), desugar_cond)
1326                 }
1327                 None => (None, null_literal(pos.clone())),
1328             };
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(
1334                 et::VISIT_FOR,
1335                 vec![
1336                     pos_expr,
1337                     vec_literal(desugar_init_exprs),
1338                     desugar_cond_expr,
1339                     vec_literal(desugar_incr_exprs),
1340                     vec_literal(desugar_body),
1341                 ],
1342                 &pos,
1343             );
1344             let virtual_stmt = Stmt(
1345                 pos,
1346                 For(Box::new((
1347                     virtual_init_exprs,
1348                     virtual_cond_option,
1349                     virtual_incr_exprs,
1350                     virtual_body_stmts,
1351                 ))),
1352             );
1353             (virtual_stmt, Some(desugar_expr))
1354         }
1355         // Source: MyDsl`break;`
1356         // Virtualized: break;
1357         // Desugared: $0v->visitBreak(new ExprPos(...))
1358         Break => {
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))
1362         }
1363         // Source: MyDsl`continue;`
1364         // Virtualized: continue;
1365         // Desugared: $0v->visitContinue(new ExprPos(...))
1366         Continue => {
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))
1370         }
1371         Noop => (Stmt(pos, Noop), None),
1372         _ => {
1373             return Err((
1374                 pos,
1375                 "Expression trees do not support this statement syntax.".into(),
1376             ));
1377         }
1378     };
1379     Ok(virtual_desugar)
1382 fn hint_name(hint: &aast::Hint) -> Result<String, (Pos, String)> {
1383     if let Hint_::Happly(id, _) = &*hint.1 {
1384         Ok(id.1.clone())
1385     } else {
1386         Err((
1387             hint.0.clone(),
1388             "Could not determine the visitor type for this Expression Tree".into(),
1389         ))
1390     }
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![]));
1398     Expr::new(
1399         (),
1400         pos.clone(),
1401         Expr_::Call(Box::new((lambda_expr, vec![], vec![], None))),
1402     )
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..],
1415         _ => name,
1416     }
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:
1426 ///     shape(
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<>],
1432 ///     )
1433 fn maketree_metadata(
1434     pos: &Pos,
1435     splices: &[Expr],
1436     functions: &[Expr],
1437     static_methods: &[Expr],
1438 ) -> Expr {
1439     let key_value_pairs = splices
1440         .iter()
1441         .enumerate()
1442         .map(|(i, expr)| {
1443             let key = Expr::new(
1444                 (),
1445                 expr.1.clone(),
1446                 Expr_::String(BString::from(temp_splice_lvar_string(i))),
1447             );
1448             let value = temp_splice_lvar(&expr.1, i);
1449             (key, value)
1450         })
1451         .collect();
1452     let splices_dict = dict_literal(pos, key_value_pairs);
1454     let function_vars = functions
1455         .iter()
1456         .enumerate()
1457         .map(|(i, expr)| temp_function_pointer_lvar(&expr.1, i))
1458         .collect();
1459     let functions_vec = vec_literal_with_pos(&pos, function_vars);
1461     let static_method_vars = static_methods
1462         .iter()
1463         .enumerate()
1464         .map(|(i, expr)| temp_static_method_lvar(&expr.1, i))
1465         .collect();
1466     let static_method_vec = vec_literal_with_pos(&pos, static_method_vars);
1468     shape_literal(
1469         pos,
1470         vec![
1471             ("splices", splices_dict),
1472             ("functions", functions_vec),
1473             ("static_methods", static_method_vec),
1474         ],
1475     )
1478 fn global_func_ptr(sid: &Sid) -> Expr {
1479     let pos = sid.0.clone();
1480     Expr::new(
1481         (),
1482         pos.clone(),
1483         Expr_::FunctionPointer(Box::new((ast::FunctionPtrId::FPId(sid.clone()), vec![]))),
1484     )
1487 fn static_meth_ptr(pos: &Pos, cid: &ClassId, meth: &Pstring) -> Expr {
1488     Expr::new(
1489         (),
1490         pos.clone(),
1491         Expr_::FunctionPointer(Box::new((
1492             aast::FunctionPtrId::FPClassConst(cid.clone(), meth.clone()),
1493             vec![],
1494         ))),
1495     )