#![allow(dead_code)]
use super::super::constant::Value;
use super::super::importer::{ImportKey, Importer, SourceRead, TraceConfig};
use super::super::objects::{DeclInfoKey, ObjKey, PackageKey, ScopeKey, TCObjects, TypeKey};
use super::super::operand::OperandMode;
use super::super::selection::Selection;
use super::interface::IfaceInfo;
use go_parser::ast;
use go_parser::ast::{Expr, Node, NodeId};
use go_parser::{AstObjects, ErrorList, FilePosErrors, FileSet, IdentKey, Map, Pos};
use std::cell::RefCell;
use std::collections::HashSet;
use std::rc::Rc;
#[derive(Debug, Clone)]
pub struct TypeAndValue {
pub mode: OperandMode,
pub typ: TypeKey,
}
impl TypeAndValue {
pub fn get_const_val(&self) -> Option<&Value> {
match &self.mode {
OperandMode::Constant(v) => Some(v),
_ => None,
}
}
}
#[derive(Debug)]
pub struct Initializer {
pub lhs: Vec<ObjKey>,
pub rhs: Expr,
}
#[derive(Debug)]
pub struct TypeInfo {
pub types: Map<NodeId, TypeAndValue>,
pub defs: Map<IdentKey, Option<ObjKey>>,
pub uses: Map<IdentKey, ObjKey>,
pub implicits: Map<NodeId, ObjKey>,
pub selections: Map<NodeId, Selection>,
pub scopes: Map<NodeId, ScopeKey>,
pub init_order: Vec<Initializer>,
pub ast_files: Vec<ast::File>,
}
impl TypeInfo {
pub fn new() -> TypeInfo {
TypeInfo {
types: Map::new(),
defs: Map::new(),
uses: Map::new(),
implicits: Map::new(),
selections: Map::new(),
scopes: Map::new(),
init_order: Vec::new(),
ast_files: Vec::new(),
}
}
}
#[derive(Debug)]
pub struct ExprInfo {
pub is_lhs: bool,
pub mode: OperandMode,
pub typ: Option<TypeKey>,
}
#[derive(Clone)]
pub struct ObjContext {
pub decl: Option<DeclInfoKey>,
pub scope: Option<ScopeKey>,
pub pos: Option<Pos>,
pub iota: Option<Value>,
pub sig: Option<TypeKey>,
pub panics: Option<HashSet<NodeId>>,
pub has_label: bool,
pub has_call_or_recv: bool,
}
type DelayedAction<S> = Box<dyn FnOnce(&mut Checker<S>, &mut FilesContext<S>)>;
pub type RcIfaceInfo = Rc<IfaceInfo>;
pub struct FilesContext<'a, S: SourceRead> {
pub files: &'a Vec<ast::File>,
pub unused_dot_imports: Map<ScopeKey, Map<PackageKey, Pos>>,
pub methods: Map<ObjKey, Vec<ObjKey>>,
pub ifaces: Map<ObjKey, Option<RcIfaceInfo>>,
pub untyped: Map<NodeId, ExprInfo>,
pub delayed: Vec<DelayedAction<S>>,
pub obj_path: Vec<ObjKey>,
}
pub struct Checker<'a, S: SourceRead> {
pub tc_objs: &'a mut TCObjects,
pub ast_objs: &'a mut AstObjects,
errors: &'a ErrorList,
pub fset: &'a mut FileSet,
pub all_pkgs: &'a mut Map<String, PackageKey>,
all_results: &'a mut Map<PackageKey, TypeInfo>,
pub pkg: PackageKey,
pub obj_map: Map<ObjKey, DeclInfoKey>,
pub imp_map: Map<ImportKey, PackageKey>,
pub octx: ObjContext,
trace_config: &'a TraceConfig,
reader: &'a S,
pub result: TypeInfo,
pub indent: Rc<RefCell<usize>>,
}
impl ObjContext {
pub fn new() -> ObjContext {
ObjContext {
decl: None,
scope: None,
pos: None,
iota: None,
sig: None,
panics: None,
has_label: false,
has_call_or_recv: false,
}
}
}
impl<S: SourceRead> FilesContext<'_, S> {
pub fn new(files: &Vec<ast::File>) -> FilesContext<'_, S> {
FilesContext {
files: files,
unused_dot_imports: Map::new(),
methods: Map::new(),
ifaces: Map::new(),
untyped: Map::new(),
delayed: Vec::new(),
obj_path: Vec::new(),
}
}
pub fn file_name(&self, index: usize, checker: &Checker<S>) -> String {
let file = &self.files[index];
let pos = file.pos(checker.ast_objs);
if pos > 0 {
checker.fset.file(pos).unwrap().name().to_owned()
} else {
format!("file[{}]", index)
}
}
pub fn add_unused_dot_import(&mut self, scope: &ScopeKey, pkg: &PackageKey, pos: Pos) {
if !self.unused_dot_imports.contains_key(scope) {
self.unused_dot_imports.insert(*scope, Map::new());
}
self.unused_dot_imports
.get_mut(scope)
.unwrap()
.insert(*pkg, pos);
}
pub fn remember_untyped(&mut self, e: &Expr, ex_info: ExprInfo) {
self.untyped.insert(e.id(), ex_info);
}
pub fn later(&mut self, action: DelayedAction<S>) {
self.delayed.push(action);
}
pub fn delayed_count(&self) -> usize {
self.delayed.len()
}
pub fn process_delayed(&mut self, top: usize, checker: &mut Checker<S>) {
let fs: Vec<DelayedAction<S>> = self.delayed.drain(top..).into_iter().collect();
for f in fs {
f(checker, self);
}
}
pub fn push(&mut self, obj: ObjKey) -> usize {
self.obj_path.push(obj);
self.obj_path.len() - 1
}
pub fn pop(&mut self) -> ObjKey {
self.obj_path.pop().unwrap()
}
}
impl TypeAndValue {
fn new(mode: OperandMode, typ: TypeKey) -> TypeAndValue {
TypeAndValue {
mode: mode,
typ: typ,
}
}
}
impl TypeInfo {
pub fn record_type_and_value(&mut self, e: &Expr, mode: OperandMode, typ: TypeKey) {
self.record_type_and_value_with_id(e.id(), mode, typ);
}
pub fn record_type_and_value_with_id(&mut self, id: NodeId, mode: OperandMode, typ: TypeKey) {
if let OperandMode::Invalid = mode {
return;
}
self.types.insert(id, TypeAndValue::new(mode, typ));
}
pub fn record_builtin_type(&mut self, mode: &OperandMode, e: &Expr, sig: TypeKey) {
let mut expr = e;
loop {
self.record_type_and_value(expr, mode.clone(), sig);
match expr {
Expr::Ident(_) => break,
Expr::Paren(p) => expr = &(*p).expr,
_ => unreachable!(),
}
}
}
pub fn record_comma_ok_types<S: SourceRead>(
&mut self,
e: &Expr,
t: &[TypeKey; 2],
tc_objs: &mut TCObjects,
ast_objs: &AstObjects,
pkg: PackageKey,
) {
let pos = e.pos(ast_objs);
let mut expr = e;
loop {
let tv = self.types.get_mut(&expr.id()).unwrap();
tv.typ = Checker::<S>::comma_ok_type(tc_objs, pos, pkg, t);
match expr {
Expr::Paren(p) => expr = &(*p).expr,
_ => break,
}
}
}
pub fn record_def(&mut self, id: IdentKey, obj: Option<ObjKey>) {
self.defs.insert(id, obj);
}
pub fn record_use(&mut self, id: IdentKey, obj: ObjKey) {
self.uses.insert(id, obj);
}
pub fn record_implicit(&mut self, node: &impl Node, obj: ObjKey) {
self.implicits.insert(node.id(), obj);
}
pub fn record_selection(&mut self, expr: &ast::SelectorExpr, sel: Selection) {
self.record_use(expr.sel, sel.obj());
self.selections.insert(expr.id(), sel);
}
pub fn record_scope(&mut self, node: &impl Node, scope: ScopeKey) {
self.scopes.insert(node.id(), scope);
}
pub fn record_init_order(&mut self, init_order: Vec<Initializer>) {
self.init_order = init_order;
}
}
impl<'a, S: SourceRead> Checker<'a, S> {
pub fn new(
tc_objs: &'a mut TCObjects,
ast_objs: &'a mut AstObjects,
fset: &'a mut FileSet,
errors: &'a ErrorList,
pkgs: &'a mut Map<String, PackageKey>,
all_results: &'a mut Map<PackageKey, TypeInfo>,
pkg: PackageKey,
cfg: &'a TraceConfig,
reader: &'a S,
) -> Checker<'a, S> {
Checker {
tc_objs: tc_objs,
ast_objs: ast_objs,
fset: fset,
errors: errors,
all_pkgs: pkgs,
all_results: all_results,
pkg: pkg,
obj_map: Map::new(),
imp_map: Map::new(),
octx: ObjContext::new(),
trace_config: cfg,
reader: reader,
result: TypeInfo::new(),
indent: Rc::new(RefCell::new(0)),
}
}
pub fn check(mut self, mut files: Vec<ast::File>) -> Result<PackageKey, ()> {
self.check_files_pkg_name(&files)?;
let fctx = &mut FilesContext::new(&files);
self.collect_objects(fctx);
self.package_objects(fctx);
fctx.process_delayed(0, &mut self);
self.init_order();
self.unused_imports(fctx);
self.record_untyped(fctx);
std::mem::swap(&mut self.result.ast_files, &mut files);
self.all_results.insert(self.pkg, self.result);
Ok(self.pkg)
}
fn record_untyped(&mut self, fctx: &mut FilesContext<S>) {
for (id, info) in fctx.untyped.iter() {
if info.mode != OperandMode::Invalid {
self.result.record_type_and_value_with_id(
id.clone(),
info.mode.clone(),
info.typ.unwrap(),
);
}
}
}
#[inline]
pub fn errors(&self) -> &ErrorList {
self.errors
}
#[inline]
pub fn trace(&self) -> bool {
self.trace_config.trace_checker
}
pub fn new_importer(&mut self, pos: Pos) -> Importer<S> {
Importer::new(
self.trace_config,
self.reader,
self.fset,
self.all_pkgs,
self.all_results,
self.ast_objs,
self.tc_objs,
self.errors,
pos,
)
}
fn check_files_pkg_name(&mut self, files: &Vec<ast::File>) -> Result<(), ()> {
let mut pkg_name: Option<String> = None;
for f in files.iter() {
let ident = &self.ast_objs.idents[f.name];
if pkg_name.is_none() {
if ident.name == "_" {
self.error(ident.pos, "invalid package name _".to_owned());
return Err(());
} else {
pkg_name = Some(ident.name.clone());
}
} else if &ident.name != pkg_name.as_ref().unwrap() {
self.error(
f.package,
format!(
"package {}; expected {}",
ident.name,
pkg_name.as_ref().unwrap()
),
);
return Err(());
}
}
self.tc_objs.pkgs[self.pkg].set_name(pkg_name.unwrap());
Ok(())
}
pub fn error(&self, pos: Pos, err: String) {
self.error_impl(pos, err, false);
}
pub fn error_str(&self, pos: Pos, err: &str) {
self.error_impl(pos, err.to_string(), false);
}
pub fn soft_error(&self, pos: Pos, err: String) {
self.error_impl(pos, err, true);
}
pub fn soft_error_str(&self, pos: Pos, err: &str) {
self.error_impl(pos, err.to_string(), true);
}
fn error_impl(&self, pos: Pos, err: String, soft: bool) {
let file = self.fset.file(pos).unwrap();
FilePosErrors::new(file, self.errors).add(pos, err, soft);
}
}