diff --git a/crates/els/diagnostics.rs b/crates/els/diagnostics.rs index 7442ba9c1..a927a0add 100644 --- a/crates/els/diagnostics.rs +++ b/crates/els/diagnostics.rs @@ -289,10 +289,16 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> { } if let Some((mut lowerer, mut irs)) = self.steal_lowerer(&uri) { if let Some((hir_diff, hir)) = - HIRDiff::new(ast_diff, &mut lowerer).zip(irs.hir.as_mut()) + HIRDiff::new(ast_diff.clone(), &mut lowerer).zip(irs.hir.as_mut()) { crate::_log!(self, "hir_diff: {hir_diff}"); hir_diff.update(hir); + if let Some(ast) = irs.ast.as_mut() { + ast_diff.update(ast); + } + } + if let Some(hir) = irs.hir.as_mut() { + HIRDiff::fix(&new, &mut hir.module, &mut lowerer); } self.restore_lowerer(uri, lowerer, irs); } diff --git a/crates/els/diff.rs b/crates/els/diff.rs index 6096e315e..c2689d4e3 100644 --- a/crates/els/diff.rs +++ b/crates/els/diff.rs @@ -8,6 +8,7 @@ use erg_compiler::erg_parser::ast::Module; use erg_compiler::hir; use erg_compiler::hir::HIR; use erg_compiler::lower::ASTLowerer; +use erg_compiler::ty::HasType; #[derive(Debug, Clone, PartialEq, Eq)] pub enum ASTDiff { @@ -67,6 +68,29 @@ impl ASTDiff { pub const fn is_nop(&self) -> bool { matches!(self, Self::Nop) } + + pub fn update(self, mut old: impl DerefMut<Target = ast::Module>) { + match self { + Self::Addition(idx, expr) => { + if idx > old.len() { + old.push(expr); + } else { + old.insert(idx, expr); + } + } + Self::Deletion(usize) => { + if old.get(usize).is_some() { + old.remove(usize); + } + } + Self::Modification(idx, expr) => { + if let Some(old_expr) = old.get_mut(idx) { + *old_expr = expr; + } + } + Self::Nop => {} + } + } } #[derive(Debug, Clone, PartialEq, Eq)] @@ -93,12 +117,10 @@ impl HIRDiff { match diff { ASTDiff::Deletion(idx) => Some(Self::Deletion(idx)), ASTDiff::Addition(idx, expr) => { - let expr = lowerer - .lower_chunk(expr, None) - .map_err(|_err| { - // crate::_log!(self, "err: {err}"); - }) - .ok()?; + let expr = match lowerer.lower_and_resolve_chunk(expr, None) { + Ok(expr) => expr, + Err((opt_expr, _err)) => opt_expr?, + }; Some(Self::Addition(idx, expr)) } ASTDiff::Modification(idx, expr) => { @@ -110,12 +132,10 @@ impl HIRDiff { lowerer.unregister(name); } } - let expr = lowerer - .lower_chunk(expr, None) - .map_err(|_err| { - // crate::_log!(self, "err: {err}"); - }) - .ok()?; + let expr = match lowerer.lower_and_resolve_chunk(expr, None) { + Ok(expr) => expr, + Err((opt_expr, _err)) => opt_expr?, + }; Some(Self::Modification(idx, expr)) } ASTDiff::Nop => Some(Self::Nop), @@ -144,4 +164,23 @@ impl HIRDiff { Self::Nop => {} } } + + pub fn fix(ast: &ast::Module, hir: &mut hir::Module, lowerer: &mut ASTLowerer) -> usize { + let mut fixed = 0; + for (ast_chunk, chunk) in ast.iter().zip(hir.iter_mut()) { + if ast_chunk.name() != chunk.name() { + continue; + } + if chunk.ref_t().contains_failure() { + match lowerer.lower_and_resolve_chunk(ast_chunk.clone(), None) { + Ok(expr) | Err((Some(expr), _)) => { + *chunk = expr; + fixed += 1; + } + _ => {} + } + } + } + fixed + } } diff --git a/crates/erg_compiler/context/generalize.rs b/crates/erg_compiler/context/generalize.rs index 3ca454aed..c8a7acf66 100644 --- a/crates/erg_compiler/context/generalize.rs +++ b/crates/erg_compiler/context/generalize.rs @@ -1529,7 +1529,11 @@ impl Context { /// Resolution should start at a deeper level. /// For example, if it is a lambda function, the body should be checked before the signature. /// However, a binop call error, etc., is more important then binop operands. - fn resolve_expr_t(&self, expr: &mut hir::Expr, qnames: &Set<Str>) -> TyCheckResult<()> { + pub(crate) fn resolve_expr_t( + &self, + expr: &mut hir::Expr, + qnames: &Set<Str>, + ) -> TyCheckResult<()> { match expr { hir::Expr::Literal(_) => Ok(()), hir::Expr::Accessor(acc) => { diff --git a/crates/erg_compiler/hir.rs b/crates/erg_compiler/hir.rs index c646954b5..36a445bae 100644 --- a/crates/erg_compiler/hir.rs +++ b/crates/erg_compiler/hir.rs @@ -3008,7 +3008,7 @@ impl Expr { match self { Self::Literal(_) => "literal", Self::Accessor(_) => "accessor", - Self::List(_) => "array", + Self::List(_) => "list", Self::Tuple(_) => "tuple", Self::Dict(_) => "dict", Self::Set(_) => "set", diff --git a/crates/erg_compiler/lower.rs b/crates/erg_compiler/lower.rs index 65de5141e..021cdbc1e 100644 --- a/crates/erg_compiler/lower.rs +++ b/crates/erg_compiler/lower.rs @@ -3606,7 +3606,7 @@ impl<A: ASTBuildable> GenericASTLowerer<A> { /// The meaning of TypeAscription changes between chunk and expr. /// For example, `x: Int`, as expr, is `x` itself, /// but as chunk, it declares that `x` is of type `Int`, and is valid even before `x` is defined. - pub fn lower_chunk( + pub(crate) fn lower_chunk( &mut self, chunk: ast::Expr, expect: Option<&Type>, @@ -3633,6 +3633,24 @@ impl<A: ASTBuildable> GenericASTLowerer<A> { } } + pub fn lower_and_resolve_chunk( + &mut self, + chunk: ast::Expr, + expect: Option<&Type>, + ) -> FailableOption<hir::Expr> { + match self.lower_chunk(chunk, expect) { + Ok(mut chunk) => { + let _ = self.module.context.resolve_expr_t(&mut chunk, &set! {}); + Ok(chunk) + } + Err((Some(mut chunk), errs)) => { + let _ = self.module.context.resolve_expr_t(&mut chunk, &set! {}); + Err((Some(chunk), errs)) + } + Err((None, errs)) => Err((None, errs)), + } + } + fn lower_block( &mut self, ast_block: ast::Block, diff --git a/crates/erg_parser/ast.rs b/crates/erg_parser/ast.rs index 88aa861f0..b8c5192b7 100644 --- a/crates/erg_parser/ast.rs +++ b/crates/erg_parser/ast.rs @@ -2,6 +2,7 @@ use std::borrow::Borrow; use std::fmt; use std::fmt::Write as _; +use std::hash::{Hash, Hasher}; use erg_common::consts::ERG_MODE; use erg_common::error::Location; @@ -2559,13 +2560,28 @@ impl ConstBlock { } #[pyclass(get_all, set_all)] -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug)] pub struct ConstDefBody { pub op: Token, pub block: ConstBlock, pub id: DefId, } +impl PartialEq for ConstDefBody { + fn eq(&self, other: &Self) -> bool { + self.op == other.op && self.block == other.block + } +} + +impl Eq for ConstDefBody {} + +impl Hash for ConstDefBody { + fn hash<H: Hasher>(&self, state: &mut H) { + self.op.hash(state); + self.block.hash(state); + } +} + impl_locational!(ConstDefBody, lossy op, block); #[pymethods] @@ -2613,7 +2629,7 @@ impl ConstDef { } #[pyclass] -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug)] pub struct ConstLambda { pub sig: Box<LambdaSignature>, pub op: Token, @@ -2621,6 +2637,22 @@ pub struct ConstLambda { pub id: DefId, } +impl PartialEq for ConstLambda { + fn eq(&self, other: &Self) -> bool { + self.sig == other.sig && self.op == other.op && self.body == other.body + } +} + +impl Eq for ConstLambda {} + +impl Hash for ConstLambda { + fn hash<H: Hasher>(&self, state: &mut H) { + self.sig.hash(state); + self.op.hash(state); + self.body.hash(state); + } +} + impl NestedDisplay for ConstLambda { fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { write!(f, "({}) {} {}", self.sig, self.op.content, self.body) @@ -5495,6 +5527,8 @@ impl LambdaSignature { } } +/// Definition ID. +/// IDs are comparable, but `Def`s with different IDs are equal if the node contents are the same. #[pyclass(subclass)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct DefId(pub usize); @@ -5506,7 +5540,7 @@ impl DefId { } #[pyclass(get_all, set_all)] -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug)] pub struct Lambda { pub sig: LambdaSignature, /// for detecting func/proc @@ -5515,6 +5549,22 @@ pub struct Lambda { pub id: DefId, } +impl PartialEq for Lambda { + fn eq(&self, other: &Self) -> bool { + self.sig == other.sig && self.op == other.op && self.body == other.body + } +} + +impl Eq for Lambda {} + +impl Hash for Lambda { + fn hash<H: Hasher>(&self, state: &mut H) { + self.sig.hash(state); + self.op.hash(state); + self.body.hash(state); + } +} + impl NestedDisplay for Lambda { fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result { writeln!(f, "{} {}", self.sig, self.op.content)?; @@ -5762,7 +5812,7 @@ impl DefKind { } #[pyclass(get_all, set_all)] -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug)] pub struct DefBody { pub op: Token, pub block: Block, @@ -5771,6 +5821,21 @@ pub struct DefBody { impl_locational!(DefBody, lossy op, block); +impl PartialEq for DefBody { + fn eq(&self, other: &Self) -> bool { + self.op == other.op && self.block == other.block + } +} + +impl Eq for DefBody {} + +impl Hash for DefBody { + fn hash<H: Hasher>(&self, state: &mut H) { + self.op.hash(state); + self.block.hash(state); + } +} + #[pymethods] impl DefBody { #[staticmethod] @@ -5896,7 +5961,7 @@ impl ReDef { /// f(a) = ... /// ``` #[pyclass] -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone)] pub struct Methods { pub id: DefId, pub class: TypeSpec, @@ -5905,6 +5970,26 @@ pub struct Methods { pub attrs: ClassAttrs, } +impl PartialEq for Methods { + fn eq(&self, other: &Self) -> bool { + self.class == other.class + && self.class_as_expr == other.class_as_expr + && self.vis == other.vis + && self.attrs == other.attrs + } +} + +impl Eq for Methods {} + +impl Hash for Methods { + fn hash<H: Hasher>(&self, state: &mut H) { + self.class.hash(state); + self.class_as_expr.hash(state); + self.vis.hash(state); + self.attrs.hash(state); + } +} + impl NestedDisplay for Methods { fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result { writeln!(f, "{}{}", self.class, self.vis)?;