Remove push/pop_local_changes from FileProvider trait
[hiphop-php.git] / hphp / hack / src / rupro / hackrs / decl_parser.rs
blob0d958c630be29eb1d923eccec0dcbdcbb0fd8535
1 // Copyright (c) Meta Platforms, Inc. and 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 crate::special_names;
7 use arena_collections::list::List;
8 use file_provider::FileProvider;
9 use names::FileSummary;
10 use obr::{
11     decl_parser_options::DeclParserOptions,
12     direct_decl_parser::Decls,
13     parser_options::ParserOptions,
14     shallow_decl_defs::{Decl, ShallowClass},
16 use oxidized_by_ref as obr;
17 use pos::{RelativePath, TypeName};
18 use std::marker::PhantomData;
19 use std::sync::Arc;
20 use ty::decl::shallow;
21 use ty::reason::Reason;
23 mod options;
24 use options::Options;
26 #[derive(Debug, Clone)]
27 pub struct DeclParser<R: Reason> {
28     file_provider: Arc<dyn FileProvider>,
29     opts: Options,
30     // We could make our parse methods generic over `R` instead, but it's
31     // usually more convenient for callers (especially tests) to pin the decl
32     // parser to a single Reason type.
33     _phantom: PhantomData<R>,
36 impl<R: Reason> DeclParser<R> {
37     pub fn new(file_provider: Arc<dyn FileProvider>) -> Self {
38         Self {
39             file_provider,
40             opts: Default::default(),
41             _phantom: PhantomData,
42         }
43     }
45     pub fn with_options(file_provider: Arc<dyn FileProvider>, opts: &ParserOptions<'_>) -> Self {
46         Self {
47             file_provider,
48             opts: Options::from(opts),
49             _phantom: PhantomData,
50         }
51     }
53     pub fn parse(&self, path: RelativePath) -> anyhow::Result<Vec<shallow::Decl<R>>> {
54         let arena = bumpalo::Bump::new();
55         let text = self.file_provider.get_contents(path)?;
56         let decl_parser_opts = DeclParserOptions::from_parser_options(self.opts.get());
57         let parsed_file = self.parse_impl(&decl_parser_opts, path, &text, &arena);
58         Ok(parsed_file.decls.iter().map(Into::into).collect())
59     }
61     pub fn parse_and_summarize(
62         &self,
63         path: RelativePath,
64     ) -> anyhow::Result<(Vec<shallow::Decl<R>>, FileSummary)> {
65         let arena = bumpalo::Bump::new();
66         let text = self.file_provider.get_contents(path)?;
67         let opts = DeclParserOptions::from(self.opts.get());
68         let parsed_file = self.parse_impl(&opts, path, &text, &arena);
69         let summary = FileSummary::from_decls(parsed_file);
70         Ok((parsed_file.decls.iter().map(Into::into).collect(), summary))
71     }
73     fn parse_impl<'a>(
74         &self,
75         opts: &'a DeclParserOptions<'a>,
76         path: RelativePath,
77         text: &'a [u8],
78         arena: &'a bumpalo::Bump,
79     ) -> oxidized_by_ref::direct_decl_parser::ParsedFile<'a> {
80         let mut parsed_file = direct_decl_parser::parse_decls(opts, path.into(), text, arena);
81         // TODO: The direct decl parser should return decls in the same
82         // order as they are declared in the file. At the moment it reverses
83         // them. Reverse them again to match the syntactic order.
84         let parser_options = self.opts.get();
85         let deregister_std_lib = path.is_hhi() && parser_options.po_deregister_php_stdlib;
86         if deregister_std_lib {
87             parsed_file.decls = Decls(List::rev_from_iter_in(
88                 (parsed_file.decls.iter()).filter_map(|d| remove_php_stdlib_decls(arena, d)),
89                 arena,
90             ));
91         } else {
92             parsed_file.decls.rev(arena);
93         }
94         parsed_file
95     }
98 // note(sf, 2022-04-12, c.f.
99 // `hphp/hack/src/providers/direct_decl_utils.ml`)
100 fn remove_php_stdlib_decls<'a>(
101     arena: &'a bumpalo::Bump,
102     (name, decl): (&'a str, Decl<'a>),
103 ) -> Option<(&'a str, Decl<'a>)> {
104     match decl {
105         Decl::Fun(fun) if fun.php_std_lib => None,
106         Decl::Class(class)
107             if (class.user_attributes.iter()).any(|ua| {
108                 TypeName::new(ua.name.1) == *special_names::user_attributes::uaPHPStdLib
109             }) =>
110         {
111             None
112         }
113         Decl::Class(class) => {
114             let props = bumpalo::collections::Vec::from_iter_in(
115                 (class.props.iter())
116                     .filter(|p| !p.flags.contains(obr::prop_flags::PropFlags::PHP_STD_LIB))
117                     .copied(),
118                 arena,
119             )
120             .into_bump_slice();
121             let sprops = bumpalo::collections::Vec::from_iter_in(
122                 (class.sprops.iter())
123                     .filter(|p| !p.flags.contains(obr::prop_flags::PropFlags::PHP_STD_LIB))
124                     .copied(),
125                 arena,
126             )
127             .into_bump_slice();
128             let methods = bumpalo::collections::Vec::from_iter_in(
129                 (class.methods.iter())
130                     .filter(|m| {
131                         !m.flags
132                             .contains(obr::method_flags::MethodFlags::PHP_STD_LIB)
133                     })
134                     .copied(),
135                 arena,
136             )
137             .into_bump_slice();
138             let static_methods = bumpalo::collections::Vec::from_iter_in(
139                 (class.static_methods.iter())
140                     .filter(|m| {
141                         !m.flags
142                             .contains(obr::method_flags::MethodFlags::PHP_STD_LIB)
143                     })
144                     .copied(),
145                 arena,
146             )
147             .into_bump_slice();
148             let masked = arena.alloc(ShallowClass {
149                 props,
150                 sprops,
151                 methods,
152                 static_methods,
153                 ..*class
154             });
155             Some((name, Decl::Class(masked)))
156         }
157         _ => Some((name, decl)),
158     }