added location information for diagnostics

This commit is contained in:
pca006132 2021-06-28 15:05:05 +08:00
parent ed04cef431
commit e72d96f165

View File

@ -1,6 +1,8 @@
use super::super::location::{FileID, Location};
use super::super::symbol_resolver::*; use super::super::symbol_resolver::*;
use super::super::typedef::*; use super::super::typedef::*;
use super::GlobalContext; use super::GlobalContext;
use rustpython_parser::ast;
use std::boxed::Box; use std::boxed::Box;
use std::collections::HashMap; use std::collections::HashMap;
@ -17,34 +19,28 @@ pub struct InferenceContext<'a> {
global: GlobalContext<'a>, global: GlobalContext<'a>,
/// per source symbol resolver /// per source symbol resolver
resolver: Box<dyn SymbolResolver>, resolver: Box<dyn SymbolResolver>,
/// File ID
file: FileID,
/// list of primitive instances
primitives: Vec<Type>,
/// list of variable instances
variables: Vec<Type>,
/// identifier to (type, readable) mapping. /// identifier to (type, readable) mapping.
/// an identifier might be defined earlier but has no value (for some code path), thus not /// an identifier might be defined earlier but has no value (for some code path), thus not
/// readable. /// readable.
sym_table: HashMap<&'a str, (Type, bool)>, sym_table: HashMap<&'a str, (Type, bool, Location)>,
/// stack /// stack
stack: ContextStack<'a>, stack: ContextStack<'a>,
} }
// non-trivial implementations here // non-trivial implementations here
impl<'a> InferenceContext<'a> { impl<'a> InferenceContext<'a> {
/// return a new `InferenceContext` from `TopLevelContext` and resolution function. pub fn new(
pub fn new(global: GlobalContext, resolver: Box<dyn SymbolResolver>) -> InferenceContext { global: GlobalContext,
let primitives = (0..global.primitive_defs.len()) resolver: Box<dyn SymbolResolver>,
.map(|v| TypeEnum::PrimitiveType(PrimitiveId(v)).into()) file: FileID,
.collect(); ) -> InferenceContext {
let variables = (0..global.var_defs.len())
.map(|v| TypeEnum::TypeVariable(VariableId(v)).into())
.collect();
InferenceContext { InferenceContext {
global, global,
resolver, resolver,
primitives, file,
variables,
sym_table: HashMap::new(), sym_table: HashMap::new(),
stack: ContextStack { stack: ContextStack {
level: 0, level: 0,
@ -56,7 +52,7 @@ impl<'a> InferenceContext<'a> {
/// execute the function with new scope. /// execute the function with new scope.
/// variable assignment would be limited within the scope (not readable outside), and type /// variable assignment would be limited within the scope (not readable outside), and type
/// returns the list of variables assigned within the scope, and the result of the function /// returns the list of variables assigned within the scope, and the result of the function
pub fn with_scope<F, R>(&mut self, f: F) -> (Vec<&'a str>, R) pub fn with_scope<F, R>(&mut self, f: F) -> (Vec<(&'a str, Type, Location)>, R)
where where
F: FnOnce(&mut Self) -> R, F: FnOnce(&mut Self) -> R,
{ {
@ -68,8 +64,8 @@ impl<'a> InferenceContext<'a> {
let (_, level) = self.stack.sym_def.last().unwrap(); let (_, level) = self.stack.sym_def.last().unwrap();
if *level > self.stack.level { if *level > self.stack.level {
let (name, _) = self.stack.sym_def.pop().unwrap(); let (name, _) = self.stack.sym_def.pop().unwrap();
self.sym_table.remove(name).unwrap(); let (t, _, l) = self.sym_table.remove(name).unwrap();
poped_names.push(name); poped_names.push((name, t, l));
} else { } else {
break; break;
} }
@ -79,8 +75,8 @@ impl<'a> InferenceContext<'a> {
/// assign a type to an identifier. /// assign a type to an identifier.
/// may return error if the identifier was defined but with different type /// may return error if the identifier was defined but with different type
pub fn assign(&mut self, name: &'a str, ty: Type) -> Result<Type, String> { pub fn assign(&mut self, name: &'a str, ty: Type, loc: ast::Location) -> Result<Type, String> {
if let Some((t, x)) = self.sym_table.get_mut(name) { if let Some((t, x, _)) = self.sym_table.get_mut(name) {
if t == &ty { if t == &ty {
if !*x { if !*x {
self.stack.sym_def.push((name, self.stack.level)); self.stack.sym_def.push((name, self.stack.level));
@ -92,21 +88,19 @@ impl<'a> InferenceContext<'a> {
} }
} else { } else {
self.stack.sym_def.push((name, self.stack.level)); self.stack.sym_def.push((name, self.stack.level));
self.sym_table.insert(name, (ty.clone(), true)); self.sym_table.insert(
name,
(ty.clone(), true, Location::CodeRange(self.file, loc)),
);
Ok(ty) Ok(ty)
} }
} }
/// check if an identifier is already defined
pub fn defined(&self, name: &str) -> bool {
self.sym_table.get(name).is_some()
}
/// get the type of an identifier /// get the type of an identifier
/// may return error if the identifier is not defined, and cannot be resolved with the /// may return error if the identifier is not defined, and cannot be resolved with the
/// resolution function. /// resolution function.
pub fn resolve(&mut self, name: &str) -> Result<Type, String> { pub fn resolve(&self, name: &str) -> Result<Type, String> {
if let Some((t, x)) = self.sym_table.get(name) { if let Some((t, x, _)) = self.sym_table.get(name) {
if *x { if *x {
Ok(t.clone()) Ok(t.clone())
} else { } else {
@ -120,15 +114,24 @@ impl<'a> InferenceContext<'a> {
} }
} }
} }
pub fn get_location(&self, name: &str) -> Option<Location> {
if let Some((_, _, l)) = self.sym_table.get(name) {
Some(*l)
} else {
self.resolver.get_symbol_location(name)
}
}
} }
// trivial getters: // trivial getters:
impl<'a> InferenceContext<'a> { impl<'a> InferenceContext<'a> {
pub fn get_primitive(&self, id: PrimitiveId) -> Type { pub fn get_primitive(&self, id: PrimitiveId) -> Type {
self.primitives.get(id.0).unwrap().clone() TypeEnum::PrimitiveType(id).into()
} }
pub fn get_variable(&self, id: VariableId) -> Type { pub fn get_variable(&self, id: VariableId) -> Type {
self.variables.get(id.0).unwrap().clone() TypeEnum::TypeVariable(id).into()
} }
pub fn get_fn_def(&self, name: &str) -> Option<&FnDef> { pub fn get_fn_def(&self, name: &str) -> Option<&FnDef> {