pub(crate) mod arena;
#[doc(hidden)]
pub mod cache;
pub(crate) mod call;
pub(crate) mod class;
pub(crate) mod collector;
pub(crate) mod context;
#[doc(hidden)]
pub mod db;
pub(crate) mod dead_code;
pub(crate) mod diagnostics;
pub(crate) mod expr;
pub mod file_analyzer;
pub(crate) mod generic;
pub(crate) mod narrowing;
#[doc(hidden)]
pub mod parser;
pub(crate) mod pass2;
pub mod php_version;
pub mod project;
pub mod session;
pub(crate) mod shared_db;
pub(crate) mod stmt;
#[doc(hidden)]
pub mod stubs;
pub(crate) mod taint;
pub(crate) mod type_env;
pub use file_analyzer::{BatchFileAnalyzer, FileAnalysis, FileAnalyzer, ParsedFile};
pub use parser::type_from_hint::type_from_hint;
pub use parser::{DocblockParser, ParsedDocblock};
pub use php_version::{ParsePhpVersionError, PhpVersion};
pub use project::{AnalysisResult, ProjectAnalyzer};
pub use session::AnalysisSession;
pub use stubs::{is_builtin_function, stub_files, StubVfs};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Position {
pub line: u32,
pub column: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Range {
pub start: Position,
pub end: Position,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Symbol {
Class(std::sync::Arc<str>),
Function(std::sync::Arc<str>),
Method {
class: std::sync::Arc<str>,
name: std::sync::Arc<str>,
},
Property {
class: std::sync::Arc<str>,
name: std::sync::Arc<str>,
},
ClassConstant {
class: std::sync::Arc<str>,
name: std::sync::Arc<str>,
},
GlobalConstant(std::sync::Arc<str>),
}
impl Symbol {
pub fn method(class: impl Into<std::sync::Arc<str>>, name: &str) -> Self {
Symbol::Method {
class: class.into(),
name: std::sync::Arc::from(name.to_ascii_lowercase()),
}
}
pub fn class(fqcn: impl Into<std::sync::Arc<str>>) -> Self {
Symbol::Class(fqcn.into())
}
pub fn function(fqn: impl Into<std::sync::Arc<str>>) -> Self {
Symbol::Function(fqn.into())
}
pub fn property(
class: impl Into<std::sync::Arc<str>>,
name: impl Into<std::sync::Arc<str>>,
) -> Self {
Symbol::Property {
class: class.into(),
name: name.into(),
}
}
pub fn class_constant(
class: impl Into<std::sync::Arc<str>>,
name: impl Into<std::sync::Arc<str>>,
) -> Self {
Symbol::ClassConstant {
class: class.into(),
name: name.into(),
}
}
pub fn global_constant(fqn: impl Into<std::sync::Arc<str>>) -> Self {
Symbol::GlobalConstant(fqn.into())
}
pub fn codebase_key(&self) -> String {
match self {
Symbol::Class(fqcn) => fqcn.to_string(),
Symbol::Function(fqn) => fqn.to_string(),
Symbol::Method { class, name } => format!("{class}::{name}"),
Symbol::Property { class, name } => format!("{class}::{name}"),
Symbol::ClassConstant { class, name } => format!("{class}::{name}"),
Symbol::GlobalConstant(fqn) => fqn.to_string(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SymbolLookupError {
NotFound,
NoSourceLocation,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LazyLoadOutcome {
AlreadyLoaded,
Loaded,
NotResolvable,
}
pub trait ClassResolver: Send + Sync {
fn resolve(&self, fqcn: &str) -> Option<std::path::PathBuf>;
}
impl ClassResolver for composer::Psr4Map {
fn resolve(&self, fqcn: &str) -> Option<std::path::PathBuf> {
composer::Psr4Map::resolve(self, fqcn)
}
}
impl std::fmt::Display for SymbolLookupError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SymbolLookupError::NotFound => write!(f, "symbol not found"),
SymbolLookupError::NoSourceLocation => write!(f, "symbol has no source location"),
}
}
}
impl std::error::Error for SymbolLookupError {}
#[derive(Debug, Clone)]
pub struct HoverInfo {
pub ty: Type,
pub docstring: Option<String>,
pub definition: Option<mir_codebase::storage::Location>,
}
#[derive(Debug, Clone)]
pub struct DependencyGraph {
dependencies: std::collections::HashMap<String, Vec<String>>,
dependents: std::collections::HashMap<String, Vec<String>>,
}
impl DependencyGraph {
pub fn dependencies_of(&self, file: &str) -> &[String] {
self.dependencies
.get(file)
.map(|v| v.as_slice())
.unwrap_or(&[])
}
pub fn dependents_of(&self, file: &str) -> &[String] {
self.dependents
.get(file)
.map(|v| v.as_slice())
.unwrap_or(&[])
}
pub fn transitive_dependencies(&self, file: &str) -> Vec<String> {
let mut visited = std::collections::HashSet::new();
let mut queue = vec![file.to_string()];
let mut result = Vec::new();
while let Some(current) = queue.pop() {
if !visited.insert(current.clone()) {
continue;
}
for dep in self.dependencies_of(¤t) {
if !visited.contains(dep) {
queue.push(dep.clone());
result.push(dep.clone());
}
}
}
result
}
pub fn transitive_dependents(&self, file: &str) -> Vec<String> {
let mut visited = std::collections::HashSet::new();
let mut queue = vec![file.to_string()];
let mut result = Vec::new();
while let Some(current) = queue.pop() {
if !visited.insert(current.clone()) {
continue;
}
for dep in self.dependents_of(¤t) {
if !visited.contains(dep) {
queue.push(dep.clone());
result.push(dep.clone());
}
}
}
result
}
}
pub mod symbol;
pub use mir_codebase::storage::{FnParam, TemplateParam, Visibility};
pub use mir_issues::{Issue, IssueKind, Location, Severity};
pub use mir_types::Union as Type;
pub fn location_from_span(
span: php_ast::Span,
file: std::sync::Arc<str>,
source: &str,
source_map: &php_rs_parser::source_map::SourceMap,
) -> mir_codebase::storage::Location {
let (line, col_start) = diagnostics::offset_to_line_col(source, span.start, source_map);
let (line_end, col_end) = if span.start < span.end {
diagnostics::offset_to_line_col(source, span.end, source_map)
} else {
(line, col_start)
};
mir_codebase::storage::Location {
file,
line,
line_end,
col_start,
col_end: col_end.max(col_start.saturating_add(1)),
}
}
pub use symbol::{DocumentSymbol, DocumentSymbolKind, ResolvedSymbol, SymbolKind};
pub mod composer;
pub use composer::{ComposerError, Psr4Map};
pub use type_env::ScopeId;
#[doc(hidden)]
pub mod test_utils;