use crate::parsing::resolution::ImportBinding;
use crate::parsing::{InheritanceResolver, ResolutionScope, ScopeLevel, ScopeType};
use crate::{FileId, SymbolId};
use std::collections::HashMap;
type UseStatement = (Option<String>, String);
type NamespaceUses = HashMap<String, UseStatement>;
pub struct PhpResolutionContext {
#[allow(dead_code)]
file_id: FileId,
current_namespace: Option<String>,
local_scope: HashMap<String, SymbolId>,
class_scope: HashMap<String, SymbolId>,
namespace_scope: HashMap<String, SymbolId>,
global_scope: HashMap<String, SymbolId>,
use_statements: NamespaceUses,
scope_stack: Vec<ScopeType>,
current_class: Option<String>,
import_bindings: HashMap<String, ImportBinding>,
}
impl PhpResolutionContext {
pub fn new(file_id: FileId) -> Self {
Self {
file_id,
current_namespace: None,
local_scope: HashMap::new(),
class_scope: HashMap::new(),
namespace_scope: HashMap::new(),
global_scope: HashMap::new(),
use_statements: HashMap::new(),
scope_stack: Vec::new(),
current_class: None,
import_bindings: HashMap::new(),
}
}
pub fn set_namespace(&mut self, namespace: String) {
self.current_namespace = Some(namespace);
}
pub fn add_use_statement(&mut self, alias: Option<String>, full_path: String) {
let key = alias.clone().unwrap_or_else(|| {
full_path
.rsplit('\\')
.next()
.unwrap_or(&full_path)
.to_string()
});
self.use_statements.insert(key, (alias, full_path));
}
fn resolve_name(&self, name: &str) -> Option<String> {
if name.starts_with('\\') {
return Some(name.to_string());
}
if let Some((_, full_path)) = self.use_statements.get(name) {
return Some(full_path.clone());
}
if let Some(pos) = name.find('\\') {
let first_part = &name[..pos];
if let Some((_, full_path)) = self.use_statements.get(first_part) {
let rest = &name[pos + 1..];
return Some(format!("{full_path}\\{rest}"));
}
}
if let Some(ref ns) = self.current_namespace {
Some(format!("{ns}\\{name}"))
} else {
Some(name.to_string())
}
}
}
impl ResolutionScope for PhpResolutionContext {
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn add_symbol(&mut self, name: String, symbol_id: SymbolId, scope_level: ScopeLevel) {
match scope_level {
ScopeLevel::Local => {
self.local_scope.insert(name, symbol_id);
}
ScopeLevel::Module => {
self.class_scope.insert(name, symbol_id);
}
ScopeLevel::Package => {
self.namespace_scope.insert(name, symbol_id);
}
ScopeLevel::Global => {
self.global_scope.insert(name, symbol_id);
}
}
}
fn resolve(&self, name: &str) -> Option<SymbolId> {
if let Some(&id) = self.local_scope.get(name) {
return Some(id);
}
if let Some(&id) = self.class_scope.get(name) {
return Some(id);
}
if let Some(full_name) = self.resolve_name(name) {
if let Some(&id) = self.namespace_scope.get(&full_name) {
return Some(id);
}
if let Some(&id) = self.global_scope.get(&full_name) {
return Some(id);
}
}
if let Some(&id) = self.namespace_scope.get(name) {
return Some(id);
}
if let Some(&id) = self.global_scope.get(name) {
return Some(id);
}
if name.contains("::") {
if let Some(&id) = self.namespace_scope.get(name) {
return Some(id);
}
if let Some(&id) = self.global_scope.get(name) {
return Some(id);
}
let parts: Vec<&str> = name.split("::").collect();
if parts.len() == 2 {
let class_or_namespace = parts[0];
let method_or_const = parts[1];
if self.resolve(class_or_namespace).is_some() {
return self.resolve(method_or_const);
}
return None;
}
}
None
}
fn clear_local_scope(&mut self) {
self.local_scope.clear();
}
fn enter_scope(&mut self, scope_type: ScopeType) {
self.scope_stack.push(scope_type);
match scope_type {
ScopeType::Class => {
self.class_scope.clear();
}
ScopeType::Function { .. } => {
self.clear_local_scope();
}
_ => {}
}
}
fn exit_scope(&mut self) {
if let Some(scope_type) = self.scope_stack.pop() {
match scope_type {
ScopeType::Function { .. } => {
self.clear_local_scope();
}
ScopeType::Class => {
self.current_class = None;
}
_ => {}
}
}
}
fn symbols_in_scope(&self) -> Vec<(String, SymbolId, ScopeLevel)> {
let mut symbols = Vec::new();
for (name, &id) in &self.local_scope {
symbols.push((name.clone(), id, ScopeLevel::Local));
}
for (name, &id) in &self.class_scope {
symbols.push((name.clone(), id, ScopeLevel::Module));
}
for (name, &id) in &self.namespace_scope {
symbols.push((name.clone(), id, ScopeLevel::Package));
}
for (name, &id) in &self.global_scope {
symbols.push((name.clone(), id, ScopeLevel::Global));
}
symbols
}
fn populate_imports(&mut self, imports: &[crate::parsing::Import]) {
for import in imports {
self.add_use_statement(import.alias.clone(), import.path.clone());
}
}
fn register_import_binding(&mut self, binding: ImportBinding) {
self.import_bindings
.insert(binding.exposed_name.clone(), binding);
}
fn import_binding(&self, name: &str) -> Option<ImportBinding> {
self.import_bindings.get(name).cloned()
}
}
#[derive(Clone)]
pub struct PhpInheritanceResolver {
class_extends: HashMap<String, String>,
class_implements: HashMap<String, Vec<String>>,
class_uses_traits: HashMap<String, Vec<String>>,
interface_extends: HashMap<String, Vec<String>>,
type_methods: HashMap<String, Vec<String>>,
trait_methods: HashMap<String, Vec<String>>,
}
impl Default for PhpInheritanceResolver {
fn default() -> Self {
Self::new()
}
}
impl PhpInheritanceResolver {
pub fn new() -> Self {
Self {
class_extends: HashMap::new(),
class_implements: HashMap::new(),
class_uses_traits: HashMap::new(),
interface_extends: HashMap::new(),
type_methods: HashMap::new(),
trait_methods: HashMap::new(),
}
}
pub fn add_class_extends(&mut self, class: String, parent: String) {
self.class_extends.insert(class, parent);
}
pub fn add_class_implements(&mut self, class: String, interfaces: Vec<String>) {
self.class_implements.insert(class, interfaces);
}
pub fn add_class_uses(&mut self, class: String, traits: Vec<String>) {
self.class_uses_traits.insert(class, traits);
}
pub fn add_interface_extends(&mut self, interface: String, parents: Vec<String>) {
self.interface_extends.insert(interface, parents);
}
pub fn add_trait_methods(&mut self, trait_name: String, methods: Vec<String>) {
self.trait_methods.insert(trait_name, methods);
}
}
impl InheritanceResolver for PhpInheritanceResolver {
fn add_inheritance(&mut self, child: String, parent: String, kind: &str) {
match kind {
"extends" => {
self.class_extends.insert(child, parent);
}
"implements" => {
self.class_implements.entry(child).or_default().push(parent);
}
"uses" => {
self.class_uses_traits
.entry(child)
.or_default()
.push(parent);
}
_ => {}
}
}
fn resolve_method(&self, type_name: &str, method_name: &str) -> Option<String> {
if let Some(methods) = self.type_methods.get(type_name) {
if methods.iter().any(|m| m == method_name) {
return Some(type_name.to_string());
}
}
if let Some(traits) = self.class_uses_traits.get(type_name) {
for trait_name in traits.iter().rev() {
if let Some(methods) = self.trait_methods.get(trait_name) {
if methods.iter().any(|m| m == method_name) {
return Some(trait_name.clone());
}
}
}
}
if let Some(parent) = self.class_extends.get(type_name) {
return self.resolve_method(parent, method_name);
}
None
}
fn get_inheritance_chain(&self, type_name: &str) -> Vec<String> {
let mut chain = vec![type_name.to_string()];
let mut visited = std::collections::HashSet::new();
visited.insert(type_name.to_string());
let mut current = type_name;
while let Some(parent) = self.class_extends.get(current) {
if visited.contains(parent) {
break; }
chain.push(parent.clone());
visited.insert(parent.clone());
current = parent;
}
if let Some(interfaces) = self.class_implements.get(type_name) {
for interface in interfaces {
if !visited.contains(interface) {
chain.push(interface.clone());
}
}
}
if let Some(traits) = self.class_uses_traits.get(type_name) {
for trait_name in traits {
if !visited.contains(trait_name) {
chain.push(trait_name.clone());
}
}
}
chain
}
fn is_subtype(&self, child: &str, parent: &str) -> bool {
if let Some(direct_parent) = self.class_extends.get(child) {
if direct_parent == parent {
return true;
}
if self.is_subtype(direct_parent, parent) {
return true;
}
}
if let Some(interfaces) = self.class_implements.get(child) {
if interfaces.iter().any(|i| i == parent) {
return true;
}
}
if let Some(traits) = self.class_uses_traits.get(child) {
if traits.iter().any(|t| t == parent) {
return true;
}
}
false
}
fn add_type_methods(&mut self, type_name: String, methods: Vec<String>) {
self.type_methods.insert(type_name, methods);
}
fn get_all_methods(&self, type_name: &str) -> Vec<String> {
let mut all_methods = Vec::new();
let mut seen = std::collections::HashSet::new();
if let Some(methods) = self.type_methods.get(type_name) {
for method in methods {
if seen.insert(method.clone()) {
all_methods.push(method.clone());
}
}
}
if let Some(traits) = self.class_uses_traits.get(type_name) {
for trait_name in traits {
if let Some(methods) = self.trait_methods.get(trait_name) {
for method in methods {
if seen.insert(method.clone()) {
all_methods.push(method.clone());
}
}
}
}
}
if let Some(parent) = self.class_extends.get(type_name) {
for method in self.get_all_methods(parent) {
if seen.insert(method.clone()) {
all_methods.push(method);
}
}
}
all_methods
}
}