mago_project/
module.rs

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