Handle Pure<> in the lowerer
[hiphop-php.git] / hphp / hack / src / naming / elaborate_namespaces_visitor.rs
blob94669425db21869010c85c7f87b1c4606692276a
1 // Copyright (c) Facebook, Inc. and its affiliates.
2 //
3 // This source code is licensed under the MIT license found in the
4 // LICENSE file in the "hack" directory of this source tree.
6 use core_utils_rust as core_utils;
7 use namespaces_rust as namespaces;
8 use naming_special_names_rust as sn;
9 use std::collections::HashSet;
11 use oxidized::{
12     aast_visitor::{AstParams, NodeMut, VisitorMut},
13     ast::*,
14     namespace_env,
17 fn is_special_identifier(name: &str) -> bool {
18     use lazy_static::lazy_static;
20     lazy_static! {
21         static ref SPECIAL_IDENTIFIERS: HashSet<&'static str> = vec![
22             sn::members::M_CLASS,
23             sn::classes::PARENT,
24             sn::classes::SELF,
25             sn::classes::STATIC,
26             sn::special_idents::THIS,
27             sn::special_idents::DOLLAR_DOLLAR,
28             sn::typehints::WILDCARD,
29         ]
30         .into_iter()
31         .collect();
32     }
34     SPECIAL_IDENTIFIERS.contains(&name)
37 #[derive(Clone)]
38 struct Env {
39     // TODO(hrust) I wanted to make namespace and type_params str's references but couldn't
40     // find a way to specify that the lifetimes of these outlived the node I was taking them from
41     namespace: ocamlrep::rc::RcOc<namespace_env::Env>,
42     type_params: HashSet<String>,
45 impl Env {
46     fn make(namespace: ocamlrep::rc::RcOc<namespace_env::Env>) -> Self {
47         Self {
48             namespace,
49             type_params: HashSet::new(),
50         }
51     }
53     // TODO: While elaboration for codegen and typing is similar, there are currently a
54     // couple differences between the two and are toggled by this flag (XHP).
55     // It would be nice to eventually eliminate the discrepancies between the two.
56     pub fn in_codegen(&self) -> bool {
57         self.namespace.is_codegen
58     }
60     fn extend_tparams(&mut self, tparaml: &[Tparam]) {
61         for tparam in tparaml {
62             self.type_params.insert(tparam.name.1.clone());
63         }
64     }
66     fn elaborate_type_name(&self, id: &mut Id) {
67         let name = &id.1;
68         if self.type_params.contains::<str>(name)
69             || is_special_identifier(name)
70             || name.starts_with("$")
71         {
72             return;
73         } else {
74             id.1 =
75                 namespaces::elaborate_id(&self.namespace, namespaces::ElaborateKind::Class, id).1;
76         }
77     }
79     fn handle_special_calls(&self, call: &mut Expr_) {
80         match call {
81             Expr_::Call(c) => {
82                 let (func, args) = (&c.0, &mut c.2);
83                 match func {
84                     Expr(_, Expr_::Id(id))
85                         if id.1 == sn::autoimported_functions::FUN_ && args.len() == 1 =>
86                     {
87                         match &args[0] {
88                             Expr(p, Expr_::String(fn_name)) => {
89                                 let fn_name = core_utils::add_ns_bstr(&fn_name);
90                                 args[0] =
91                                     Expr(p.clone(), Expr_::String(fn_name.into_owned().into()));
92                             }
93                             _ => {}
94                         }
95                     }
96                     Expr(_, Expr_::Id(id))
97                         if (id.1 == sn::autoimported_functions::METH_CALLER
98                             || id.1 == sn::autoimported_functions::CLASS_METH)
99                             && args.len() == 2
100                             && !self.in_codegen() =>
101                     {
102                         match &args[0] {
103                             Expr(p, Expr_::String(cl_name)) => {
104                                 let cl_name = core_utils::add_ns_bstr(&cl_name);
105                                 args[0] =
106                                     Expr(p.clone(), Expr_::String(cl_name.into_owned().into()));
107                             }
108                             _ => {}
109                         }
110                     }
111                     _ => {}
112                 }
113             }
114             _ => {}
115         }
116     }
119 fn is_reserved_type_hint(name: &str) -> bool {
120     let base_name = core_utils::strip_ns(name);
121     return sn::typehints::is_reserved_type_hint(&base_name) || sn::rx::is_reactive_typehint(name);
124 struct ElaborateNamespacesVisitor {}
126 impl<'ast> VisitorMut<'ast> for ElaborateNamespacesVisitor {
127     type P = AstParams<Env, ()>;
129     fn object(&mut self) -> &mut dyn VisitorMut<'ast, P = Self::P> {
130         self
131     }
133     // Namespaces were already precomputed by ElaborateDefs
134     // The following functions just set the namespace env correctly
135     fn visit_class_(&mut self, env: &mut Env, cd: &mut Class_) -> Result<(), ()> {
136         let mut env = env.clone();
137         env.namespace = cd.namespace.clone();
138         env.extend_tparams(&cd.tparams);
139         cd.recurse(&mut env, self.object())
140     }
142     fn visit_typedef(&mut self, env: &mut Env, td: &mut Typedef) -> Result<(), ()> {
143         let mut env = env.clone();
144         env.namespace = td.namespace.clone();
145         env.extend_tparams(&td.tparams);
146         td.recurse(&mut env, self.object())
147     }
149     fn visit_def(&mut self, env: &mut Env, def: &mut Def) -> Result<(), ()> {
150         match &def {
151             // need to handle it ourselves, because in visit_fun_ is
152             // called both for toplevel functions and lambdas
153             Def::Fun(f) => {
154                 let mut env = env.clone();
155                 env.namespace = f.namespace.clone();
156                 env.extend_tparams(&f.tparams);
157                 def.recurse(&mut env, self.object())
158             }
159             Def::SetNamespaceEnv(nsenv) => Ok(env.namespace = (**nsenv).clone()),
160             _ => def.recurse(env, self.object()),
161         }
162     }
164     fn visit_method_(&mut self, env: &mut Env, m: &mut Method_) -> Result<(), ()> {
165         let mut env = env.clone();
166         env.extend_tparams(&m.tparams);
167         m.recurse(&mut env, self.object())
168     }
170     fn visit_pu_enum(&mut self, env: &mut Env, pue: &mut PuEnum) -> Result<(), ()> {
171         let mut env = env.clone();
172         env.extend_tparams(&pue.case_types);
173         pue.recurse(&mut env, self.object())
174     }
176     fn visit_gconst(&mut self, env: &mut Env, gc: &mut Gconst) -> Result<(), ()> {
177         let mut env = env.clone();
178         env.namespace = gc.namespace.clone();
179         gc.recurse(&mut env, self.object())
180     }
182     fn visit_file_attribute(&mut self, env: &mut Env, fa: &mut FileAttribute) -> Result<(), ()> {
183         let mut env = env.clone();
184         env.namespace = fa.namespace.clone();
185         fa.recurse(&mut env, self.object())
186     }
188     fn visit_record_def(&mut self, env: &mut Env, rd: &mut RecordDef) -> Result<(), ()> {
189         rd.name =
190             namespaces::elaborate_id(&env.namespace, namespaces::ElaborateKind::Record, &rd.name);
191         match &mut rd.extends {
192             Some(Hint(_, h_)) => {
193                 let h = h_.as_mut();
194                 match h {
195                     Hint_::Happly(sid, _hl) => {
196                         let new_name = namespaces::elaborate_id(
197                             &env.namespace,
198                             namespaces::ElaborateKind::Record,
199                             &sid,
200                         );
201                         *sid = new_name;
202                     }
203                     _ => {}
204                 }
205             }
206             _ => {}
207         }
208         rd.recurse(env, self.object())
209     }
211     // I don't think we need to visit blocks because we got rid of let bindings :)
213     fn visit_catch(&mut self, env: &mut Env, catch: &mut Catch) -> Result<(), ()> {
214         let exception_sid = &mut catch.0;
215         env.elaborate_type_name(exception_sid);
216         catch.recurse(env, self.object())
217     }
219     // I don't think we need to visit stmts because we got rid of let bindings :)
221     // Lfun and Efun as Expr_:: nodes so update the env in visit_expr_
223     // Actually rewrites the names
224     fn visit_expr_(&mut self, env: &mut Env, e: &mut Expr_) -> Result<(), ()> {
225         // Sets env for lambdas
226         match e {
227             Expr_::Call(c) => {
228                 let (func, targs, args, uargs) = (&mut c.0, &mut c.1, &mut c.2, &mut c.3);
230                 // Recurse first due to borrow order
231                 targs.accept(env, self.object())?;
232                 args.accept(env, self.object())?;
233                 uargs.accept(env, self.object())?;
235                 if let Some(sid) = func.1.as_id_mut() {
236                     if !sn::special_functions::is_special_function(&sid.1) {
237                         sid.1 = namespaces::elaborate_id(
238                             &env.namespace,
239                             namespaces::ElaborateKind::Fun,
240                             sid,
241                         )
242                         .1;
243                         env.handle_special_calls(e);
244                     }
245                 } else {
246                     func.accept(env, self.object())?;
247                 }
248             }
249             Expr_::FunctionPointer(fp) => {
250                 let (fpid, targs) = (&mut fp.0, &mut fp.1);
251                 if let Some(sid) = fpid.as_fpid_mut() {
252                     sid.1 = namespaces::elaborate_id(
253                         &env.namespace,
254                         namespaces::ElaborateKind::Fun,
255                         sid,
256                     )
257                     .1;
258                 } else if let Some(cc) = fpid.as_fpclass_const_mut() {
259                     let type_ = cc.0;
260                     if let Some(e) = type_.1.as_ciexpr_mut() {
261                         if let Some(sid) = e.1.as_id_mut() {
262                             env.elaborate_type_name(sid);
263                         } else {
264                             e.accept(env, self.object())?;
265                         }
266                     }
267                 } else {
268                     fpid.accept(env, self.object())?;
269                 }
270                 targs.accept(env, self.object())?;
271             }
272             Expr_::ObjGet(og) => {
273                 let (obj, expr, nullsafe) = (&mut og.0, &mut og.1, &mut og.2);
274                 if let Expr_::Id(..) = expr.1 {
275                 } else {
276                     expr.accept(env, self.object())?;
277                 }
279                 obj.accept(env, self.object())?;
280                 nullsafe.accept(env, self.object())?;
281             }
282             Expr_::Id(sid) if !((sid.1 == "NAN" || sid.1 == "INF") && env.in_codegen()) => {
283                 sid.1 =
284                     namespaces::elaborate_id(&env.namespace, namespaces::ElaborateKind::Const, sid)
285                         .1;
286             }
287             Expr_::PUIdentifier(pui) => {
288                 let class_id = &mut pui.0;
289                 if let Some(e) = class_id.1.as_ciexpr_mut() {
290                     if let Some(sid) = e.1.as_id_mut() {
291                         env.elaborate_type_name(sid);
292                     } else {
293                         e.accept(env, self.object())?;
294                     }
295                 } else {
296                     class_id.accept(env, self.object())?;
297                 }
298             }
299             Expr_::New(n) => {
300                 let (class_id, targs, args, unpacked_el) = (&mut n.0, &mut n.1, &mut n.2, &mut n.3);
301                 if let Some(e) = class_id.1.as_ciexpr_mut() {
302                     if let Some(sid) = e.1.as_id_mut() {
303                         env.elaborate_type_name(sid);
304                     } else {
305                         e.accept(env, self.object())?;
306                     }
307                 } else {
308                     class_id.accept(env, self.object())?;
309                 }
310                 targs.accept(env, self.object())?;
311                 args.accept(env, self.object())?;
312                 unpacked_el.accept(env, self.object())?;
313             }
314             Expr_::Record(r) => {
315                 let record_name = &mut r.0;
316                 env.elaborate_type_name(record_name);
317             }
318             Expr_::ClassConst(cc) => {
319                 let type_ = &mut cc.0;
320                 if let Some(e) = type_.1.as_ciexpr_mut() {
321                     if let Some(sid) = e.1.as_id_mut() {
322                         env.elaborate_type_name(sid);
323                     } else {
324                         e.accept(env, self.object())?;
325                     }
326                 } else {
327                     type_.accept(env, self.object())?;
328                 }
329             }
330             Expr_::ClassGet(cg) => {
331                 let (class_id, class_get_expr) = (&mut cg.0, &mut cg.1);
332                 if let Some(e) = class_id.1.as_ciexpr_mut() {
333                     if let Some(sid) = e.1.as_id_mut() {
334                         env.elaborate_type_name(sid);
335                     } else {
336                         e.accept(env, self.object())?;
337                     }
338                 } else {
339                     class_id.accept(env, self.object())?;
340                 }
341                 class_get_expr.accept(env, self.object())?;
342             }
343             Expr_::Xml(x) => {
344                 let (xml_id, attributes, el) = (&mut x.0, &mut x.1, &mut x.2);
345                 /* if XHP element mangling is disabled, namespaces are supported */
346                 if !env.in_codegen() || env.namespace.disable_xhp_element_mangling {
347                     env.elaborate_type_name(xml_id);
348                 }
349                 attributes.recurse(env, self.object())?;
350                 el.recurse(env, self.object())?;
351             }
352             _ => e.recurse(env, self.object())?,
353         }
354         Ok(())
355     }
357     fn visit_hint_(&mut self, env: &mut Env, hint: &mut Hint_) -> Result<(), ()> {
358         fn is_rx(x: &str) -> bool {
359             x == sn::rx::RX || x == sn::rx::RX_LOCAL || x == sn::rx::RX_SHALLOW || x == sn::rx::PURE
360         }
361         fn is_xhp_screwup(x: &str) -> bool {
362             x == "Xhp" || x == ":Xhp" || x == "XHP"
363         }
364         match hint {
365             Hint_::Happly(sid, _) if is_xhp_screwup(&sid.1) => {}
366             Hint_::Happly(sid, hints) if is_rx(&sid.1) && hints.len() == 1 => match *hints[0].1 {
367                 Hint_::Hfun(_) => {}
368                 _ => {
369                     env.elaborate_type_name(sid);
370                 }
371             },
372             Hint_::Happly(sid, _) if is_rx(&sid.1) => {
373                 env.elaborate_type_name(sid);
374             }
375             Hint_::Happly(sid, _) if is_reserved_type_hint(&sid.1) && !env.in_codegen() => {}
376             Hint_::Happly(sid, _) => {
377                 env.elaborate_type_name(sid);
378             }
379             _ => {}
380         }
381         hint.recurse(env, self.object())
382     }
384     fn visit_shape_field_name(
385         &mut self,
386         env: &mut Env,
387         sfn: &mut ShapeFieldName,
388     ) -> Result<(), ()> {
389         match sfn {
390             ShapeFieldName::SFclassConst(id, _) => {
391                 env.elaborate_type_name(id);
392             }
393             _ => {}
394         }
395         sfn.recurse(env, self.object())
396     }
398     fn visit_user_attribute(&mut self, env: &mut Env, ua: &mut UserAttribute) -> Result<(), ()> {
399         if !sn::user_attributes::is_reserved(&ua.name.1) {
400             env.elaborate_type_name(&mut ua.name);
401         }
402         ua.recurse(env, self.object())
403     }
405     fn visit_insteadof_alias(
406         &mut self,
407         env: &mut Env,
408         alias: &mut InsteadofAlias,
409     ) -> Result<(), ()> {
410         let (replacement_sid, orig_sids) = (&mut alias.0, &mut alias.2);
411         env.elaborate_type_name(replacement_sid);
412         for sid in orig_sids.iter_mut() {
413             env.elaborate_type_name(sid);
414         }
415         alias.recurse(env, self.object())
416     }
418     fn visit_use_as_alias(&mut self, env: &mut Env, alias: &mut UseAsAlias) -> Result<(), ()> {
419         let sid_option = &mut alias.0;
420         Ok(match sid_option {
421             Some(sid) => {
422                 env.elaborate_type_name(sid);
423             }
424             _ => {}
425         })
426     }
428     fn visit_xhp_child(&mut self, env: &mut Env, child: &mut XhpChild) -> Result<(), ()> {
429         match child {
430             XhpChild::ChildName(sid)
431                 if !env.in_codegen()
432                     && !sn::xhp::is_reserved(&sid.1)
433                     && !sn::xhp::is_xhp_category(&sid.1) =>
434             {
435                 env.elaborate_type_name(sid);
436             }
437             _ => {}
438         }
439         child.recurse(env, self.object())
440     }
443 pub fn elaborate_program(e: ocamlrep::rc::RcOc<namespace_env::Env>, defs: &mut Program) {
444     let mut env = Env::make(e);
445     let mut visitor = ElaborateNamespacesVisitor {};
446     for mut def in defs.into_iter() {
447         visitor.visit_def(&mut env, &mut def).unwrap();
448     }