1 // Copyright (c) Facebook, Inc. and its affiliates.
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;
12 aast_visitor::{AstParams, NodeMut, VisitorMut},
17 fn is_special_identifier(name: &str) -> bool {
18 use lazy_static::lazy_static;
21 static ref SPECIAL_IDENTIFIERS: HashSet<&'static str> = vec![
26 sn::special_idents::THIS,
27 sn::special_idents::DOLLAR_DOLLAR,
28 sn::typehints::WILDCARD,
34 SPECIAL_IDENTIFIERS.contains(&name)
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>,
46 fn make(namespace: ocamlrep::rc::RcOc<namespace_env::Env>) -> Self {
49 type_params: HashSet::new(),
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
60 fn extend_tparams(&mut self, tparaml: &[Tparam]) {
61 for tparam in tparaml {
62 self.type_params.insert(tparam.name.1.clone());
66 fn elaborate_type_name(&self, id: &mut Id) {
68 if self.type_params.contains::<str>(name)
69 || is_special_identifier(name)
70 || name.starts_with("$")
75 namespaces::elaborate_id(&self.namespace, namespaces::ElaborateKind::Class, id).1;
79 fn handle_special_calls(&self, call: &mut Expr_) {
82 let (func, args) = (&c.0, &mut c.2);
84 Expr(_, Expr_::Id(id))
85 if id.1 == sn::autoimported_functions::FUN_ && args.len() == 1 =>
88 Expr(p, Expr_::String(fn_name)) => {
89 let fn_name = core_utils::add_ns_bstr(&fn_name);
91 Expr(p.clone(), Expr_::String(fn_name.into_owned().into()));
96 Expr(_, Expr_::Id(id))
97 if (id.1 == sn::autoimported_functions::METH_CALLER
98 || id.1 == sn::autoimported_functions::CLASS_METH)
100 && !self.in_codegen() =>
103 Expr(p, Expr_::String(cl_name)) => {
104 let cl_name = core_utils::add_ns_bstr(&cl_name);
106 Expr(p.clone(), Expr_::String(cl_name.into_owned().into()));
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> {
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())
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())
149 fn visit_def(&mut self, env: &mut Env, def: &mut Def) -> Result<(), ()> {
151 // need to handle it ourselves, because in visit_fun_ is
152 // called both for toplevel functions and lambdas
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())
159 Def::SetNamespaceEnv(nsenv) => Ok(env.namespace = (**nsenv).clone()),
160 _ => def.recurse(env, self.object()),
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())
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())
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())
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())
188 fn visit_record_def(&mut self, env: &mut Env, rd: &mut RecordDef) -> Result<(), ()> {
190 namespaces::elaborate_id(&env.namespace, namespaces::ElaborateKind::Record, &rd.name);
191 match &mut rd.extends {
192 Some(Hint(_, h_)) => {
195 Hint_::Happly(sid, _hl) => {
196 let new_name = namespaces::elaborate_id(
198 namespaces::ElaborateKind::Record,
208 rd.recurse(env, self.object())
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())
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
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(
239 namespaces::ElaborateKind::Fun,
243 env.handle_special_calls(e);
246 func.accept(env, self.object())?;
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(
254 namespaces::ElaborateKind::Fun,
258 } else if let Some(cc) = fpid.as_fpclass_const_mut() {
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);
264 e.accept(env, self.object())?;
268 fpid.accept(env, self.object())?;
270 targs.accept(env, self.object())?;
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 {
276 expr.accept(env, self.object())?;
279 obj.accept(env, self.object())?;
280 nullsafe.accept(env, self.object())?;
282 Expr_::Id(sid) if !((sid.1 == "NAN" || sid.1 == "INF") && env.in_codegen()) => {
284 namespaces::elaborate_id(&env.namespace, namespaces::ElaborateKind::Const, sid)
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);
293 e.accept(env, self.object())?;
296 class_id.accept(env, self.object())?;
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);
305 e.accept(env, self.object())?;
308 class_id.accept(env, self.object())?;
310 targs.accept(env, self.object())?;
311 args.accept(env, self.object())?;
312 unpacked_el.accept(env, self.object())?;
314 Expr_::Record(r) => {
315 let record_name = &mut r.0;
316 env.elaborate_type_name(record_name);
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);
324 e.accept(env, self.object())?;
327 type_.accept(env, self.object())?;
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);
336 e.accept(env, self.object())?;
339 class_id.accept(env, self.object())?;
341 class_get_expr.accept(env, self.object())?;
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);
349 attributes.recurse(env, self.object())?;
350 el.recurse(env, self.object())?;
352 _ => e.recurse(env, self.object())?,
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
361 fn is_xhp_screwup(x: &str) -> bool {
362 x == "Xhp" || x == ":Xhp" || x == "XHP"
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 {
369 env.elaborate_type_name(sid);
372 Hint_::Happly(sid, _) if is_rx(&sid.1) => {
373 env.elaborate_type_name(sid);
375 Hint_::Happly(sid, _) if is_reserved_type_hint(&sid.1) && !env.in_codegen() => {}
376 Hint_::Happly(sid, _) => {
377 env.elaborate_type_name(sid);
381 hint.recurse(env, self.object())
384 fn visit_shape_field_name(
387 sfn: &mut ShapeFieldName,
388 ) -> Result<(), ()> {
390 ShapeFieldName::SFclassConst(id, _) => {
391 env.elaborate_type_name(id);
395 sfn.recurse(env, self.object())
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);
402 ua.recurse(env, self.object())
405 fn visit_insteadof_alias(
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);
415 alias.recurse(env, self.object())
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 {
422 env.elaborate_type_name(sid);
428 fn visit_xhp_child(&mut self, env: &mut Env, child: &mut XhpChild) -> Result<(), ()> {
430 XhpChild::ChildName(sid)
432 && !sn::xhp::is_reserved(&sid.1)
433 && !sn::xhp::is_xhp_category(&sid.1) =>
435 env.elaborate_type_name(sid);
439 child.recurse(env, self.object())
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();