Tweak `HhasTypeConstant` C++: Replace Option with Maybe
[hiphop-php.git] / hphp / hack / src / hhbc / hhbc_by_ref / ast_scope.rs
blob77dde9fa99d248d1d4bfee800e762ac6344bd213
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 mod ast_scope_item;
7 pub use ast_scope_item::*;
9 use hhbc_by_ref_hhas_coeffects::HhasCoeffects;
10 use oxidized::{
11     ast,
12     ast_defs::{FunKind, Id},
13     pos::Pos,
16 #[derive(Clone, Default, Debug)]
17 pub struct Scope<'a> {
18     pub items: Vec<ScopeItem<'a>>,
21 impl<'a> Scope<'a> {
22     pub fn toplevel() -> Self {
23         Scope { items: vec![] }
24     }
26     pub fn push_item(&mut self, s: ScopeItem<'a>) {
27         self.items.push(s)
28     }
30     pub fn iter(&self) -> impl ExactSizeIterator<Item = &ScopeItem<'a>> {
31         self.items.iter().rev()
32     }
34     pub fn iter_subscopes(&self) -> impl Iterator<Item = &[ScopeItem<'a>]> {
35         (0..self.items.len()).rev().map(move |i| &self.items[..i])
36     }
38     pub fn get_subscope_class<'b>(sub_scope: &'b [ScopeItem<'b>]) -> Option<&'b Class> {
39         for scope_item in sub_scope.iter().rev() {
40             if let ScopeItem::Class(cd) = scope_item {
41                 return Some(cd);
42             }
43         }
44         None
45     }
47     pub fn get_class(&self) -> Option<&Class> {
48         Self::get_subscope_class(&self.items[..])
49     }
51     pub fn get_span(&self) -> Pos {
52         for scope_item in self.iter() {
53             match scope_item {
54                 ScopeItem::Class(cd) => {
55                     return cd.get_span().clone();
56                 }
57                 ScopeItem::Function(fd) => {
58                     return fd.get_span().clone();
59                 }
60                 ScopeItem::Method(md) => {
61                     return md.get_span().clone();
62                 }
63                 _ => {}
64             }
65         }
66         Pos::make_none()
67     }
69     pub fn get_tparams(&self) -> Vec<&ast::Tparam> {
70         let mut tparams = vec![];
71         let extend_shallowly = &mut |tps| {
72             for tparam in tps {
73                 tparams.push(tparam);
74             }
75         };
76         for scope_item in self.iter() {
77             match scope_item {
78                 ScopeItem::Class(cd) => {
79                     extend_shallowly(cd.get_tparams());
80                 }
81                 ScopeItem::Function(fd) => {
82                     extend_shallowly(fd.get_tparams());
83                 }
84                 ScopeItem::Method(md) => {
85                     extend_shallowly(md.get_tparams());
86                 }
87                 _ => {}
88             }
89         }
90         tparams
91     }
93     pub fn get_fun_tparams(&self) -> &[ast::Tparam] {
94         for scope_item in self.iter() {
95             match scope_item {
96                 ScopeItem::Class(_) => {
97                     return &[];
98                 }
99                 ScopeItem::Function(fd) => {
100                     return fd.get_tparams();
101                 }
102                 ScopeItem::Method(md) => {
103                     return md.get_tparams();
104                 }
105                 _ => {}
106             }
107         }
108         &[]
109     }
111     pub fn get_class_tparams(&self) -> &[ast::Tparam] {
112         for scope_item in self.iter() {
113             if let ScopeItem::Class(cd) = scope_item {
114                 return cd.get_tparams();
115             }
116         }
117         &[]
118     }
120     pub fn has_this(&self) -> bool {
121         if self.items.is_empty() {
122             /* Assume top level has this */
123             return true;
124         }
125         for scope_item in self.iter() {
126             match scope_item {
127                 ScopeItem::Class(_) | ScopeItem::Function(_) => {
128                     return false;
129                 }
130                 ScopeItem::Method(_) => {
131                     return true;
132                 }
133                 _ => {}
134             }
135         }
136         false
137     }
139     pub fn is_in_async(&self) -> bool {
140         for scope_item in self.iter() {
141             match scope_item {
142                 ScopeItem::Class(_) => {
143                     return false;
144                 }
145                 ScopeItem::Method(m) => {
146                     let fun_kind = m.get_fun_kind();
147                     return fun_kind == FunKind::FAsync || fun_kind == FunKind::FAsyncGenerator;
148                 }
149                 ScopeItem::Function(f) => {
150                     let fun_kind = f.get_fun_kind();
151                     return fun_kind == FunKind::FAsync || fun_kind == FunKind::FAsyncGenerator;
152                 }
153                 _ => {}
154             }
155         }
156         false
157     }
159     pub fn is_toplevel(&self) -> bool {
160         self.items.is_empty()
161     }
163     pub fn is_in_static_method(&self) -> bool {
164         for scope_item in self.iter() {
165             match scope_item {
166                 ScopeItem::Method(md) => {
167                     return md.is_static();
168                 }
169                 ScopeItem::LongLambda(_) => {}
170                 ScopeItem::Lambda(_) => {}
171                 _ => return false,
172             }
173         }
174         false
175     }
177     pub fn is_in_lambda(&self) -> bool {
178         self.items.last().map_or(false, &ScopeItem::is_in_lambda)
179     }
181     pub fn coeffects_of_scope(&self) -> HhasCoeffects {
182         for scope_item in self.iter() {
183             match scope_item {
184                 ScopeItem::Class(_) => {
185                     return HhasCoeffects::default();
186                 }
187                 ScopeItem::Method(m) => {
188                     return HhasCoeffects::from_ast(
189                         m.get_ctxs(),
190                         m.get_params(),
191                         m.get_tparams(),
192                         self.get_class_tparams(),
193                     );
194                 }
195                 ScopeItem::Function(f) => {
196                     return HhasCoeffects::from_ast(
197                         f.get_ctxs(),
198                         f.get_params(),
199                         f.get_tparams(),
200                         vec![],
201                     );
202                 }
203                 ScopeItem::Lambda(Lambda { coeffects, .. })
204                 | ScopeItem::LongLambda(LongLambda { coeffects, .. })
205                     if !coeffects.get_static_coeffects().is_empty() =>
206                 {
207                     return coeffects.clone();
208                 }
209                 _ => {}
210             }
211         }
212         HhasCoeffects::default()
213     }
215     pub fn has_function_attribute(&self, attr_name: impl AsRef<str>) -> bool {
216         let has = |ua: &[ast::UserAttribute]| ua.iter().any(|a| a.name.1 == attr_name.as_ref());
217         for scope_item in self.iter() {
218             match scope_item {
219                 ScopeItem::Method(m) => {
220                     return has(m.get_user_attributes());
221                 }
222                 ScopeItem::Function(f) => {
223                     return has(f.get_user_attributes());
224                 }
225                 _ => {}
226             }
227         }
228         false
229     }
231     pub fn is_static(&self) -> bool {
232         for x in self.iter() {
233             match x {
234                 ScopeItem::Function(_) => return true,
235                 ScopeItem::Method(md) => return md.is_static(),
236                 ScopeItem::Lambda(_) | ScopeItem::LongLambda(_) => continue,
237                 _ => return true,
238             }
239         }
240         true
241     }
243     // get captured variables when in closure scope
244     pub fn get_captured_vars(&self) -> Vec<String> {
245         // closure scope: lambda -> method -> class
246         match &self.items[..] {
247             [.., ScopeItem::Class(ast_cls), _, _] => ast_cls
248                 .get_vars()
249                 .iter()
250                 .map(|var| {
251                     let Id(_, id) = &var.id;
252                     format!("${}", id)
253                 })
254                 .collect::<Vec<_>>(),
255             _ => panic!("closure scope should be lambda -> method -> class"),
256         }
257     }
260     pub fn is_in_debugger_eval_fun(&self) -> bool {
261         for x in self.iter() {
262             match x {
263                 ScopeItem::LongLambda(_) | ScopeItem::Lambda(_) => continue,
264                 ScopeItem::Function(f) => return f.get_name().1 == "include",
265                 _ => return false,
266             }
267         }
268         true
269     }