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::analysis::types::{TypeArena, TypeFacts, TypeId, TypeKind};
|
||||||
use crate::common::diagnostics::{Diagnostic, DiagnosticBundle, DiagnosticLevel};
|
use crate::common::diagnostics::{Diagnostic, DiagnosticBundle, DiagnosticLevel};
|
||||||
use crate::common::spans::Span;
|
use crate::common::spans::Span;
|
||||||
@ -14,11 +15,13 @@ pub struct Resolver<'a> {
|
|||||||
interner: &'a NameInterner,
|
interner: &'a NameInterner,
|
||||||
module_provider: &'a dyn ModuleProvider,
|
module_provider: &'a dyn ModuleProvider,
|
||||||
current_module: &'a ModuleSymbols,
|
current_module: &'a ModuleSymbols,
|
||||||
scopes: Vec<HashMap<NameId, Symbol>>,
|
scopes: Vec<HashMap<NameId, (Symbol, Option<SymbolId>)>>,
|
||||||
pub imported_symbols: ModuleSymbols,
|
pub imported_symbols: ModuleSymbols,
|
||||||
diagnostics: Vec<Diagnostic>,
|
diagnostics: Vec<Diagnostic>,
|
||||||
pub type_arena: TypeArena,
|
pub type_arena: TypeArena,
|
||||||
pub type_facts: TypeFacts,
|
pub type_facts: TypeFacts,
|
||||||
|
pub symbol_arena: crate::analysis::symbols::SymbolArena,
|
||||||
|
pub node_to_symbol: crate::analysis::symbols::NodeToSymbol,
|
||||||
primitives: HashMap<String, TypeId>,
|
primitives: HashMap<String, TypeId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,6 +40,8 @@ impl<'a> Resolver<'a> {
|
|||||||
diagnostics: Vec::new(),
|
diagnostics: Vec::new(),
|
||||||
type_arena: TypeArena::new(),
|
type_arena: TypeArena::new(),
|
||||||
type_facts: TypeFacts::new(),
|
type_facts: TypeFacts::new(),
|
||||||
|
symbol_arena: SymbolArena::new(),
|
||||||
|
node_to_symbol: NodeToSymbol::new(),
|
||||||
primitives: HashMap::new(),
|
primitives: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,7 +157,13 @@ impl<'a> Resolver<'a> {
|
|||||||
NodeKind::Call(n) => {
|
NodeKind::Call(n) => {
|
||||||
if let NodeKind::Ident(id) = arena.kind(n.callee) {
|
if let NodeKind::Ident(id) = arena.kind(n.callee) {
|
||||||
if !self.is_builtin(id.name, Namespace::Value) {
|
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() {
|
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)
|
// TODO: Resolver for v0 allows unresolved call targets (e.g. constructors via prelude)
|
||||||
}
|
}
|
||||||
@ -304,10 +315,14 @@ impl<'a> Resolver<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodeKind::Ident(n) => {
|
NodeKind::Ident(n) => {
|
||||||
let _sym = self.resolve_identifier(n.name, arena.span(node), Namespace::Value);
|
if let Some((_sym, Some(sym_id))) = self.lookup_with_id(n.name, Namespace::Value) {
|
||||||
// For v0 bootstrap, if we found a local variable, we don't know its type yet (no inference)
|
self.node_to_symbol.bind_node(node, sym_id);
|
||||||
// but if we had a way to track symbol types, we'd use it here.
|
if let Some(ty) = self.type_facts.get_symbol_type(sym_id) {
|
||||||
// For now, we only type literals and simple operators.
|
self.type_facts.set_node_type(node, ty);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.resolve_identifier(n.name, arena.span(node), Namespace::Value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
NodeKind::IntLit(_) => {
|
NodeKind::IntLit(_) => {
|
||||||
if let Some(id) = self.primitives.get("int") {
|
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) {
|
fn resolve_fn_decl(&mut self, arena: &AstArena, _id: NodeId, n: &FnDeclNodeArena) {
|
||||||
self.enter_scope();
|
self.enter_scope();
|
||||||
for param in &n.params {
|
for param in &n.params {
|
||||||
self.resolve_type_ref(arena, param.ty);
|
let ty_id = self.resolve_type_ref(arena, param.ty);
|
||||||
self.define_local(param.name, param.span, SymbolKind::Local);
|
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 {
|
if let Some(ret) = n.ret {
|
||||||
self.resolve_type_ref(arena, 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) {
|
fn resolve_let_stmt(&mut self, arena: &AstArena, id: NodeId, n: &LetStmtNodeArena) {
|
||||||
if let Some(ty) = n.ty {
|
let ty_id = if let Some(ty) = n.ty {
|
||||||
self.resolve_type_ref(arena, ty);
|
self.resolve_type_ref(arena, ty)
|
||||||
}
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
self.resolve_node(arena, n.init);
|
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);
|
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> {
|
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> {
|
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
|
// 1. local bindings
|
||||||
if namespace == Namespace::Value {
|
if namespace == Namespace::Value {
|
||||||
for scope in self.scopes.iter().rev() {
|
for scope in self.scopes.iter().rev() {
|
||||||
if let Some(sym) = scope.get(&name) {
|
if let Some(pair) = scope.get(&name) {
|
||||||
return Some(sym.clone());
|
return Some(pair.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -506,7 +545,7 @@ impl<'a> Resolver<'a> {
|
|||||||
|
|
||||||
// 2 & 3. file-private and module symbols
|
// 2 & 3. file-private and module symbols
|
||||||
if let Some(sym) = table.get(name) {
|
if let Some(sym) = table.get(name) {
|
||||||
return Some(sym.clone());
|
return Some((sym.clone(), None));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. imported symbols
|
// 4. imported symbols
|
||||||
@ -516,19 +555,19 @@ impl<'a> Resolver<'a> {
|
|||||||
&self.imported_symbols.value_symbols
|
&self.imported_symbols.value_symbols
|
||||||
};
|
};
|
||||||
if let Some(sym) = imp_table.get(name) {
|
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
|
// 5. Fallback for constructor calls: check Type namespace if looking for a Value
|
||||||
if namespace == Namespace::Value {
|
if namespace == Namespace::Value {
|
||||||
if let Some(sym) = self.current_module.type_symbols.get(name) {
|
if let Some(sym) = self.current_module.type_symbols.get(name) {
|
||||||
if sym.kind == SymbolKind::Struct {
|
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 let Some(sym) = self.imported_symbols.type_symbols.get(name) {
|
||||||
if sym.kind == SymbolKind::Struct {
|
if sym.kind == SymbolKind::Struct {
|
||||||
return Some(sym.clone());
|
return Some((sym.clone(), None));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -536,7 +575,7 @@ impl<'a> Resolver<'a> {
|
|||||||
None
|
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");
|
let scope = self.scopes.last_mut().expect("No scope to define local");
|
||||||
|
|
||||||
// Check for collision in Type namespace at top-level?
|
// Check for collision in Type namespace at top-level?
|
||||||
@ -552,7 +591,7 @@ impl<'a> Resolver<'a> {
|
|||||||
),
|
),
|
||||||
span: Some(span),
|
span: Some(span),
|
||||||
});
|
});
|
||||||
return;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if scope.contains_key(&name) {
|
if scope.contains_key(&name) {
|
||||||
@ -562,8 +601,9 @@ impl<'a> Resolver<'a> {
|
|||||||
message: format!("Duplicate local variable '{}'", self.interner.resolve(name)),
|
message: format!("Duplicate local variable '{}'", self.interner.resolve(name)),
|
||||||
span: Some(span),
|
span: Some(span),
|
||||||
});
|
});
|
||||||
|
None
|
||||||
} else {
|
} else {
|
||||||
scope.insert(name, Symbol {
|
let symbol = Symbol {
|
||||||
name,
|
name,
|
||||||
kind,
|
kind,
|
||||||
namespace: Namespace::Value,
|
namespace: Namespace::Value,
|
||||||
@ -572,7 +612,22 @@ impl<'a> Resolver<'a> {
|
|||||||
is_host: false,
|
is_host: false,
|
||||||
span,
|
span,
|
||||||
origin: None,
|
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) {
|
fn setup_test(source: &str) -> (ParsedAst, usize, NameInterner) {
|
||||||
let mut fm = FileManager::new();
|
let mut fm = FileManager::new();
|
||||||
let file_id = fm.add(PathBuf::from("test.pbs"), source.to_string());
|
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 mut parser = parser::Parser::new(source, file_id, fm.interner_mut());
|
||||||
let parsed = parser.parse_file().expect("Parsing failed");
|
let parsed = parser.parse_file().expect("Parsing failed");
|
||||||
(parsed, file_id, fm.interner().clone())
|
(parsed, file_id, fm.interner().clone())
|
||||||
@ -842,7 +896,7 @@ mod tests {
|
|||||||
let c = -1;
|
let c = -1;
|
||||||
}
|
}
|
||||||
";
|
";
|
||||||
let (parsed, _, mut interner) = setup_test(source);
|
let (parsed, _, interner) = setup_test(source);
|
||||||
let mut collector = SymbolCollector::new(&interner);
|
let mut collector = SymbolCollector::new(&interner);
|
||||||
let (ts, vs) = collector.collect(&parsed.arena, parsed.root).unwrap();
|
let (ts, vs) = collector.collect(&parsed.arena, parsed.root).unwrap();
|
||||||
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
|
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
|
||||||
@ -892,4 +946,99 @@ mod tests {
|
|||||||
assert!(found_b, "Binary == node not found");
|
assert!(found_b, "Binary == node not found");
|
||||||
assert!(found_c, "Unary - 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