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