use crate::parsing::resolution::ImportBinding;
use crate::parsing::{InheritanceResolver, ResolutionScope, ScopeLevel, ScopeType};
use crate::{FileId, SymbolId};
use std::collections::HashMap;
type ImportInfo = (String, Option<String>);
type ModuleImports = Vec<(String, Vec<ImportInfo>)>;
pub struct PythonResolutionContext {
#[allow(dead_code)]
file_id: FileId,
local_scope: HashMap<String, SymbolId>,
enclosing_scope: HashMap<String, SymbolId>,
global_scope: HashMap<String, SymbolId>,
imported_symbols: HashMap<String, SymbolId>,
builtin_scope: HashMap<String, SymbolId>,
scope_stack: Vec<ScopeType>,
imports: ModuleImports,
current_class: Option<String>,
import_bindings: HashMap<String, ImportBinding>,
}
impl PythonResolutionContext {
pub fn new(file_id: FileId) -> Self {
Self {
file_id,
local_scope: HashMap::new(),
enclosing_scope: HashMap::new(),
global_scope: HashMap::new(),
imported_symbols: HashMap::new(),
builtin_scope: HashMap::new(),
scope_stack: Vec::new(),
imports: Vec::new(),
current_class: None,
import_bindings: HashMap::new(),
}
}
pub fn add_import(&mut self, module: String, name: String, alias: Option<String>) {
if let Some(entry) = self.imports.iter_mut().find(|(m, _)| m == &module) {
entry.1.push((name, alias));
} else {
self.imports.push((module, vec![(name, alias)]));
}
}
pub fn add_symbol_python(&mut self, name: String, symbol_id: SymbolId, is_global: bool) {
if is_global || self.scope_stack.is_empty() || self.scope_stack.len() == 1 {
self.global_scope.insert(name, symbol_id);
} else {
self.local_scope.insert(name, symbol_id);
}
}
pub fn push_enclosing_scope(&mut self) {
let locals = std::mem::take(&mut self.local_scope);
for (name, id) in locals {
self.enclosing_scope.insert(name, id);
}
}
pub fn pop_enclosing_scope(&mut self) {
self.enclosing_scope.clear();
}
}
impl ResolutionScope for PythonResolutionContext {
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.global_scope.insert(name, symbol_id);
}
ScopeLevel::Package => {
self.imported_symbols.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.enclosing_scope.get(name) {
return Some(id);
}
if let Some(&id) = self.global_scope.get(name) {
return Some(id);
}
if let Some(&id) = self.imported_symbols.get(name) {
return Some(id);
}
if let Some(&id) = self.builtin_scope.get(name) {
return Some(id);
}
if name.contains('.') {
if let Some(&id) = self.imported_symbols.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 module_or_class = parts[0];
let function_or_method = parts[1];
if self.resolve(module_or_class).is_some() {
return self.resolve(function_or_method);
}
return None;
}
}
None
}
fn clear_local_scope(&mut self) {
self.local_scope.clear();
}
fn enter_scope(&mut self, scope_type: ScopeType) {
if matches!(scope_type, ScopeType::Function { .. }) && !self.scope_stack.is_empty() {
self.push_enclosing_scope();
}
self.scope_stack.push(scope_type);
}
fn exit_scope(&mut self) {
if let Some(scope) = self.scope_stack.pop() {
match scope {
ScopeType::Function { .. } => {
self.clear_local_scope();
self.pop_enclosing_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.imported_symbols {
symbols.push((name.clone(), id, ScopeLevel::Package));
}
for (name, &id) in &self.global_scope {
symbols.push((name.clone(), id, ScopeLevel::Global));
}
symbols
}
fn resolve_relationship(
&self,
_from_name: &str,
to_name: &str,
kind: crate::RelationKind,
_from_file: FileId,
) -> Option<SymbolId> {
use crate::RelationKind;
match kind {
RelationKind::Defines => {
self.resolve(to_name)
}
RelationKind::Calls => {
if to_name.contains('.') {
if let Some(id) = self.resolve(to_name) {
return Some(id);
}
if let Some(last_part) = to_name.rsplit('.').next() {
return self.resolve(last_part);
}
}
self.resolve(to_name)
}
RelationKind::Extends => {
self.resolve(to_name)
}
_ => {
self.resolve(to_name)
}
}
}
fn populate_imports(&mut self, imports: &[crate::parsing::Import]) {
for import in imports {
if let Some(last_dot) = import.path.rfind('.') {
let module = import.path[..last_dot].to_string();
let name = import.path[last_dot + 1..].to_string();
self.add_import(module, name, import.alias.clone());
} else {
self.add_import(import.path.clone(), String::new(), import.alias.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 PythonInheritanceResolver {
class_bases: HashMap<String, Vec<String>>,
class_methods: HashMap<String, Vec<String>>,
mro_cache: HashMap<String, Vec<String>>,
}
impl Default for PythonInheritanceResolver {
fn default() -> Self {
Self::new()
}
}
impl PythonInheritanceResolver {
pub fn new() -> Self {
Self {
class_bases: HashMap::new(),
class_methods: HashMap::new(),
mro_cache: HashMap::new(),
}
}
fn calculate_mro(&self, class_name: &str) -> Vec<String> {
if let Some(mro) = self.mro_cache.get(class_name) {
return mro.clone();
}
let mut mro = vec![class_name.to_string()];
if let Some(bases) = self.class_bases.get(class_name) {
for base in bases {
let base_mro = self.calculate_mro(base);
for class in base_mro {
if !mro.contains(&class) {
mro.push(class);
}
}
}
}
mro
}
pub fn add_class(&mut self, class_name: String, bases: Vec<String>) {
self.class_bases.insert(class_name.clone(), bases);
self.mro_cache.clear();
}
pub fn add_class_methods(&mut self, class_name: String, methods: Vec<String>) {
self.class_methods.insert(class_name, methods);
}
}
impl InheritanceResolver for PythonInheritanceResolver {
fn add_inheritance(&mut self, child: String, parent: String, kind: &str) {
if kind == "extends" || kind == "inherits" {
self.class_bases.entry(child).or_default().push(parent);
self.mro_cache.clear();
}
}
fn resolve_method(&self, type_name: &str, method_name: &str) -> Option<String> {
let mro = self.calculate_mro(type_name);
for class in &mro {
if let Some(methods) = self.class_methods.get(class) {
if methods.iter().any(|m| m == method_name) {
return Some(class.clone());
}
}
}
None
}
fn get_inheritance_chain(&self, type_name: &str) -> Vec<String> {
self.calculate_mro(type_name)
}
fn is_subtype(&self, child: &str, parent: &str) -> bool {
let mro = self.calculate_mro(child);
mro.contains(&parent.to_string())
}
fn add_type_methods(&mut self, type_name: String, methods: Vec<String>) {
self.add_class_methods(type_name, methods);
}
fn get_all_methods(&self, type_name: &str) -> Vec<String> {
let mut all_methods = Vec::new();
let mro = self.calculate_mro(type_name);
for class in &mro {
if let Some(methods) = self.class_methods.get(class) {
for method in methods {
if !all_methods.contains(method) {
all_methods.push(method.clone());
}
}
}
}
all_methods
}
}