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}