1390 lines
58 KiB
Rust

use crate::analysis::symbols::{SymbolArena, NodeToSymbol};
use crate::analysis::types::{TypeArena, TypeFacts, TypeKind};
use crate::common::diagnostics::{Diagnostic, DiagnosticBundle, Severity};
use crate::common::spans::Span;
use crate::frontends::pbs::ast::*;
use crate::frontends::pbs::symbols::*;
use prometeu_analysis::{NameId, NameInterner, SymbolId, ModuleId, TypeId, NodeId};
use std::collections::HashMap;
pub trait ModuleProvider {
fn get_module_symbols(&self, from_path: &str) -> Option<&ModuleSymbols>;
}
pub struct Resolver<'a> {
interner: &'a NameInterner,
module_provider: &'a dyn ModuleProvider,
current_module: &'a ModuleSymbols,
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>,
}
impl<'a> Resolver<'a> {
pub fn new(
current_module: &'a ModuleSymbols,
module_provider: &'a dyn ModuleProvider,
interner: &'a NameInterner,
) -> Self {
Self {
interner,
module_provider,
current_module,
scopes: Vec::new(),
imported_symbols: ModuleSymbols::new(),
diagnostics: Vec::new(),
type_arena: TypeArena::new(),
type_facts: TypeFacts::new(),
symbol_arena: SymbolArena::new(),
node_to_symbol: NodeToSymbol::new(),
primitives: HashMap::new(),
}
}
pub fn bootstrap_types(&mut self, interner: &NameInterner) {
let primitive_names = ["int", "bool", "float", "string", "bounded", "void"];
for name in primitive_names {
let name_id = interner.get(name).expect("primitive name not interned");
let type_id = self.type_arena.intern_type(TypeKind::Primitive { name: name_id });
self.primitives.insert(name.to_string(), type_id);
}
}
pub fn resolve(&mut self, arena: &AstArena, root: NodeId) -> Result<(), DiagnosticBundle> {
let file = match arena.kind(root) {
NodeKind::File(file) => file,
_ => {
return Err(DiagnosticBundle::error(
"E_RESOLVE_INVALID_ROOT",
"Expected File node as root".to_string(),
arena.span(root),
))
}
};
// Step 0: Populate symbol_arena with top-level symbols for global lookup
for (name, list) in &self.current_module.type_symbols.symbols {
for sym in list {
self.symbol_arena.insert(crate::analysis::symbols::Symbol {
name: *name,
kind: match sym.kind {
SymbolKind::Function => crate::analysis::symbols::SymbolKind::Function,
SymbolKind::Service => crate::analysis::symbols::SymbolKind::Service,
SymbolKind::Struct => crate::analysis::symbols::SymbolKind::Struct,
SymbolKind::Contract => crate::analysis::symbols::SymbolKind::Contract,
SymbolKind::ErrorType => crate::analysis::symbols::SymbolKind::ErrorType,
SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local,
},
exported: sym.visibility == Visibility::Pub,
module: ModuleId(0),
decl_span: sym.span.clone(),
});
}
}
for (name, list) in &self.current_module.value_symbols.symbols {
for sym in list {
self.symbol_arena.insert(crate::analysis::symbols::Symbol {
name: *name,
kind: match sym.kind {
SymbolKind::Function => crate::analysis::symbols::SymbolKind::Function,
SymbolKind::Service => crate::analysis::symbols::SymbolKind::Service,
SymbolKind::Struct => crate::analysis::symbols::SymbolKind::Struct,
SymbolKind::Contract => crate::analysis::symbols::SymbolKind::Contract,
SymbolKind::ErrorType => crate::analysis::symbols::SymbolKind::ErrorType,
SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local,
},
exported: sym.visibility == Visibility::Pub,
module: ModuleId(0),
decl_span: sym.span.clone(),
});
}
}
// Step 1: Process imports to populate imported_symbols
for imp in &file.imports {
if let NodeKind::Import(imp_node) = arena.kind(*imp) {
self.resolve_import(arena, *imp, imp_node);
}
}
// Add imported symbols to symbol_arena too
for (name, list) in &self.imported_symbols.type_symbols.symbols {
for sym in list {
self.symbol_arena.insert(crate::analysis::symbols::Symbol {
name: *name,
kind: match sym.kind {
SymbolKind::Function => crate::analysis::symbols::SymbolKind::Function,
SymbolKind::Service => crate::analysis::symbols::SymbolKind::Service,
SymbolKind::Struct => crate::analysis::symbols::SymbolKind::Struct,
SymbolKind::Contract => crate::analysis::symbols::SymbolKind::Contract,
SymbolKind::ErrorType => crate::analysis::symbols::SymbolKind::ErrorType,
SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local,
},
exported: sym.visibility == Visibility::Pub,
module: ModuleId(0), // Should be target module
decl_span: sym.span.clone(),
});
}
}
for (name, list) in &self.imported_symbols.value_symbols.symbols {
for sym in list {
self.symbol_arena.insert(crate::analysis::symbols::Symbol {
name: *name,
kind: match sym.kind {
SymbolKind::Function => crate::analysis::symbols::SymbolKind::Function,
SymbolKind::Service => crate::analysis::symbols::SymbolKind::Service,
SymbolKind::Struct => crate::analysis::symbols::SymbolKind::Struct,
SymbolKind::Contract => crate::analysis::symbols::SymbolKind::Contract,
SymbolKind::ErrorType => crate::analysis::symbols::SymbolKind::ErrorType,
SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local,
},
exported: sym.visibility == Visibility::Pub,
module: ModuleId(0), // Should be target module
decl_span: sym.span.clone(),
});
}
}
// Step 2: Resolve all top-level declarations
for decl in &file.decls {
self.resolve_node(arena, *decl);
}
if !self.diagnostics.is_empty() {
return Err(DiagnosticBundle {
diagnostics: self.diagnostics.clone(),
});
}
Ok(())
}
fn resolve_import(&mut self, arena: &AstArena, imp_id: NodeId, imp: &ImportNodeArena) {
let provider = self.module_provider;
if let Some(target_symbols) = provider.get_module_symbols(&imp.from) {
let spec = match arena.kind(imp.spec) {
NodeKind::ImportSpec(spec) => spec,
_ => {
self.diagnostics.push(Diagnostic {
severity: Severity::Error,
code: "E_RESOLVE_INVALID_IMPORT".to_string(),
message: "Invalid import spec".to_string(),
span: arena.span(imp_id),
related: Vec::new(),
});
return;
}
};
for name in &spec.path {
// Try to find in Type namespace
if let Some(sym) = target_symbols.type_symbols.get(*name) {
if sym.visibility == Visibility::Pub {
let is_service = sym.kind == SymbolKind::Service;
let mut cloned = sym.clone();
cloned.origin = Some(imp.from.clone());
if let Err(_) = self.imported_symbols.type_symbols.insert(cloned) {
self.error_duplicate_import(*name, arena.span(imp_id));
}
// If a Service type is imported, also bring its public methods from the same module into value namespace
if is_service {
let base = self.interner.resolve(*name);
let prefix = format!("{}.", base);
for list in target_symbols.value_symbols.symbols.values() {
for vs in list {
// Apenas métodos do service importado: nomes exportados de métodos devem ser no formato "Service.method#sigN"
let vname = self.interner.resolve(vs.name);
if vname.starts_with(&prefix) {
if vs.visibility == Visibility::Pub {
let mut vsym = vs.clone();
vsym.origin = Some(imp.from.clone());
let _ = self.imported_symbols.value_symbols.insert(vsym);
}
}
}
}
}
} else {
self.error_visibility(sym, arena.span(imp_id));
}
}
// Try to find in Value namespace
else if let Some(sym) = target_symbols.value_symbols.get(*name) {
if sym.visibility == Visibility::Pub {
let mut sym = sym.clone();
sym.origin = Some(imp.from.clone());
if let Err(_) = self.imported_symbols.value_symbols.insert(sym) {
self.error_duplicate_import(*name, arena.span(imp_id));
}
} else {
self.error_visibility(sym, arena.span(imp_id));
}
} else {
self.error_undefined(*name, arena.span(imp_id));
}
}
} else {
self.diagnostics.push(Diagnostic {
severity: Severity::Error,
code: "E_RESOLVE_INVALID_IMPORT".to_string(),
message: format!("Module not found: {}", imp.from),
span: arena.span(imp_id),
related: Vec::new(),
});
}
}
fn resolve_node(&mut self, arena: &AstArena, node: NodeId) {
match arena.kind(node) {
NodeKind::FnDecl(n) => self.resolve_fn_decl(arena, node, n),
NodeKind::ServiceDecl(n) => self.resolve_service_decl(arena, node, n),
NodeKind::TypeDecl(n) => self.resolve_type_decl(arena, node, n),
NodeKind::Block(n) => self.resolve_block(arena, n),
NodeKind::LetStmt(n) => self.resolve_let_stmt(arena, node, n),
NodeKind::ExprStmt(n) => self.resolve_node(arena, n.expr),
NodeKind::ReturnStmt(n) => {
if let Some(expr) = n.expr {
self.resolve_node(arena, expr);
}
}
NodeKind::Call(n) => {
if let NodeKind::Ident(id) = arena.kind(n.callee) {
if !self.is_builtin(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)
}
}
} else {
self.resolve_node(arena, n.callee);
}
for arg in &n.args {
self.resolve_node(arena, *arg);
}
}
NodeKind::Unary(n) => {
self.resolve_node(arena, n.expr);
match n.op.as_str() {
"-" => {
let int_type = self.primitives.get("int").copied();
if let Some(expr_type) = self.type_facts.get_node_type(n.expr) {
if Some(expr_type) == int_type {
if let Some(id) = int_type {
self.type_facts.set_node_type(node, id);
}
} else {
self.diagnostics.push(Diagnostic {
severity: Severity::Error,
code: "E_TYPE_MISMATCH".to_string(),
message: "Unary '-' operator expects 'int'".to_string(),
span: arena.span(node),
related: Vec::new(),
});
}
}
}
"!" => {
let bool_type = self.primitives.get("bool").copied();
if let Some(expr_type) = self.type_facts.get_node_type(n.expr) {
if Some(expr_type) == bool_type {
if let Some(id) = bool_type {
self.type_facts.set_node_type(node, id);
}
} else {
self.diagnostics.push(Diagnostic {
severity: Severity::Error,
code: "E_TYPE_MISMATCH".to_string(),
message: "Unary '!' operator expects 'bool'".to_string(),
span: arena.span(node),
related: Vec::new(),
});
}
}
}
_ => {}
}
}
NodeKind::Binary(n) => {
self.resolve_node(arena, n.left);
self.resolve_node(arena, n.right);
let left_type = self.type_facts.get_node_type(n.left);
let right_type = self.type_facts.get_node_type(n.right);
let int_type = self.primitives.get("int").copied();
let bool_type = self.primitives.get("bool").copied();
match n.op.as_str() {
"+" | "-" | "*" | "/" => {
if let (Some(l), Some(r)) = (left_type, right_type) {
if Some(l) == int_type && Some(r) == int_type {
if let Some(id) = int_type {
self.type_facts.set_node_type(node, id);
}
} else {
self.diagnostics.push(Diagnostic {
severity: Severity::Error,
code: "E_TYPE_MISMATCH".to_string(),
message: format!("Binary '{}' operator expects 'int' operands", n.op),
span: arena.span(node),
related: Vec::new(),
});
}
}
}
"==" | "<" | ">" | "<=" | ">=" | "!=" => {
if let (Some(l), Some(r)) = (left_type, right_type) {
if l == r {
if let Some(id) = bool_type {
self.type_facts.set_node_type(node, id);
}
} else {
self.diagnostics.push(Diagnostic {
severity: Severity::Error,
code: "E_TYPE_MISMATCH".to_string(),
message: format!("Binary '{}' operator expects operands of the same type", n.op),
span: arena.span(node),
related: Vec::new(),
});
}
}
}
_ => {}
}
}
NodeKind::TypeName(_) | NodeKind::TypeApp(_) => {
self.resolve_type_ref(arena, node);
}
NodeKind::ConstructorDecl(n) => self.resolve_constructor_decl(arena, node, n),
NodeKind::ConstantDecl(n) => self.resolve_node(arena, n.value),
NodeKind::Alloc(n) => {
self.resolve_type_ref(arena, n.ty);
}
NodeKind::Mutate(n) => {
self.resolve_node(arena, n.target);
self.enter_scope();
self.define_local(n.binding, arena.span(node), SymbolKind::Local);
self.resolve_node(arena, n.body);
self.exit_scope();
}
NodeKind::Borrow(n) => {
self.resolve_node(arena, n.target);
self.enter_scope();
self.define_local(n.binding, arena.span(node), SymbolKind::Local);
self.resolve_node(arena, n.body);
self.exit_scope();
}
NodeKind::Peek(n) => {
self.resolve_node(arena, n.target);
self.enter_scope();
self.define_local(n.binding, arena.span(node), SymbolKind::Local);
self.resolve_node(arena, n.body);
self.exit_scope();
}
NodeKind::Cast(n) => {
self.resolve_node(arena, n.expr);
self.resolve_type_ref(arena, n.ty);
}
NodeKind::IfExpr(n) => {
self.resolve_node(arena, n.cond);
self.resolve_node(arena, n.then_block);
if let Some(else_block) = n.else_block {
self.resolve_node(arena, else_block);
}
}
NodeKind::WhenExpr(n) => {
for arm in &n.arms {
if let NodeKind::WhenArm(arm_node) = arena.kind(*arm) {
self.resolve_node(arena, arm_node.cond);
self.resolve_node(arena, arm_node.body);
}
}
}
NodeKind::Ident(n) => {
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") {
self.type_facts.set_node_type(node, *id);
}
}
NodeKind::FloatLit(_) => {
if let Some(id) = self.primitives.get("float") {
self.type_facts.set_node_type(node, *id);
}
}
NodeKind::BoundedLit(_) => {
if let Some(id) = self.primitives.get("bounded") {
self.type_facts.set_node_type(node, *id);
}
}
NodeKind::StringLit(_) => {
if let Some(id) = self.primitives.get("string") {
self.type_facts.set_node_type(node, *id);
}
}
NodeKind::MemberAccess(n) => match arena.kind(n.object) {
NodeKind::Ident(id) => {
let ident_span = arena.span(n.object);
if !self.is_builtin(id.name, Namespace::Type) {
if self.lookup_identifier(id.name, Namespace::Value).is_none() {
// If not found in Value namespace, try Type namespace (for Contracts/Services)
if self.lookup_identifier(id.name, Namespace::Type).is_none() {
// Still not found, use resolve_identifier to report error in Value namespace
self.resolve_identifier(id.name, ident_span, Namespace::Value);
}
}
}
}
_ => {
self.resolve_node(arena, n.object);
}
},
_ => {}
}
}
fn resolve_fn_decl(&mut self, arena: &AstArena, _id: NodeId, n: &FnDeclNodeArena) {
self.enter_scope();
for param in &n.params {
let ty_id = self.resolve_type_ref(arena, param.ty);
let sym_id = self.define_local(param.name, param.span.clone(), SymbolKind::Local);
if let (Some(tid), Some(sid)) = (ty_id, sym_id) {
self.type_facts.set_symbol_type(sid, tid);
// The Ident node of the parameter in the declaration could also be bound
// But ParamNodeArena does not have a direct NodeId in the AST that represents the parameter's Ident.
// `param.name` is a NameId.
}
}
if let Some(ret) = n.ret {
self.resolve_type_ref(arena, ret);
}
self.resolve_node(arena, n.body);
self.exit_scope();
}
fn resolve_service_decl(&mut self, arena: &AstArena, id: NodeId, n: &ServiceDeclNodeArena) {
if let Some(ext) = n.extends {
self.resolve_identifier(ext, arena.span(id), Namespace::Type);
}
for member in &n.members {
match arena.kind(*member) {
NodeKind::ServiceFnSig(sig) => {
for param in &sig.params {
self.resolve_type_ref(arena, param.ty);
}
self.resolve_type_ref(arena, sig.ret);
}
NodeKind::ServiceFnDecl(decl) => {
// service com corpo: resolve tipos, define parâmetros como locais e resolve corpo
for param in &decl.params {
self.resolve_type_ref(arena, param.ty);
}
self.resolve_type_ref(arena, decl.ret);
self.enter_scope();
for param in &decl.params {
let sym_id = self.define_local(param.name, param.span.clone(), SymbolKind::Local);
if let Some(sym_id) = sym_id {
if let Some(ty_id) = self.resolve_type_ref(arena, param.ty) {
self.type_facts.set_symbol_type(sym_id, ty_id);
}
}
}
self.resolve_node(arena, decl.body);
self.exit_scope();
}
_ => {}
}
}
}
fn resolve_type_decl(&mut self, arena: &AstArena, _id: NodeId, n: &TypeDeclNodeArena) {
for param in &n.params {
self.resolve_type_ref(arena, param.ty);
}
for constructor in &n.constructors {
if let NodeKind::ConstructorDecl(ctor) = arena.kind(*constructor) {
self.resolve_constructor_decl(arena, *constructor, ctor);
}
}
self.enter_scope();
for ctor_id in &n.constructors {
if let NodeKind::ConstructorDecl(ctor) = arena.kind(*ctor_id) {
self.define_local(ctor.name, arena.span(*ctor_id), SymbolKind::Local);
}
}
for constant in &n.constants {
self.resolve_node(arena, *constant);
}
self.exit_scope();
if let Some(body_node) = n.body {
if let NodeKind::TypeBody(body) = arena.kind(body_node) {
for member in &body.members {
self.resolve_type_ref(arena, member.ty);
}
for method in &body.methods {
if let NodeKind::ServiceFnSig(sig) = arena.kind(*method) {
for param in &sig.params {
self.resolve_type_ref(arena, param.ty);
}
self.resolve_type_ref(arena, sig.ret);
}
}
}
}
}
fn resolve_constructor_decl(
&mut self,
arena: &AstArena,
_id: NodeId,
n: &ConstructorDeclNodeArena,
) {
self.enter_scope();
for param in &n.params {
self.resolve_type_ref(arena, param.ty);
self.define_local(param.name, param.span.clone(), SymbolKind::Local);
}
for init in &n.initializers {
self.resolve_node(arena, *init);
}
self.resolve_node(arena, n.body);
self.exit_scope();
}
fn resolve_block(&mut self, arena: &AstArena, n: &BlockNodeArena) {
self.enter_scope();
for stmt in &n.stmts {
self.resolve_node(arena, *stmt);
}
self.exit_scope();
}
fn resolve_let_stmt(&mut self, arena: &AstArena, id: NodeId, n: &LetStmtNodeArena) {
let ty_id = if let Some(ty) = n.ty {
self.resolve_type_ref(arena, ty)
} else {
None
};
self.resolve_node(arena, n.init);
let sym_id = self.define_local(n.name, arena.span(id), SymbolKind::Local);
if let Some(sid) = sym_id {
self.node_to_symbol.bind_node(id, sid);
if let Some(tid) = ty_id {
self.type_facts.set_symbol_type(sid, tid);
}
}
}
fn resolve_type_ref(&mut self, arena: &AstArena, node: NodeId) -> Option<TypeId> {
let type_id = self.lower_type_node(arena, node);
if let Some(tid) = type_id {
self.type_facts.set_node_type(node, tid);
}
type_id
}
fn lower_type_node(&mut self, arena: &AstArena, node: NodeId) -> Option<TypeId> {
match arena.kind(node) {
NodeKind::TypeName(n) => {
let name = self.interner.resolve(n.name);
if let Some(prim) = self.primitives.get(name) {
return Some(*prim);
}
// Resolve struct/type symbol
if let Some((_sym, sym_id)) = self.lookup_with_id(n.name, Namespace::Type) {
// Try to find if this symbol is a type symbol that can be converted to TypeId
// For now, we only have TypeKind::Struct
if let Some(sid) = sym_id {
let kind = TypeKind::Struct { sym: sid };
return Some(self.type_arena.intern_type(kind));
} else {
// sym_id is None, but _sym exists. This means it's a built-in or something not in analysis arena yet.
// But for Struct resolution we need a SymbolId.
// If it's a built-in it should have been handled by self.primitives.
}
}
// If not found in current or imports, maybe it's a symbol from another module not yet in SymbolArena?
// Actually, lookup_with_id checks imported_symbols too.
self.diagnostics.push(Diagnostic {
severity: Severity::Error,
code: "E_TYPE_UNKNOWN_TYPE".to_string(),
message: format!("Unknown type: {}", name),
span: arena.span(node),
related: Vec::new(),
});
None
}
NodeKind::TypeApp(n) => {
let base_name = self.interner.resolve(n.base);
match base_name {
"optional" => {
if n.args.len() != 1 {
self.diagnostics.push(Diagnostic {
severity: Severity::Error,
code: "E_TYPE_INVALID_ARGS".to_string(),
message: "optional<T> expects exactly 1 argument".to_string(),
span: arena.span(node),
related: Vec::new(),
});
return None;
}
let inner = self.lower_type_node(arena, n.args[0])?;
Some(self.type_arena.intern_type(TypeKind::Optional { inner }))
}
"result" => {
if n.args.len() != 2 {
self.diagnostics.push(Diagnostic {
severity: Severity::Error,
code: "E_TYPE_INVALID_ARGS".to_string(),
message: "result<T, E> expects exactly 2 arguments".to_string(),
span: arena.span(node),
related: Vec::new(),
});
return None;
}
let ok = self.lower_type_node(arena, n.args[0])?;
let err = self.lower_type_node(arena, n.args[1])?;
Some(self.type_arena.intern_type(TypeKind::Result { ok, err }))
}
"array" => {
if n.args.len() < 1 || n.args.len() > 2 {
self.diagnostics.push(Diagnostic {
severity: Severity::Error,
code: "E_TYPE_INVALID_ARGS".to_string(),
message: "array<T> or array<T>[N] expects 1 or 2 arguments".to_string(),
span: arena.span(node),
related: Vec::new(),
});
return None;
}
let inner = self.lower_type_node(arena, n.args[0])?;
let len = if n.args.len() == 2 {
match arena.kind(n.args[1]) {
NodeKind::IntLit(lit) => Some(lit.value as u32),
_ => {
self.diagnostics.push(Diagnostic {
severity: Severity::Error,
code: "E_TYPE_INVALID_ARGS".to_string(),
message: "Array length must be an integer literal".to_string(),
span: arena.span(n.args[1]),
related: Vec::new(),
});
None
}
}
} else {
None
};
Some(self.type_arena.intern_type(TypeKind::Array { inner, len }))
}
_ => {
self.diagnostics.push(Diagnostic {
severity: Severity::Error,
code: "E_TYPE_UNKNOWN_TYPE".to_string(),
message: format!("Unknown generic type: {}", base_name),
span: arena.span(node),
related: Vec::new(),
});
None
}
}
}
_ => {
self.diagnostics.push(Diagnostic {
severity: Severity::Error,
code: "E_TYPE_NOT_A_TYPE".to_string(),
message: "Expected a type node".to_string(),
span: arena.span(node),
related: Vec::new(),
});
None
}
}
}
fn resolve_identifier(&mut self, name: NameId, span: Span, namespace: Namespace) -> Option<Symbol> {
if self.is_builtin(name, namespace) {
return None;
}
if let Some(sym) = self.lookup_identifier(name, namespace) {
Some(sym)
} else {
if namespace == Namespace::Type {
self.diagnostics.push(Diagnostic {
severity: Severity::Error,
code: "E_TYPE_UNKNOWN_TYPE".to_string(),
message: format!("Unknown type: {}", self.interner.resolve(name)),
span,
related: Vec::new(),
});
} else {
self.error_undefined(name, span);
}
None
}
}
fn is_builtin(&self, name: NameId, namespace: Namespace) -> bool {
let name = self.interner.resolve(name);
match namespace {
Namespace::Type => match name {
"int" | "float" | "string" | "bool" | "void" | "optional" | "result" | "bounded" |
"Color" | "ButtonState" | "Pad" | "Touch" => true,
_ => false,
},
Namespace::Value => match name {
"none" | "some" | "ok" | "err" | "true" | "false" => true,
_ => false,
},
}
}
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(pair) = scope.get(&name) {
return Some(pair.clone());
}
}
}
let table = if namespace == Namespace::Type {
&self.current_module.type_symbols
} else {
&self.current_module.value_symbols
};
// 2 & 3. file-private and module symbols
if let Some(sym) = table.get(name) {
// Se encontrarmos no módulo atual, tentamos achar o SymbolId correspondente na symbol_arena
let sym_id = self.symbol_arena.symbols.iter().enumerate().find(|(_, s)| {
s.name == sym.name && s.kind == match sym.kind {
SymbolKind::Function => crate::analysis::symbols::SymbolKind::Function,
SymbolKind::Service => crate::analysis::symbols::SymbolKind::Service,
SymbolKind::Struct => crate::analysis::symbols::SymbolKind::Struct,
SymbolKind::Contract => crate::analysis::symbols::SymbolKind::Contract,
SymbolKind::ErrorType => crate::analysis::symbols::SymbolKind::ErrorType,
SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local,
}
}).map(|(i, _)| SymbolId(i as u32));
return Some((sym.clone(), sym_id));
}
// 4. imported symbols
let imp_table = if namespace == Namespace::Type {
&self.imported_symbols.type_symbols
} else {
&self.imported_symbols.value_symbols
};
if let Some(sym) = imp_table.get(name) {
let sym_id = self.symbol_arena.symbols.iter().enumerate().find(|(_, s)| {
s.name == sym.name && s.kind == match sym.kind {
SymbolKind::Function => crate::analysis::symbols::SymbolKind::Function,
SymbolKind::Service => crate::analysis::symbols::SymbolKind::Service,
SymbolKind::Struct => crate::analysis::symbols::SymbolKind::Struct,
SymbolKind::Contract => crate::analysis::symbols::SymbolKind::Contract,
SymbolKind::ErrorType => crate::analysis::symbols::SymbolKind::ErrorType,
SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local,
}
}).map(|(i, _)| SymbolId(i as u32));
return Some((sym.clone(), sym_id));
}
// 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(), None));
}
}
if let Some(sym) = self.imported_symbols.type_symbols.get(name) {
if sym.kind == SymbolKind::Struct {
return Some((sym.clone(), None));
}
}
}
None
}
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?
// Actually, the spec says "A name may not exist in both namespaces".
// If we want to be strict, we check current module's type symbols too.
if let Some(existing) = self.current_module.type_symbols.get(name) {
self.diagnostics.push(Diagnostic {
severity: Severity::Error,
code: "E_RESOLVE_NAMESPACE_COLLISION".to_string(),
message: format!(
"Local variable '{}' collides with a type name",
self.interner.resolve(name)
),
span,
related: vec![("type defined here".to_string(), existing.span.clone())],
});
return None;
}
if let Some((prev_sym, _)) = scope.get(&name) {
self.diagnostics.push(Diagnostic {
severity: Severity::Error,
code: "E_RESOLVE_DUPLICATE_SYMBOL".to_string(),
message: format!("Duplicate local variable '{}'", self.interner.resolve(name)),
span,
related: vec![("previous definition here".to_string(), prev_sym.span.clone())],
});
None
} else {
let symbol = Symbol {
name,
kind,
namespace: Namespace::Value,
visibility: Visibility::FilePrivate,
ty: None, // Will be set by TypeChecker
is_host: false,
span: span.clone(),
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: ModuleId(0), // TODO: set actual module id when available
decl_span: span,
});
scope.insert(name, (symbol, Some(sym_id)));
Some(sym_id)
}
}
fn enter_scope(&mut self) {
self.scopes.push(HashMap::new());
}
fn exit_scope(&mut self) {
self.scopes.pop();
}
fn error_undefined(&mut self, name: NameId, span: Span) {
self.diagnostics.push(Diagnostic {
severity: Severity::Error,
code: "E_RESOLVE_UNDEFINED".to_string(),
message: format!("Undefined identifier: {}", self.interner.resolve(name)),
span,
related: Vec::new(),
});
}
fn error_duplicate_import(&mut self, name: NameId, span: Span) {
self.diagnostics.push(Diagnostic {
severity: Severity::Error,
code: "E_RESOLVE_DUPLICATE_SYMBOL".to_string(),
message: format!("Duplicate import: {}", self.interner.resolve(name)),
span,
related: Vec::new(),
});
}
fn error_visibility(&mut self, sym: &Symbol, span: Span) {
self.diagnostics.push(Diagnostic {
severity: Severity::Error,
code: "E_RESOLVE_VISIBILITY".to_string(),
message: format!(
"DebugSymbol '{}' is not visible here",
self.interner.resolve(sym.name)
),
span,
related: vec![("symbol defined here".to_string(), sym.span.clone())],
});
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::common::files::FileManager;
use crate::common::spans::Span;
use crate::frontends::pbs::*;
use std::path::PathBuf;
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 parser = parser::Parser::new(source, crate::common::spans::FileId(file_id as u32), fm.interner_mut());
let parsed = parser.parse_file().expect("Parsing failed");
(parsed, file_id, fm.interner().clone())
}
#[test]
fn test_duplicate_symbols() {
let source = "
declare struct Foo()
declare struct Foo()
";
let (parsed, _, interner) = setup_test(source);
let mut collector = SymbolCollector::new(&interner);
let result = collector.collect(&parsed.arena, parsed.root);
assert!(result.is_err());
let bundle = result.unwrap_err();
assert!(bundle.diagnostics.iter().any(|d| d.code == "E_RESOLVE_DUPLICATE_SYMBOL"));
}
#[test]
fn test_namespace_collision() {
let source = "
declare struct Foo()
fn Foo() {}
";
let (parsed, _, interner) = setup_test(source);
let mut collector = SymbolCollector::new(&interner);
let result = collector.collect(&parsed.arena, parsed.root);
assert!(result.is_err());
let bundle = result.unwrap_err();
assert!(bundle.diagnostics.iter().any(|d| d.code == "E_RESOLVE_NAMESPACE_COLLISION"));
}
#[test]
fn test_undefined_identifier() {
let source = "
fn main() {
let x = y;
}
";
let (parsed, _, interner) = setup_test(source);
let mut collector = SymbolCollector::new(&interner);
let (ts, vs) = collector
.collect(&parsed.arena, parsed.root)
.expect("Collection failed");
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 resolver = Resolver::new(&ms, &EmptyProvider, &interner);
let result = resolver.resolve(&parsed.arena, parsed.root);
assert!(result.is_err());
let bundle = result.unwrap_err();
assert!(bundle.diagnostics.iter().any(|d| d.code == "E_RESOLVE_UNDEFINED"));
}
#[test]
fn test_local_variable_resolution() {
let source = "
fn main() {
let x = 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)
.expect("Collection failed");
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 resolver = Resolver::new(&ms, &EmptyProvider, &interner);
let result = resolver.resolve(&parsed.arena, parsed.root);
assert!(result.is_ok());
}
#[test]
fn test_visibility_error() {
let source = "
import PrivateType from \"./other.pbs\"
fn main() {}
";
let (parsed, _, mut interner) = setup_test(source);
let mut collector = SymbolCollector::new(&interner);
let (ts, vs) = collector
.collect(&parsed.arena, parsed.root)
.expect("Collection failed");
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
struct MockProvider {
other: ModuleSymbols,
}
impl ModuleProvider for MockProvider {
fn get_module_symbols(&self, path: &str) -> Option<&ModuleSymbols> {
if path == "./other.pbs" { Some(&self.other) } else { None }
}
}
let mut other_ts = SymbolTable::new();
other_ts.insert(Symbol {
name: interner.intern("PrivateType"),
kind: SymbolKind::Struct,
namespace: Namespace::Type,
visibility: Visibility::FilePrivate,
ty: None,
is_host: false,
span: Span::new(crate::common::spans::FileId(1), 0, 0),
origin: None,
}).unwrap();
let mock_provider = MockProvider {
other: ModuleSymbols { type_symbols: other_ts, value_symbols: SymbolTable::new() },
};
let mut resolver = Resolver::new(&ms, &mock_provider, &interner);
let result = resolver.resolve(&parsed.arena, parsed.root);
assert!(result.is_err());
let bundle = result.unwrap_err();
assert!(bundle.diagnostics.iter().any(|d| d.code == "E_RESOLVE_VISIBILITY"));
}
#[test]
fn test_import_resolution() {
let source = "
import PubType from \"./other.pbs\"
fn main() {
let x: PubType = 10;
}
";
let (parsed, _, mut interner) = setup_test(source);
let mut collector = SymbolCollector::new(&interner);
let (ts, vs) = collector
.collect(&parsed.arena, parsed.root)
.expect("Collection failed");
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
struct MockProvider {
other: ModuleSymbols,
}
impl ModuleProvider for MockProvider {
fn get_module_symbols(&self, path: &str) -> Option<&ModuleSymbols> {
if path == "./other.pbs" { Some(&self.other) } else { None }
}
}
let mut other_ts = SymbolTable::new();
other_ts.insert(Symbol {
name: interner.intern("PubType"),
kind: SymbolKind::Struct,
namespace: Namespace::Type,
visibility: Visibility::Pub,
ty: None,
is_host: false,
span: Span::new(crate::common::spans::FileId(1), 0, 0),
origin: None,
}).unwrap();
let mock_provider = MockProvider {
other: ModuleSymbols { type_symbols: other_ts, value_symbols: SymbolTable::new() },
};
let mut resolver = Resolver::new(&ms, &mock_provider, &interner);
let result = resolver.resolve(&parsed.arena, parsed.root);
assert!(result.is_ok());
}
#[test]
fn test_invalid_import_module_not_found() {
let source = "
import NonExistent from \"./missing.pbs\"
fn main() {}
";
let (parsed, _, interner) = setup_test(source);
let mut collector = SymbolCollector::new(&interner);
let (ts, vs) = collector
.collect(&parsed.arena, parsed.root)
.expect("Collection failed");
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 resolver = Resolver::new(&ms, &EmptyProvider, &interner);
let result = resolver.resolve(&parsed.arena, parsed.root);
assert!(result.is_err());
let bundle = result.unwrap_err();
assert!(bundle.diagnostics.iter().any(|d| d.code == "E_RESOLVE_INVALID_IMPORT"));
}
#[test]
fn test_typing_literals_and_binary() {
let source = "
fn main() {
let a = 1 + 2;
let b = 1 == 2;
let c = -1;
}
";
let (parsed, _, mut 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 }
}
// Ensure primitives are interned for bootstrap
for p in ["int", "bool", "float", "string", "bounded", "void"] { interner.intern(p); }
let interner_for_bootstrap = interner.clone();
let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner);
resolver.bootstrap_types(&interner);
resolver.resolve(&parsed.arena, parsed.root).expect("Resolution failed");
// Verify types in TypeFacts using format_type for easier assertion
use crate::analysis::types::format_type;
let mut found_a = false;
let mut found_b = false;
let mut found_c = false;
for i in 0..parsed.arena.nodes.len() {
let id = NodeId(i as u32);
let kind = parsed.arena.kind(id);
match kind {
NodeKind::Binary(n) if n.op == "+" => {
let ty = resolver.type_facts.get_node_type(id).expect("Binary + should have type");
assert_eq!(format_type(ty, &resolver.type_arena, &interner_for_bootstrap, None), "int");
found_a = true;
}
NodeKind::Binary(n) if n.op == "==" => {
let ty = resolver.type_facts.get_node_type(id).expect("Binary == should have type");
assert_eq!(format_type(ty, &resolver.type_arena, &interner_for_bootstrap, None), "bool");
found_b = true;
}
NodeKind::Unary(n) if n.op == "-" => {
let ty = resolver.type_facts.get_node_type(id).expect("Unary - should have type");
assert_eq!(format_type(ty, &resolver.type_arena, &interner_for_bootstrap, None), "int");
found_c = true;
}
_ => {}
}
}
assert!(found_a, "Binary + node not found");
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, _, mut 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 }
}
for p in ["int", "bool", "float", "string", "bounded", "void"] { interner.intern(p); }
let interner_for_bootstrap = interner.clone();
let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner);
resolver.bootstrap_types(&interner);
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, _, mut 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 }
}
for p in ["int", "bool", "float", "string", "bounded", "void"] { interner.intern(p); }
let interner_for_bootstrap = interner.clone();
let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner);
resolver.bootstrap_types(&interner);
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");
}
#[test]
fn test_lower_type_node_complex() {
let source = "
declare struct MyType()
fn main() {
let x: optional<int> = 1;
let y: result<int, string> = 2;
let z: array<float>[10] = 3;
let w: MyType = 4;
}
";
let (parsed, _, mut 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 }
}
for p in ["int", "bool", "float", "string", "bounded", "void"] { interner.intern(p); }
let interner_for_bootstrap = interner.clone();
let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner);
resolver.bootstrap_types(&interner);
resolver.resolve(&parsed.arena, parsed.root).expect("Resolution failed");
use crate::analysis::types::format_type;
let mut found_x = false;
let mut found_y = false;
let mut found_z = false;
let mut found_w = 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 {
let name = interner.resolve(n.name);
let sym_id = resolver.node_to_symbol.get(id).expect(&format!("Node {:?} should be bound to a symbol", kind));
let ty_id = resolver.type_facts.get_symbol_type(sym_id).expect("Symbol should have type");
match name {
"x" => {
assert_eq!(format_type(ty_id, &resolver.type_arena, &interner_for_bootstrap, None), "optional<int>");
found_x = true;
}
"y" => {
assert_eq!(format_type(ty_id, &resolver.type_arena, &interner_for_bootstrap, None), "result<int, string>");
found_y = true;
}
"z" => {
assert_eq!(format_type(ty_id, &resolver.type_arena, &interner_for_bootstrap, None), "array<float>[10]");
found_z = true;
}
"w" => {
// MyType is a struct. Since we don't pass symbols to format_type in this test, it should be struct#ID
let formatted = format_type(ty_id, &resolver.type_arena, &interner_for_bootstrap, None);
assert!(formatted.starts_with("struct#"), "Formatted type should be struct#ID, got {}", formatted);
found_w = true;
}
_ => {}
}
}
}
assert!(found_x);
assert!(found_y);
assert!(found_z);
assert!(found_w);
}
}