1 // Copyright (c) Meta Platforms, Inc. and 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 crate::special_names;
7 use arena_collections::list::List;
8 use file_provider::FileProvider;
9 use names::FileSummary;
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;
20 use ty::decl::shallow;
21 use ty::reason::Reason;
26 #[derive(Debug, Clone)]
27 pub struct DeclParser<R: Reason> {
28 file_provider: Arc<dyn FileProvider>,
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 {
40 opts: Default::default(),
41 _phantom: PhantomData,
45 pub fn with_options(file_provider: Arc<dyn FileProvider>, opts: &ParserOptions<'_>) -> Self {
48 opts: Options::from(opts),
49 _phantom: PhantomData,
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())
61 pub fn parse_and_summarize(
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))
75 opts: &'a DeclParserOptions<'a>,
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)),
92 parsed_file.decls.rev(arena);
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>)> {
105 Decl::Fun(fun) if fun.php_std_lib => None,
107 if (class.user_attributes.iter()).any(|ua| {
108 TypeName::new(ua.name.1) == *special_names::user_attributes::uaPHPStdLib
113 Decl::Class(class) => {
114 let props = bumpalo::collections::Vec::from_iter_in(
116 .filter(|p| !p.flags.contains(obr::prop_flags::PropFlags::PHP_STD_LIB))
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))
128 let methods = bumpalo::collections::Vec::from_iter_in(
129 (class.methods.iter())
132 .contains(obr::method_flags::MethodFlags::PHP_STD_LIB)
138 let static_methods = bumpalo::collections::Vec::from_iter_in(
139 (class.static_methods.iter())
142 .contains(obr::method_flags::MethodFlags::PHP_STD_LIB)
148 let masked = arena.alloc(ShallowClass {
155 Some((name, Decl::Class(masked)))
157 _ => Some((name, decl)),