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}