mago_project/
module.rs

1use mago_names::resolver::NameResolver;
2use serde::Deserialize;
3use serde::Serialize;
4
5use mago_interner::ThreadedInterner;
6use mago_names::ResolvedNames;
7use mago_php_version::PHPVersion;
8use mago_reflection::CodebaseReflection;
9use mago_reporting::IssueCollection;
10use mago_source::Source;
11use mago_source::SourceCategory;
12use mago_syntax::ast::Program;
13use mago_syntax::error::ParseError;
14
15use crate::internal;
16
17/// `Module` represents a processed PHP source code module.
18///
19/// It encapsulates the original source, resolved names, any parsing errors,
20/// optional reflection data (if enabled), and a collection of semantic issues.
21#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
22pub struct Module {
23    pub source: Source,
24    pub names: ResolvedNames,
25    pub parse_error: Option<ParseError>,
26    pub reflection: Option<CodebaseReflection>,
27    pub issues: IssueCollection,
28}
29
30/// `ModuleBuildOptions` configures the behavior of the module building process.
31///
32/// The options determine whether the module should perform reflection, validation, or both:
33/// - `reflect`: When true, reflection is performed.
34/// - `validate`: When true, semantic validation is performed.
35#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
36pub struct ModuleBuildOptions {
37    pub reflect: bool,
38    pub validate: bool,
39}
40
41impl Module {
42    /// Builds a `Module` from a given PHP source.
43    ///
44    /// This convenience function parses the PHP source code to generate an abstract syntax tree (AST),
45    /// resolves names, optionally performs reflection and/or semantic validation based on the provided
46    /// build options, and collects any issues encountered during analysis.
47    ///
48    /// Internally, it delegates to [`build_with_ast`] and discards the AST.
49    ///
50    /// # Arguments
51    ///
52    /// * `interner` - A reference to a `ThreadedInterner` used for efficient string interning.
53    /// * `version` - The PHP version guiding the parsing and analysis.
54    /// * `source` - The PHP source code encapsulated in a `Source` struct.
55    /// * `options` - The build options controlling reflection and validation.
56    ///
57    /// # Returns
58    ///
59    /// A new `Module` instance containing the resolved names, any parse errors, optional reflection data,
60    /// and a collection of issues.
61    #[inline]
62    pub fn build(
63        interner: &ThreadedInterner,
64        version: PHPVersion,
65        source: Source,
66        options: ModuleBuildOptions,
67    ) -> Self {
68        let (module, _) = Self::build_with_ast(interner, version, source, options);
69
70        module
71    }
72
73    /// Builds a `Module` from a given PHP source and returns the associated AST.
74    ///
75    /// This function performs the complete module processing workflow:
76    /// - Parses the PHP source to generate an AST (`Program`).
77    /// - Resolves symbol names.
78    /// - Optionally performs reflection and/or semantic validation based on the provided build options.
79    /// - Collects any issues encountered during analysis.
80    ///
81    /// # Arguments
82    ///
83    /// * `interner` - A reference to a `ThreadedInterner` for efficient string interning.
84    /// * `version` - The PHP version used to guide parsing and analysis.
85    /// * `source` - The PHP source code encapsulated in a `Source` struct.
86    /// * `options` - The build options that control reflection and validation.
87    ///
88    /// # Returns
89    ///
90    /// A tuple containing:
91    /// - A `Module` instance with the processed data (names, reflection, issues, etc.).
92    /// - The `Program` representing the parsed abstract syntax tree (AST) of the source.
93    ///
94    /// This is useful when the AST is required immediately before moving the module
95    /// to another context (e.g., across threads) so that the cost of re-parsing can be avoided.
96    #[inline]
97    pub fn build_with_ast(
98        interner: &ThreadedInterner,
99        version: PHPVersion,
100        source: Source,
101        options: ModuleBuildOptions,
102    ) -> (Self, Program) {
103        let (program, parse_error) = mago_syntax::parser::parse_source(interner, &source);
104        let resolver = NameResolver::new(interner);
105        let names = resolver.resolve(&program);
106        let (reflection, issues) = internal::build(interner, version, &source, &program, &names, options);
107        let module = Self { source, parse_error, names, reflection, issues };
108
109        (module, program)
110    }
111
112    /// Parses the module's source code to generate an abstract syntax tree (AST).
113    ///
114    /// For performance reasons, the AST is not stored within the module. If an AST is needed later,
115    /// this method re-parses the source code on demand.
116    ///
117    /// # Arguments
118    ///
119    /// * `interner` - A reference to a `ThreadedInterner` used for efficient string handling during parsing.
120    ///
121    /// # Returns
122    ///
123    /// A `Program` representing the abstract syntax tree (AST) of the module.
124    pub fn parse(&self, interner: &ThreadedInterner) -> Program {
125        mago_syntax::parser::parse_source(interner, &self.source).0
126    }
127
128    /// Retrieves the category of the module's source.
129    ///
130    /// This method extracts the source category (e.g., user-defined, built-in, external)
131    /// from the module's source identifier.
132    #[inline]
133    pub const fn category(&self) -> SourceCategory {
134        self.source.identifier.category()
135    }
136}
137
138impl ModuleBuildOptions {
139    /// Creates a new `ModuleBuildOptions` with the specified settings.
140    #[inline]
141    pub const fn new(reflect: bool, validate: bool) -> Self {
142        Self { reflect, validate }
143    }
144
145    /// Returns build options configured for reflection only (without validation).
146    #[inline]
147    pub const fn reflection() -> Self {
148        Self { reflect: true, validate: false }
149    }
150
151    /// Returns build options configured for validation only (without reflection).
152    #[inline]
153    pub const fn validation() -> Self {
154        Self { reflect: false, validate: true }
155    }
156}
157
158impl Default for ModuleBuildOptions {
159    /// Returns the default build options with both reflection and validation enabled.
160    fn default() -> Self {
161        Self::new(true, true)
162    }
163}