mago_semantics/
lib.rs

1//! # Mago Semantics Crate
2//!
3//! The `mago_semantics` crate provides semantic analysis capabilities for PHP code.
4//! **Mago** is the name of the toolchain, and this crate processes PHP source code to generate
5//! an abstract syntax tree (AST), performs name resolution, builds a symbol table, and detects
6//! semantic issues.
7//!
8//! This crate is essential for the compilation pipeline of PHP within the Mago toolchain,
9//! ensuring that source code adheres to the language's semantic rules before code generation
10//! or interpretation.
11//!
12//! # Features
13//!
14//! - **Parsing**: Converts PHP source code into an AST.
15//! - **Name Resolution**: Associates identifiers with their declarations.
16//! - **Symbol Table Construction**: Records all symbols (classes, functions, variables) for quick lookup.
17//! - **Semantic Analysis**: Checks for semantic correctness and reports issues.
18
19use serde::Deserialize;
20use serde::Serialize;
21
22use mago_ast::Program;
23use mago_interner::ThreadedInterner;
24use mago_names::Names;
25use mago_parser::error::ParseError;
26use mago_php_version::PHPVersion;
27use mago_reporting::IssueCollection;
28use mago_source::Source;
29use mago_source::SourceCategory;
30use mago_walker::Walker;
31
32use crate::context::Context;
33use crate::walker::SemanticsWalker;
34
35mod consts;
36mod context;
37mod walker;
38
39/// The `Semantics` struct encapsulates all the information obtained after performing semantic analysis
40/// on a PHP source code file. It includes the original source code, the parsed abstract syntax tree (AST),
41/// any parse errors encountered, resolved names, the symbol table, and a collection of semantic issues.
42#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
43pub struct Semantics {
44    /// The original PHP source code, including its name and content.
45    pub source: Source,
46
47    /// The abstract syntax tree (AST) resulting from parsing the source code.
48    pub program: Program,
49
50    /// An optional parse error, if one occurred during parsing.
51    pub parse_error: Option<ParseError>,
52
53    /// The resolved names within the source code, used for identifier resolution.
54    pub names: Names,
55
56    /// A collection of semantic issues found during analysis, such as invalid inheritance,
57    ///  improper returns, duplicate names, etc.
58    pub issues: IssueCollection,
59}
60
61impl Semantics {
62    /// Builds the `Semantics` object by performing parsing, name resolution, symbol table construction,
63    /// and semantic analysis on the provided PHP source code.
64    ///
65    /// # Parameters
66    ///
67    /// - `interner`: A reference to a `ThreadedInterner` used for string interning, which helps in
68    ///   efficiently handling string comparisons and memory usage.
69    /// - `version`: The PHP version to use for semantic analysis.
70    /// - `source`: The `Source` object representing the PHP source code to be analyzed.
71    ///
72    /// # Returns
73    ///
74    /// A `Semantics` object containing the results of the semantic analysis, including the AST,
75    /// any parse errors, resolved names, the symbol table, and any semantic issues found.
76    ///
77    /// # Steps
78    ///
79    /// 1. **Parsing**: The source code is parsed into an abstract syntax tree (AST).
80    ///    If there are syntax errors, they are captured in `parse_error`.
81    /// 2. **Name Resolution**: Resolves all the names in the AST, linking identifiers to their declarations.
82    /// 3. **Symbol Table Construction**: Builds a symbol table containing all the symbols (classes, functions, constants, etc.) defined in the source code.
83    /// 4. **Semantic Analysis**: Checks the AST for semantic correctness, such as type checking, scope rules, etc., and collects any issues.
84    pub fn build(interner: &ThreadedInterner, version: PHPVersion, source: Source) -> Self {
85        // Parse the source code into an AST.
86        // The parser returns a tuple containing the AST and an optional parse error.
87        let (program, parse_error) = mago_parser::parse_source(interner, &source);
88
89        // Resolve names in the AST.
90        // This step links identifiers to their declarations, handling scopes and imports.
91        let names = Names::resolve(interner, &program);
92
93        // Perform semantic analysis and collect issues.
94        // This includes checks for type correctness, proper usage of constructs, etc.
95        let mut context = Context::new(interner, version, &program, &names);
96        SemanticsWalker.walk_program(&program, &mut context);
97        let issues = context.take_issue_collection();
98
99        // Return the Semantics object containing all analysis results.
100        Self { source, program, parse_error, names, issues }
101    }
102
103    /// Determines whether the semantic analysis was successful,
104    /// i.e., no parse errors or semantic issues were found.
105    pub fn is_valid(&self) -> bool {
106        self.parse_error.is_none() && self.issues.is_empty()
107    }
108
109    /// Determines whether the source code contains any parse errors.
110    pub fn has_parse_error(&self) -> bool {
111        self.parse_error.is_some()
112    }
113
114    /// Determines whether the source code contains any semantic issues.
115    pub fn has_issues(&self) -> bool {
116        !self.issues.is_empty()
117    }
118
119    /// Retrieves the category of the source code.
120    pub fn category(&self) -> SourceCategory {
121        self.source.identifier.category()
122    }
123}