pr 05.e
This commit is contained in:
parent
e6cfef9a77
commit
acd74d57ff
@ -1,3 +1,4 @@
|
||||
use crate::analysis::symbols::{SymbolArena, SymbolId, NodeToSymbol};
|
||||
use crate::analysis::types::{TypeArena, TypeFacts, TypeId, TypeKind};
|
||||
use crate::common::diagnostics::{Diagnostic, DiagnosticBundle, DiagnosticLevel};
|
||||
use crate::common::spans::Span;
|
||||
@ -14,11 +15,13 @@ pub struct Resolver<'a> {
|
||||
interner: &'a NameInterner,
|
||||
module_provider: &'a dyn ModuleProvider,
|
||||
current_module: &'a ModuleSymbols,
|
||||
scopes: Vec<HashMap<NameId, Symbol>>,
|
||||
scopes: Vec<HashMap<NameId, (Symbol, Option<SymbolId>)>>,
|
||||
pub imported_symbols: ModuleSymbols,
|
||||
diagnostics: Vec<Diagnostic>,
|
||||
pub type_arena: TypeArena,
|
||||
pub type_facts: TypeFacts,
|
||||
pub symbol_arena: crate::analysis::symbols::SymbolArena,
|
||||
pub node_to_symbol: crate::analysis::symbols::NodeToSymbol,
|
||||
primitives: HashMap<String, TypeId>,
|
||||
}
|
||||
|
||||
@ -37,6 +40,8 @@ impl<'a> Resolver<'a> {
|
||||
diagnostics: Vec::new(),
|
||||
type_arena: TypeArena::new(),
|
||||
type_facts: TypeFacts::new(),
|
||||
symbol_arena: SymbolArena::new(),
|
||||
node_to_symbol: NodeToSymbol::new(),
|
||||
primitives: HashMap::new(),
|
||||
}
|
||||
}
|
||||
@ -152,7 +157,13 @@ impl<'a> Resolver<'a> {
|
||||
NodeKind::Call(n) => {
|
||||
if let NodeKind::Ident(id) = arena.kind(n.callee) {
|
||||
if !self.is_builtin(id.name, Namespace::Value) {
|
||||
let value_sym = self.lookup_identifier(id.name, Namespace::Value);
|
||||
let (value_sym, sym_id) = match self.lookup_with_id(id.name, Namespace::Value) {
|
||||
Some((sym, id)) => (Some(sym), id),
|
||||
None => (None, None),
|
||||
};
|
||||
if let Some(sid) = sym_id {
|
||||
self.node_to_symbol.bind_node(n.callee, sid);
|
||||
}
|
||||
if value_sym.is_none() && self.lookup_identifier(id.name, Namespace::Type).is_none() {
|
||||
// TODO: Resolver for v0 allows unresolved call targets (e.g. constructors via prelude)
|
||||
}
|
||||
@ -304,10 +315,14 @@ impl<'a> Resolver<'a> {
|
||||
}
|
||||
}
|
||||
NodeKind::Ident(n) => {
|
||||
let _sym = self.resolve_identifier(n.name, arena.span(node), Namespace::Value);
|
||||
// For v0 bootstrap, if we found a local variable, we don't know its type yet (no inference)
|
||||
// but if we had a way to track symbol types, we'd use it here.
|
||||
// For now, we only type literals and simple operators.
|
||||
if let Some((_sym, Some(sym_id))) = self.lookup_with_id(n.name, Namespace::Value) {
|
||||
self.node_to_symbol.bind_node(node, sym_id);
|
||||
if let Some(ty) = self.type_facts.get_symbol_type(sym_id) {
|
||||
self.type_facts.set_node_type(node, ty);
|
||||
}
|
||||
} else {
|
||||
self.resolve_identifier(n.name, arena.span(node), Namespace::Value);
|
||||
}
|
||||
}
|
||||
NodeKind::IntLit(_) => {
|
||||
if let Some(id) = self.primitives.get("int") {
|
||||
@ -353,8 +368,14 @@ impl<'a> Resolver<'a> {
|
||||
fn resolve_fn_decl(&mut self, arena: &AstArena, _id: NodeId, n: &FnDeclNodeArena) {
|
||||
self.enter_scope();
|
||||
for param in &n.params {
|
||||
self.resolve_type_ref(arena, param.ty);
|
||||
self.define_local(param.name, param.span, SymbolKind::Local);
|
||||
let ty_id = self.resolve_type_ref(arena, param.ty);
|
||||
let sym_id = self.define_local(param.name, param.span, SymbolKind::Local);
|
||||
if let (Some(tid), Some(sid)) = (ty_id, sym_id) {
|
||||
self.type_facts.set_symbol_type(sid, tid);
|
||||
// O node do Ident do parâmetro na declaração também pode ser vinculado
|
||||
// Mas o ParamNodeArena não tem um NodeId direto no AST que represente o Ident do parâmetro.
|
||||
// O `param.name` é NameId.
|
||||
}
|
||||
}
|
||||
if let Some(ret) = n.ret {
|
||||
self.resolve_type_ref(arena, ret);
|
||||
@ -440,15 +461,29 @@ impl<'a> Resolver<'a> {
|
||||
}
|
||||
|
||||
fn resolve_let_stmt(&mut self, arena: &AstArena, id: NodeId, n: &LetStmtNodeArena) {
|
||||
if let Some(ty) = n.ty {
|
||||
self.resolve_type_ref(arena, ty);
|
||||
}
|
||||
let ty_id = if let Some(ty) = n.ty {
|
||||
self.resolve_type_ref(arena, ty)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.resolve_node(arena, n.init);
|
||||
self.define_local(n.name, arena.span(id), SymbolKind::Local);
|
||||
let sym_id = self.define_local(n.name, arena.span(id), SymbolKind::Local);
|
||||
|
||||
if let (Some(tid), Some(sid)) = (ty_id, sym_id) {
|
||||
self.type_facts.set_symbol_type(sid, tid);
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_type_ref(&mut self, arena: &AstArena, node: NodeId) {
|
||||
fn resolve_type_ref(&mut self, arena: &AstArena, node: NodeId) -> Option<TypeId> {
|
||||
self.resolve_node(arena, node);
|
||||
|
||||
match arena.kind(node) {
|
||||
NodeKind::TypeName(n) => {
|
||||
let name = self.interner.resolve(n.name);
|
||||
self.primitives.get(name).copied()
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_identifier(&mut self, name: NameId, span: Span, namespace: Namespace) -> Option<Symbol> {
|
||||
@ -489,11 +524,15 @@ impl<'a> Resolver<'a> {
|
||||
}
|
||||
|
||||
fn lookup_identifier(&self, name: NameId, namespace: Namespace) -> Option<Symbol> {
|
||||
self.lookup_with_id(name, namespace).map(|(sym, _)| sym)
|
||||
}
|
||||
|
||||
fn lookup_with_id(&self, name: NameId, namespace: Namespace) -> Option<(Symbol, Option<SymbolId>)> {
|
||||
// 1. local bindings
|
||||
if namespace == Namespace::Value {
|
||||
for scope in self.scopes.iter().rev() {
|
||||
if let Some(sym) = scope.get(&name) {
|
||||
return Some(sym.clone());
|
||||
if let Some(pair) = scope.get(&name) {
|
||||
return Some(pair.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -506,7 +545,7 @@ impl<'a> Resolver<'a> {
|
||||
|
||||
// 2 & 3. file-private and module symbols
|
||||
if let Some(sym) = table.get(name) {
|
||||
return Some(sym.clone());
|
||||
return Some((sym.clone(), None));
|
||||
}
|
||||
|
||||
// 4. imported symbols
|
||||
@ -516,19 +555,19 @@ impl<'a> Resolver<'a> {
|
||||
&self.imported_symbols.value_symbols
|
||||
};
|
||||
if let Some(sym) = imp_table.get(name) {
|
||||
return Some(sym.clone());
|
||||
return Some((sym.clone(), None));
|
||||
}
|
||||
|
||||
// 5. Fallback for constructor calls: check Type namespace if looking for a Value
|
||||
if namespace == Namespace::Value {
|
||||
if let Some(sym) = self.current_module.type_symbols.get(name) {
|
||||
if sym.kind == SymbolKind::Struct {
|
||||
return Some(sym.clone());
|
||||
return Some((sym.clone(), None));
|
||||
}
|
||||
}
|
||||
if let Some(sym) = self.imported_symbols.type_symbols.get(name) {
|
||||
if sym.kind == SymbolKind::Struct {
|
||||
return Some(sym.clone());
|
||||
return Some((sym.clone(), None));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -536,7 +575,7 @@ impl<'a> Resolver<'a> {
|
||||
None
|
||||
}
|
||||
|
||||
fn define_local(&mut self, name: NameId, span: Span, kind: SymbolKind) {
|
||||
fn define_local(&mut self, name: NameId, span: Span, kind: SymbolKind) -> Option<SymbolId> {
|
||||
let scope = self.scopes.last_mut().expect("No scope to define local");
|
||||
|
||||
// Check for collision in Type namespace at top-level?
|
||||
@ -552,7 +591,7 @@ impl<'a> Resolver<'a> {
|
||||
),
|
||||
span: Some(span),
|
||||
});
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
if scope.contains_key(&name) {
|
||||
@ -562,8 +601,9 @@ impl<'a> Resolver<'a> {
|
||||
message: format!("Duplicate local variable '{}'", self.interner.resolve(name)),
|
||||
span: Some(span),
|
||||
});
|
||||
None
|
||||
} else {
|
||||
scope.insert(name, Symbol {
|
||||
let symbol = Symbol {
|
||||
name,
|
||||
kind,
|
||||
namespace: Namespace::Value,
|
||||
@ -572,7 +612,22 @@ impl<'a> Resolver<'a> {
|
||||
is_host: false,
|
||||
span,
|
||||
origin: None,
|
||||
};
|
||||
|
||||
// Criar símbolo na symbol_arena (análise global)
|
||||
let sym_id = self.symbol_arena.insert(crate::analysis::symbols::Symbol {
|
||||
name,
|
||||
kind: match kind {
|
||||
SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local,
|
||||
_ => crate::analysis::symbols::SymbolKind::Value,
|
||||
},
|
||||
exported: false,
|
||||
module: 0, // TODO
|
||||
decl_span: span,
|
||||
});
|
||||
|
||||
scope.insert(name, (symbol, Some(sym_id)));
|
||||
Some(sym_id)
|
||||
}
|
||||
}
|
||||
|
||||
@ -626,7 +681,6 @@ mod tests {
|
||||
fn setup_test(source: &str) -> (ParsedAst, usize, NameInterner) {
|
||||
let mut fm = FileManager::new();
|
||||
let file_id = fm.add(PathBuf::from("test.pbs"), source.to_string());
|
||||
let mut interner = fm.interner().clone();
|
||||
let mut parser = parser::Parser::new(source, file_id, fm.interner_mut());
|
||||
let parsed = parser.parse_file().expect("Parsing failed");
|
||||
(parsed, file_id, fm.interner().clone())
|
||||
@ -842,7 +896,7 @@ mod tests {
|
||||
let c = -1;
|
||||
}
|
||||
";
|
||||
let (parsed, _, mut interner) = setup_test(source);
|
||||
let (parsed, _, interner) = setup_test(source);
|
||||
let mut collector = SymbolCollector::new(&interner);
|
||||
let (ts, vs) = collector.collect(&parsed.arena, parsed.root).unwrap();
|
||||
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
|
||||
@ -892,4 +946,99 @@ mod tests {
|
||||
assert!(found_b, "Binary == node not found");
|
||||
assert!(found_c, "Unary - node not found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hover_let_annotated_type() {
|
||||
let source = "
|
||||
fn main() {
|
||||
let x: int = 10;
|
||||
let y = x;
|
||||
}
|
||||
";
|
||||
let (parsed, _, interner) = setup_test(source);
|
||||
let mut collector = SymbolCollector::new(&interner);
|
||||
let (ts, vs) = collector.collect(&parsed.arena, parsed.root).unwrap();
|
||||
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
|
||||
|
||||
struct EmptyProvider;
|
||||
impl ModuleProvider for EmptyProvider {
|
||||
fn get_module_symbols(&self, _path: &str) -> Option<&ModuleSymbols> { None }
|
||||
}
|
||||
|
||||
let mut interner_for_bootstrap = interner.clone();
|
||||
let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner);
|
||||
resolver.bootstrap_types(&mut interner_for_bootstrap);
|
||||
resolver.resolve(&parsed.arena, parsed.root).expect("Resolution failed");
|
||||
|
||||
use crate::analysis::types::format_type;
|
||||
|
||||
let mut found_y_ref = false;
|
||||
|
||||
for i in 0..parsed.arena.nodes.len() {
|
||||
let id = NodeId(i as u32);
|
||||
let kind = parsed.arena.kind(id);
|
||||
|
||||
if let NodeKind::LetStmt(n) = kind {
|
||||
if interner.resolve(n.name) == "y" {
|
||||
let init_id = n.init;
|
||||
if let NodeKind::Ident(ident) = parsed.arena.kind(init_id) {
|
||||
if interner.resolve(ident.name) == "x" {
|
||||
let ty = resolver.type_facts.get_node_type(init_id).expect("Ident x should have type");
|
||||
assert_eq!(format_type(ty, &resolver.type_arena, &interner_for_bootstrap, None), "int");
|
||||
found_y_ref = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert!(found_y_ref, "Reference to x in let y = x not found or not typed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hover_param_type() {
|
||||
let source = "
|
||||
fn foo(a: int) {
|
||||
let b = a;
|
||||
}
|
||||
";
|
||||
let (parsed, _, interner) = setup_test(source);
|
||||
let mut collector = SymbolCollector::new(&interner);
|
||||
let (ts, vs) = collector.collect(&parsed.arena, parsed.root).unwrap();
|
||||
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
|
||||
|
||||
struct EmptyProvider;
|
||||
impl ModuleProvider for EmptyProvider {
|
||||
fn get_module_symbols(&self, _path: &str) -> Option<&ModuleSymbols> { None }
|
||||
}
|
||||
|
||||
let mut interner_for_bootstrap = interner.clone();
|
||||
let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner);
|
||||
resolver.bootstrap_types(&mut interner_for_bootstrap);
|
||||
resolver.resolve(&parsed.arena, parsed.root).expect("Resolution failed");
|
||||
|
||||
use crate::analysis::types::format_type;
|
||||
|
||||
let mut found_a_ref = false;
|
||||
|
||||
for i in 0..parsed.arena.nodes.len() {
|
||||
let id = NodeId(i as u32);
|
||||
let kind = parsed.arena.kind(id);
|
||||
|
||||
if let NodeKind::LetStmt(n) = kind {
|
||||
if interner.resolve(n.name) == "b" {
|
||||
let init_id = n.init;
|
||||
if let NodeKind::Ident(ident) = parsed.arena.kind(init_id) {
|
||||
if interner.resolve(ident.name) == "a" {
|
||||
let ty = resolver.type_facts.get_node_type(init_id).expect("Ident a should have type");
|
||||
assert_eq!(format_type(ty, &resolver.type_arena, &interner_for_bootstrap, None), "int");
|
||||
found_a_ref = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert!(found_a_ref, "Reference to a in let b = a not found or not typed");
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user