#![allow(clippy::unwrap_used)]
use crate::semantic::resolver::Resolver;
use super::super::*;
#[test]
fn test_lookup_from_scope_in_current_scope() {
let mut table = SymbolTable::new();
let symbol = Symbol::Package {
documentation: None,
scope_id: 0,
source_file: None,
span: None,
name: "RootSymbol".to_string(),
qualified_name: "RootSymbol".to_string(),
};
table.insert("RootSymbol".to_string(), symbol).unwrap();
let resolver = Resolver::new(&table);
let found = resolver.resolve_in_scope("RootSymbol", 0);
assert!(found.is_some());
assert_eq!(found.unwrap().name(), "RootSymbol");
}
#[test]
fn test_lookup_from_scope_in_parent_scope() {
let mut table = SymbolTable::new();
let parent_symbol = Symbol::Package {
documentation: None,
scope_id: 0,
source_file: None,
span: None,
name: "ParentSymbol".to_string(),
qualified_name: "ParentSymbol".to_string(),
};
table
.insert("ParentSymbol".to_string(), parent_symbol)
.unwrap();
let child_scope = table.enter_scope();
let resolver = Resolver::new(&table);
let found = resolver.resolve_in_scope("ParentSymbol", child_scope);
assert!(found.is_some());
assert_eq!(found.unwrap().name(), "ParentSymbol");
}
#[test]
fn test_lookup_from_scope_in_grandparent_scope() {
let mut table = SymbolTable::new();
let root_symbol = Symbol::Package {
documentation: None,
scope_id: 0,
source_file: None,
span: None,
name: "GrandparentSymbol".to_string(),
qualified_name: "GrandparentSymbol".to_string(),
};
table
.insert("GrandparentSymbol".to_string(), root_symbol)
.unwrap();
table.enter_scope();
let grandchild_scope = table.enter_scope();
let resolver = Resolver::new(&table);
let found = resolver.resolve_in_scope("GrandparentSymbol", grandchild_scope);
assert!(found.is_some());
assert_eq!(found.unwrap().name(), "GrandparentSymbol");
}
#[test]
fn test_lookup_from_scope_not_found() {
let mut table = SymbolTable::new();
let symbol = Symbol::Package {
documentation: None,
scope_id: 0,
source_file: None,
span: None,
name: "ExistingSymbol".to_string(),
qualified_name: "ExistingSymbol".to_string(),
};
table.insert("ExistingSymbol".to_string(), symbol).unwrap();
let resolver = Resolver::new(&table);
let found = resolver.resolve_in_scope("NonExistentSymbol", 0);
assert!(found.is_none());
}
#[test]
fn test_lookup_from_scope_symbol_shadowing() {
let mut table = SymbolTable::new();
let parent_symbol = Symbol::Package {
documentation: None,
scope_id: 0,
source_file: None,
span: None,
name: "Symbol".to_string(),
qualified_name: "Parent::Symbol".to_string(),
};
table.insert("Symbol".to_string(), parent_symbol).unwrap();
let child_scope = table.enter_scope();
let child_symbol = Symbol::Classifier {
scope_id: 1,
source_file: None,
span: None,
name: "Symbol".to_string(),
qualified_name: "Parent::Child::Symbol".to_string(),
kind: "Class".to_string(),
is_abstract: false,
documentation: None,
};
table.insert("Symbol".to_string(), child_symbol).unwrap();
let resolver = Resolver::new(&table);
let found = resolver.resolve_in_scope("Symbol", child_scope);
assert!(found.is_some());
let symbol = found.unwrap();
assert_eq!(symbol.qualified_name(), "Parent::Child::Symbol");
assert!(matches!(symbol, Symbol::Classifier { .. }));
}
#[test]
fn test_lookup_from_scope_from_root() {
let mut table = SymbolTable::new();
let symbol = Symbol::Package {
documentation: None,
scope_id: 0,
source_file: None,
span: None,
name: "RootSymbol".to_string(),
qualified_name: "RootSymbol".to_string(),
};
table.insert("RootSymbol".to_string(), symbol).unwrap();
table.enter_scope();
let resolver = Resolver::new(&table);
let found = resolver.resolve_in_scope("RootSymbol", 0);
assert!(found.is_some());
}
#[test]
fn test_lookup_from_scope_no_sibling_access() {
let mut table = SymbolTable::new();
table.enter_scope();
let sibling1_symbol = Symbol::Package {
documentation: None,
scope_id: 1,
source_file: None,
span: None,
name: "Sibling1".to_string(),
qualified_name: "Sibling1".to_string(),
};
table
.insert("Sibling1".to_string(), sibling1_symbol)
.unwrap();
table.exit_scope();
let sibling2_scope = table.enter_scope();
let resolver = Resolver::new(&table);
let found = resolver.resolve_from_scope_direct("Sibling1", sibling2_scope);
assert!(found.is_none());
}
#[test]
fn test_lookup_from_scope_different_symbol_types() {
let mut table = SymbolTable::new();
table
.insert(
"RootPkg".to_string(),
Symbol::Package {
documentation: None,
scope_id: 0,
source_file: None,
span: None,
name: "RootPkg".to_string(),
qualified_name: "RootPkg".to_string(),
},
)
.unwrap();
table.enter_scope();
table
.insert(
"MyClass".to_string(),
Symbol::Classifier {
scope_id: 1,
source_file: None,
span: None,
name: "MyClass".to_string(),
qualified_name: "RootPkg::MyClass".to_string(),
kind: "Class".to_string(),
is_abstract: false,
documentation: None,
},
)
.unwrap();
let feature_scope = table.enter_scope();
table
.insert(
"MyFeature".to_string(),
Symbol::Feature {
scope_id: 2,
source_file: None,
span: None,
name: "MyFeature".to_string(),
qualified_name: "RootPkg::MyClass::MyFeature".to_string(),
feature_type: Some("String".to_string()),
documentation: None,
},
)
.unwrap();
let resolver = Resolver::new(&table);
let pkg = resolver.resolve_in_scope("RootPkg", feature_scope);
assert!(pkg.is_some());
assert!(matches!(
pkg.unwrap(),
Symbol::Package {
documentation: None,
..
}
));
let resolver = Resolver::new(&table);
let class = resolver.resolve_in_scope("MyClass", feature_scope);
assert!(class.is_some());
assert!(matches!(class.unwrap(), Symbol::Classifier { .. }));
let resolver = Resolver::new(&table);
let feature = resolver.resolve_in_scope("MyFeature", feature_scope);
assert!(feature.is_some());
assert!(matches!(feature.unwrap(), Symbol::Feature { .. }));
}
#[test]
fn test_lookup_from_scope_deeply_nested() {
let mut table = SymbolTable::new();
let root_symbol = Symbol::Package {
documentation: None,
scope_id: 0,
source_file: None,
span: None,
name: "Level0".to_string(),
qualified_name: "Level0".to_string(),
};
table.insert("Level0".to_string(), root_symbol).unwrap();
let mut scope_ids = vec![0];
for i in 1..=4 {
let scope_id = table.enter_scope();
scope_ids.push(scope_id);
let symbol = Symbol::Package {
documentation: None,
scope_id: i,
source_file: None,
span: None,
name: format!("Level{i}"),
qualified_name: format!("Level0::Level{i}"),
};
table.insert(format!("Level{i}"), symbol).unwrap();
}
let deepest_scope = *scope_ids.last().unwrap();
assert!(
Resolver::new(&table)
.resolve_in_scope("Level0", deepest_scope)
.is_some()
);
assert!(
Resolver::new(&table)
.resolve_in_scope("Level1", deepest_scope)
.is_some()
);
assert!(
Resolver::new(&table)
.resolve_in_scope("Level2", deepest_scope)
.is_some()
);
assert!(
Resolver::new(&table)
.resolve_in_scope("Level3", deepest_scope)
.is_some()
);
assert!(
Resolver::new(&table)
.resolve_in_scope("Level4", deepest_scope)
.is_some()
);
let level2_scope = scope_ids[2];
assert!(
Resolver::new(&table)
.resolve_in_scope("Level0", level2_scope)
.is_some()
);
assert!(
Resolver::new(&table)
.resolve_in_scope("Level1", level2_scope)
.is_some()
);
assert!(
Resolver::new(&table)
.resolve_in_scope("Level2", level2_scope)
.is_some()
);
assert!(
Resolver::new(&table)
.resolve_in_scope("Level3", level2_scope)
.is_none()
);
assert!(
Resolver::new(&table)
.resolve_in_scope("Level4", level2_scope)
.is_none()
);
}
#[test]
fn test_lookup_from_scope_no_child_access() {
let mut table = SymbolTable::new();
let root_symbol = Symbol::Package {
documentation: None,
scope_id: 0,
source_file: None,
span: None,
name: "Root".to_string(),
qualified_name: "Root".to_string(),
};
table.insert("Root".to_string(), root_symbol).unwrap();
table.enter_scope();
let child_symbol = Symbol::Package {
documentation: None,
scope_id: 1,
source_file: None,
span: None,
name: "Child".to_string(),
qualified_name: "Child".to_string(),
};
table.insert("Child".to_string(), child_symbol).unwrap();
let resolver = Resolver::new(&table);
let found = resolver.resolve_from_scope_direct("Child", 0);
assert!(found.is_none());
}
#[test]
fn test_lookup_from_scope_with_alias() {
let mut table = SymbolTable::new();
table
.insert(
"RealSymbol".to_string(),
Symbol::Package {
documentation: None,
scope_id: 0,
source_file: None,
span: None,
name: "RealSymbol".to_string(),
qualified_name: "RealSymbol".to_string(),
},
)
.unwrap();
let child_scope = table.enter_scope();
table
.insert(
"AliasSymbol".to_string(),
Symbol::Alias {
scope_id: 1,
source_file: None,
span: None,
name: "AliasSymbol".to_string(),
qualified_name: "AliasSymbol".to_string(),
target: "RealSymbol".to_string(),
target_span: None,
},
)
.unwrap();
let resolver = Resolver::new(&table);
let found = resolver.resolve_in_scope("AliasSymbol", child_scope);
assert!(found.is_some());
assert!(matches!(found.unwrap(), Symbol::Alias { .. }));
let resolver = Resolver::new(&table);
let real = resolver.resolve_in_scope("RealSymbol", child_scope);
assert!(real.is_some());
assert!(matches!(
real.unwrap(),
Symbol::Package {
documentation: None,
..
}
));
}
#[test]
fn test_lookup_from_scope_definition_and_usage() {
let mut table = SymbolTable::new();
table
.insert(
"MyDef".to_string(),
Symbol::Definition {
scope_id: 0,
source_file: None,
span: None,
name: "MyDef".to_string(),
qualified_name: "MyDef".to_string(),
kind: "Part".to_string(),
semantic_role: None,
documentation: None,
},
)
.unwrap();
let child_scope = table.enter_scope();
table
.insert(
"MyUsage".to_string(),
Symbol::Usage {
scope_id: 1,
source_file: None,
span: None,
usage_type: None,
semantic_role: None,
name: "MyUsage".to_string(),
qualified_name: "MyUsage".to_string(),
kind: "Part".to_string(),
documentation: None,
},
)
.unwrap();
let resolver = Resolver::new(&table);
let def = resolver.resolve_from_scope_direct("MyDef", child_scope);
assert!(def.is_some());
assert!(matches!(def.unwrap(), Symbol::Definition { .. }));
let usage = resolver.resolve_from_scope_direct("MyUsage", child_scope);
assert!(usage.is_some());
assert!(matches!(usage.unwrap(), Symbol::Usage { .. }));
let def_from_root = resolver.resolve_from_scope_direct("MyDef", 0);
assert!(def_from_root.is_some());
let usage_from_root = resolver.resolve_from_scope_direct("MyUsage", 0);
assert!(usage_from_root.is_none());
}
#[test]
fn test_lookup_from_scope_idempotent() {
let mut table = SymbolTable::new();
let symbol = Symbol::Package {
documentation: None,
scope_id: 0,
source_file: None,
span: None,
name: "Symbol".to_string(),
qualified_name: "Symbol".to_string(),
};
table.insert("Symbol".to_string(), symbol).unwrap();
let resolver = Resolver::new(&table);
let found1 = resolver.resolve_in_scope("Symbol", 0);
let resolver = Resolver::new(&table);
let found2 = resolver.resolve_in_scope("Symbol", 0);
let resolver = Resolver::new(&table);
let found3 = resolver.resolve_in_scope("Symbol", 0);
assert!(found1.is_some());
assert!(found2.is_some());
assert!(found3.is_some());
assert_eq!(found1.unwrap().name(), found2.unwrap().name());
assert_eq!(found2.unwrap().name(), found3.unwrap().name());
}