#![allow(clippy::unwrap_used)]
use super::super::*;
#[test]
fn test_find_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 found = table.lookup_mut("RootSymbol");
assert!(found.is_some());
assert_eq!(found.unwrap().name(), "RootSymbol");
}
#[test]
fn test_find_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();
table.enter_scope();
let found = table.lookup_mut("ParentSymbol");
assert!(found.is_some());
assert_eq!(found.unwrap().name(), "ParentSymbol");
}
#[test]
fn test_find_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();
table.enter_scope();
let found = table.lookup_mut("GrandparentSymbol");
assert!(found.is_some());
assert_eq!(found.unwrap().name(), "GrandparentSymbol");
}
#[test]
fn test_symbol_not_found_in_chain() {
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 found = table.lookup_mut("NonExistentSymbol");
assert!(found.is_none());
}
#[test]
fn test_symbol_precedence_current_over_parent() {
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();
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,
specializes: Vec::new(),
};
table.insert("Symbol".to_string(), child_symbol).unwrap();
let found = table.lookup_mut("Symbol");
assert!(found.is_some());
let symbol = found.unwrap();
assert_eq!(symbol.qualified_name(), "Parent::Child::Symbol");
assert!(matches!(symbol, Symbol::Classifier { .. }));
}
#[test]
fn test_mutable_access_to_found_symbol() {
let mut table = SymbolTable::new();
let symbol = Symbol::Package {
documentation: None,
scope_id: 0,
source_file: None,
span: None,
name: "MutableSymbol".to_string(),
qualified_name: "MutableSymbol".to_string(),
};
table.insert("MutableSymbol".to_string(), symbol).unwrap();
let found = table.lookup_mut("MutableSymbol");
assert!(found.is_some());
assert_eq!(found.unwrap().name(), "MutableSymbol");
}
#[test]
fn test_deeply_nested_scopes() {
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();
for i in 1..=4 {
table.enter_scope();
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();
}
assert!(table.lookup_mut("Level0").is_some());
assert!(table.lookup_mut("Level1").is_some());
assert!(table.lookup_mut("Level2").is_some());
assert!(table.lookup_mut("Level3").is_some());
assert!(table.lookup_mut("Level4").is_some());
table.exit_scope(); table.exit_scope();
assert!(table.lookup_mut("Level0").is_some());
assert!(table.lookup_mut("Level1").is_some());
assert!(table.lookup_mut("Level2").is_some());
assert!(table.lookup_mut("Level3").is_none());
assert!(table.lookup_mut("Level4").is_none());
}
#[test]
fn test_different_symbol_types_in_chain() {
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,
specializes: Vec::new(),
},
)
.unwrap();
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,
subsets: Vec::new(),
redefines: Vec::new(),
},
)
.unwrap();
assert!(table.lookup_mut("RootPkg").is_some());
assert!(table.lookup_mut("MyClass").is_some());
assert!(table.lookup_mut("MyFeature").is_some());
let pkg = table.lookup_mut("RootPkg").unwrap();
assert!(matches!(
pkg,
Symbol::Package {
documentation: None,
..
}
));
let class = table.lookup_mut("MyClass").unwrap();
assert!(matches!(class, Symbol::Classifier { .. }));
let feature = table.lookup_mut("MyFeature").unwrap();
assert!(matches!(feature, Symbol::Feature { .. }));
}
#[test]
fn test_scope_chain_after_enter_exit() {
let mut table = SymbolTable::new();
table
.insert(
"Root".to_string(),
Symbol::Package {
documentation: None,
scope_id: 0,
source_file: None,
span: None,
name: "Root".to_string(),
qualified_name: "Root".to_string(),
},
)
.unwrap();
table.enter_scope();
table
.insert(
"Child1".to_string(),
Symbol::Package {
documentation: None,
scope_id: 1,
source_file: None,
span: None,
name: "Child1".to_string(),
qualified_name: "Root::Child1".to_string(),
},
)
.unwrap();
table.enter_scope();
table
.insert(
"Child2".to_string(),
Symbol::Package {
documentation: None,
scope_id: 2,
source_file: None,
span: None,
name: "Child2".to_string(),
qualified_name: "Root::Child1::Child2".to_string(),
},
)
.unwrap();
assert!(table.lookup_mut("Root").is_some());
assert!(table.lookup_mut("Child1").is_some());
assert!(table.lookup_mut("Child2").is_some());
table.exit_scope();
assert!(table.lookup_mut("Root").is_some());
assert!(table.lookup_mut("Child1").is_some());
assert!(table.lookup_mut("Child2").is_none());
table.enter_scope();
assert!(table.lookup_mut("Child2").is_none());
table
.insert(
"Child2New".to_string(),
Symbol::Package {
documentation: None,
scope_id: 3,
source_file: None,
span: None,
name: "Child2New".to_string(),
qualified_name: "Root::Child1::Child2New".to_string(),
},
)
.unwrap();
assert!(table.lookup_mut("Child2New").is_some());
assert!(table.lookup_mut("Child1").is_some());
assert!(table.lookup_mut("Root").is_some());
}
#[test]
fn test_alias_symbols_in_chain() {
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();
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 found = table.lookup_mut("AliasSymbol");
assert!(found.is_some());
assert!(matches!(found.unwrap(), Symbol::Alias { .. }));
let real = table.lookup_mut("RealSymbol");
assert!(real.is_some());
assert!(matches!(
real.unwrap(),
Symbol::Package {
documentation: None,
..
}
));
}
#[test]
fn test_empty_string_name() {
let mut table = SymbolTable::new();
let symbol = Symbol::Package {
documentation: None,
scope_id: 0,
source_file: None,
span: None,
name: "".to_string(),
qualified_name: "".to_string(),
};
table.insert("".to_string(), symbol).unwrap();
let found = table.lookup_mut("");
assert!(found.is_some());
assert_eq!(found.unwrap().name(), "");
}
#[test]
fn test_special_characters_in_name() {
let mut table = SymbolTable::new();
let special_names = vec![
"name-with-dash",
"name_with_underscore",
"name::with::colons",
"name.with.dots",
"name123",
];
for name in &special_names {
let symbol = Symbol::Package {
documentation: None,
scope_id: 0,
source_file: None,
span: None,
name: name.to_string(),
qualified_name: name.to_string(),
};
table.insert(name.to_string(), symbol).unwrap();
}
for name in special_names {
let found = table.lookup_mut(name);
assert!(found.is_some(), "Should find symbol with name: {name}");
assert_eq!(found.unwrap().name(), name);
}
}
#[test]
fn test_symbol_in_middle_of_chain() {
let mut table = SymbolTable::new();
table.enter_scope();
let symbol = Symbol::Package {
documentation: None,
scope_id: 1,
source_file: None,
span: None,
name: "MiddleSymbol".to_string(),
qualified_name: "MiddleSymbol".to_string(),
};
table.insert("MiddleSymbol".to_string(), symbol).unwrap();
table.enter_scope(); table.enter_scope();
let found = table.lookup_mut("MiddleSymbol");
assert!(found.is_some());
assert_eq!(found.unwrap().name(), "MiddleSymbol");
}